##############
## contents ##
##############

## types:
## - Investments :: quite similar to Timematr, with fields ~vals~ and ~idx~

#################################
## definition Investments type ##
#################################

type Investments{T}
    vals::DataFrame
    idx::Array{T, 1}

    function Investments(vals::DataFrame, idx::Array{T, 1})
        ## consists of time index and numeric data (no NAs allowed)
        ## with equal row numbers and rowsums of data equal to one

        ## check constraints
        TimeData.chkIdx(idx)
        TimeData.chkNum(vals)           # numeric values only?
        chkEqualsOne(vals)

        ## check equal number of rows
        if(size(vals, 1) != length(idx))
            if (length(idx) == 0) | (size(vals, 1) == 0)
                return new(DataFrame([]), Array{T, 1}[])
            end
            error(length(idx), " idx entries, but ", size(vals, 1), " rows of data")
        end
        return new(vals, idx)
    end
end

## required for parametric type declaration
function Investments{T}(vals::DataFrame, idx::Array{T, 1})
    return Investments{T}(vals, idx)
end

## initialization without dates
function Investments(vals::DataFrame)
    nPortfolios = size(vals, 1)
    return Investments(vals, [1:nPortfolios])
end

####################
## display method ##
####################

import Base.Multimedia.display
function display(invs::Investments)
    ## display information about an array

    ## set display parameters
    maxDispCols = 5;

    ## get type and field information
    typ = typeof(invs)
    println("\ntype: $typ")
    print("dimensions: ")
    print(size(invs))
    print("\n")

    ## get first entries
    (nrow, ncol) = size(invs)

    showCols = minimum([maxDispCols ncol]);

    Peekidx = DataFrame(idx = invs.idx);
    Peek = [Peekidx invs.vals[:, 1:showCols]];
    display(Peek)
end

function str(invs::Investments)
    display(invs)
end

######################
## Investments size ##
######################

import Base.size
function size(invs::Investments)
    return size(invs.vals)
end

function size(invs::Investments, ind::Int)
    return size(invs.vals, ind)
end

import Base.ndims
function ndims(invs::Investments)
    return ndims(invs.vals)
end

#####################
## get investments ##
#####################

function weights(invs::Investments)
    return array(invs.vals)
end

import TimeData.core
function core(invs::Investments)
    return convert(Array, invs.vals)
end

###############
## get index ##
###############

import TimeData.idx
function idx(invs::Investments)
    return invs.idx
end

import Base.names
function names(invs::Investments)
    return names(invs.vals)
end

#############
## convert ##
#############

import Base.convert
function convert(::Type{Timematr}, invs::Investments)
    return Timematr(invs.vals, idx(invs))
end

function convert(::Type{DataFrame}, invs::Investments)
    return [DataFrame(idx = invs.idx) invs.vals]
end

## asArr
##------

import TimeData.asArr
function asArr(invs::Investments)
    return convert(Array, invs.vals)
end

########
## IO ##
########

function readInvestments(filename::String)
    td = readTimedata(filename)
    return Investments(td.vals, idx(td))
end

function writeInvestments(filename::String, invs::Investments)
    ## create large dataframe
    idxDf = DataFrame(idx = idx(invs));
    df = [idxDf invs.vals];
    writetable(filename, df)
end

#############
## isequal ##
#############

import Base.isequal
function isequal(invs::Investments, invs2::Investments)
    typeEqu = isequal(typeof(invs), typeof(invs2))
    valsEqu = isequal(invs.vals, invs2.vals)
    idxEqu = isequal(invs.idx, invs2.idx)
    equ = (valsEqu & idxEqu & typeEqu)
    return equ
end

##########
## copy ##
##########

function Base.copy(invs::Investments)
    copiedVals = copy(invs.vals)
    copiedInvs = AssetMgmt.Investments(copiedVals, idx(invs))
    return copiedInvs
end

###############################################
## check matching investment and return data ##
###############################################

function chkMatchInvData(invs::Investments, discRet::Timematr)
    ## test whether investments and return data are matching
    ##
    ## Matching: all investement dates are entailed in return data
    ## dates.
    ##
    ## Output: error when inputs don't match

    ## check for conforming dates and assets
    if !issubset(idx(invs), idx(discRet))
        error("indices / dates of investments must be contained in
    dates of return data")
    end

    if AssetMgmt.names(invs) != names(discRet)
        error("asset names of investments and returns must coincide")
    end
end

#######################################
## find trading days for investments ##
#######################################

function isTradingDay(invs::Investments, discRet::Timematr)
    ## most changes in investment weights are caused by simple price
    ## changes without actual trading. This function finds all days
    ## with active rebalancing of weights. If evening weights
    ## (achieved through price changes) deviate from next day morning
    ## weights, next day will be called trading day.
    ##
    ## return dates may deviate from investment dates as long as all
    ## investment dates are also contained in return dates.
    ##
    ## trading day indicators are returned for all investment dates.

    ## get automatic daily changes of weights
    wgtsPriceChanges = evolWgts(invs, discRet)

    ## get values without metadata
    invsArr = AssetMgmt.core(invs)
    wgtsPriceChangesArr = AssetMgmt.core(wgtsPriceChanges)

    ## preallocate trading indicator
    nObs = size(invs, 1)
    isTrading = falses(nObs)

    ## first day is defined as trading day
    isTrading[1] = true

    for ii=2:nObs
        ## get weight changes from evening weights last day to morning
        ## weights of current day
        wgtChange =
            sum(abs(invsArr[ii, :] - wgtsPriceChangesArr[ii-1, :]))
        if wgtChange > 1e-12
            isTrading[ii] = true
        end
    end

    return isTrading
end

######################
## getindex methods ##
######################

## invs[SingleColumnIndex] => Timematr
## invs[MultiColumnIndex] => Timematr (if columns sum up to one:
## Investments?)
## invs[SingleRowIndex, SingleColumnIndex] => Scalar
## invs[SingleRowIndex, MultiColumnIndex] => Timematr
## invs[MultiRowIndex, SingleColumnIndex] => Timematr
## invs[MultiRowIndex, MultiColumnIndex] => Timematr (if columns sum
## up to one: Investments)

## TimeData types do ALWAYS preserve types when indexing.

typealias ColumnIndex Union(Real, Symbol)

# invs[SingleColumnIndex] => Timematr
function Base.getindex(invs::Investments, col_ind::ColumnIndex)
    tm = convert(Timematr, invs)
    return tm[col_ind]
end

# invs[MultiColumnIndex] => Timematr (Investments for simple
# re-ordering)
function Base.getindex{T <: ColumnIndex}(invs::Investments,
                                         col_inds::AbstractVector{T})
    output = convert(Timematr, invs)[col_inds]

    ## are all columns selected?
    nAss = size(invs, 2)
    if length(col_inds) == nAss
        if sort(col_inds) == sort(names(invs))
            output = AssetMgmt.Investments(output.vals, idx(invs))
        end
    end
    return output
end

# invs[SingleRowIndex, SingleColumnIndex] => Timematr
function Base.getindex(invs::Investments, row_ind::Real,
                       col_ind::ColumnIndex)
    tm = convert(Timematr, invs)
    return tm[row_ind, col_ind]
end

# invs[SingleRowIndex, MultiColumnIndex] => Timematr
function Base.getindex{T <: ColumnIndex}(invs::Investments,
                                         row_ind::Real,
                                         col_inds::AbstractVector{T})
    output = convert(Timematr, invs)[row_ind, col_inds]

    ## are all columns selected?
    nAss = size(invs, 2)
    if length(col_inds) == nAss
        if sort(col_inds) == sort(names(invs))
            output = AssetMgmt.Investments(output.vals, idx(invs))
        end
    end
    return output
end

# invs[MultiRowIndex, SingleColumnIndex] => Timematr
function Base.getindex{T <: Real}(invs::Investments,
                                  row_inds::AbstractVector{T},
                                  col_ind::ColumnIndex)
    tm = convert(Timematr, invs)
    return tm[row_inds, col_ind]
end

# invs[MultiRowIndex, MultiColumnIndex] => Timematr
function Base.getindex{R <: Real, T <: ColumnIndex}(invs::Investments,
                                                    row_inds::AbstractVector{R},
                                                    col_inds::AbstractVector{T})
    output = convert(Timematr, invs)[row_inds, col_inds]

    ## are all columns selected?
    nAss = size(invs, 2)
    if length(col_inds) == nAss
        if sort(col_inds) == sort(names(invs))
            output = AssetMgmt.Investments(output.vals, idx(invs))
        end
    end
    return output
end


#enum cusparseStatus_t
#error messages from CUSPARSE

typealias cusparseStatus_t UInt32
const CUSPARSE_STATUS_SUCCESS                   = 0
const CUSPARSE_STATUS_NOT_INITIALIZED           = 1
const CUSPARSE_STATUS_ALLOC_FAILED              = 2
const CUSPARSE_STATUS_INVALID_VALUE             = 3
const CUSPARSE_STATUS_ARCH_MISMATCH             = 4
const CUSPARSE_STATUS_MAPPING_ERROR             = 5
const CUSPARSE_STATUS_EXECUTION_FAILED          = 6
const CUSPARSE_STATUS_INTERNAL_ERROR            = 7
const CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED = 8

#enum cusparseAction_t
# perform operation only indices only or
# on both data and indices
typealias cusparseAction_t UInt32
const CUSPARSE_ACTION_SYMBOLIC = 0
const CUSPARSE_ACTION_NUMERIC  = 1

#enum cusparseDirection_t
# parse dense matrix by rows or cols
# to compute its number of non-zeros
typealias cusparseDirection_t UInt32
const CUSPARSE_DIRECTION_ROW = 0
const CUSPARSE_DIRECTION_COL = 1

#enum cusparseHybPartition_t
# how to partition the HYB matrix
typealias cusparseHybPartition_t UInt32
const CUSPARSE_HYB_PARTITION_AUTO = 0
const CUSPARSE_HYB_PARTITION_USER = 1
const CUSPARSE_HYB_PARTITION_MAX  = 2

#enum cusparseFillMode_t
# filling for HE,TR,SY matrices
typealias cusparseFillMode_t UInt32
const CUSPARSE_FILL_MODE_LOWER = 0
const CUSPARSE_FILL_MODE_UPPER = 1

#enum cusparseDiagType_t
# is the diagonal all ones
typealias cusparseDiagType_t UInt32
const CUSPARSE_DIAG_TYPE_NON_UNIT = 0
const CUSPARSE_DIAG_TYPE_UNIT     = 1

#enum cusparsePointerMode_t
# are scalars on the host or device
typealias cusparsePointerMode_t UInt32
const CUSPARSE_POINTER_MODE_HOST   = 0
const CUSPARSE_POINTER_MODE_DEVICE = 1

#enum cusparseOperation_t
# no op, transpose, conj transpose
typealias cusparseOperation_t UInt32
const CUSPARSE_OPERATION_NON_TRANSPOSE       = 0
const CUSPARSE_OPERATION_TRANSPOSE           = 1
const CUSPARSE_OPERATION_CONJUGATE_TRANSPOSE = 2

#enum cusparseMatrixType_t
# marker to flag HE,TR,SY matrices
typealias cusparseMatrixType_t UInt32
const CUSPARSE_MATRIX_TYPE_GENERAL    = 0
const CUSPARSE_MATRIX_TYPE_SYMMETRIC  = 1
const CUSPARSE_MATRIX_TYPE_HERMITIAN  = 2
const CUSPARSE_MATRIX_TYPE_TRIANGULAR = 3

#enum cusparseSolvePolicy_t
# keep level info in solvers
typealias cusparseSolvePolicy_t UInt32
const CUSPARSE_SOLVE_POLICY_NO_LEVEL  = 0
const CUSPARSE_SOLVE_POLICY_USE_LEVEL = 1

#enum cusparseIndexBase_t
# 0- or 1-based indexing
typealias cusparseIndexBase_t UInt32
const CUSPARSE_INDEX_BASE_ZERO = 0
const CUSPARSE_INDEX_BASE_ONE  = 1

#struct cusparseMatDescr_t
# Describes shape and properties
# of a CUSPARSE matrix
type cusparseMatDescr_t
    MatrixType::cusparseMatrixType_t
    FillMode::cusparseFillMode_t
    DiagType::cusparseDiagType_t
    IndexBase::cusparseIndexBase_t
    function cusparseMatDescr_t(MatrixType,FillMode,DiagType,IndexBase)
        new(MatrixType,FillMode,DiagType,IndexBase)
    end
end

typealias cusparseSolveAnalysisInfo_t Ptr{Void}
typealias bsrsm2Info_t Ptr{Void}
typealias bsrsv2Info_t Ptr{Void}
typealias csrsv2Info_t Ptr{Void}
typealias csric02Info_t Ptr{Void}
typealias csrilu02Info_t Ptr{Void}
typealias bsric02Info_t Ptr{Void}
typealias bsrilu02Info_t Ptr{Void}

typealias cusparseContext Void
typealias cusparseHandle_t Ptr{cusparseContext}

#complex numbers

typealias cuComplex Complex{Float32}
typealias cuDoubleComplex Complex{Float64}

typealias CusparseFloat Union{Float64,Float32,Complex128,Complex64}
typealias CusparseReal Union{Float64,Float32}
typealias CusparseComplex Union{Complex128,Complex64}


module TestLift

using StructuredQueries
const SQ = StructuredQueries
using Base.Test

##############################################################################
##
## Test standard lifting semantics
##
##############################################################################

types = [
    Float16,
    Float32,
    Float64,
    Int128,
    Int16,
    Int32,
    Int64,
    Int8,
    UInt16,
    UInt32,
    UInt64,
    UInt8,
]

f(x::Number) = 5 * x
f(x::Number, y::Number) = x + y
f(x::Number, y::Number, z::Number) = x + y * z

for T in types
    a = one(T)
    x = Nullable{T}(a)
    y = Nullable{T}()

    U1 = Core.Inference.return_type(f, Tuple{T})
    @test isequal(SQ.lift(f, x), Nullable(f(a)))
    @test isequal(SQ.lift(f, y), Nullable{U1}())

    U2 = Core.Inference.return_type(f, Tuple{T, T})
    @test isequal(SQ.lift(f, x, x), Nullable(f(a, a)))
    @test isequal(SQ.lift(f, x, y), Nullable{U2}())

    U3 = Core.Inference.return_type(f, Tuple{T, T, T})
    @test isequal(SQ.lift(f, x, x, x), Nullable(f(a, a, a)))
    @test isequal(SQ.lift(f, x, y, x), Nullable{U3}())
end

##############################################################################
##
## Test non-standard lifting semantics
##
##############################################################################

# three-valued logic

# & truth table
v1 = SQ.lift(&, Nullable(true), Nullable(true))
v2 = SQ.lift(&, Nullable(true), Nullable(false))
v3 = SQ.lift(&, Nullable(true), Nullable{Bool}())
v4 = SQ.lift(&, Nullable(false), Nullable(true))
v5 = SQ.lift(&, Nullable(false), Nullable(false))
v6 = SQ.lift(&, Nullable(false), Nullable{Bool}())
v7 = SQ.lift(&, Nullable{Bool}(), Nullable(true))
v8 = SQ.lift(&, Nullable{Bool}(), Nullable(false))
v9 = SQ.lift(&, Nullable{Bool}(), Nullable{Bool}())

@test isequal(v1, Nullable(true))
@test isequal(v2, Nullable(false))
@test isequal(v3, Nullable{Bool}())
@test isequal(v4, Nullable(false))
@test isequal(v5, Nullable(false))
@test isequal(v6, Nullable(false))
@test isequal(v7, Nullable{Bool}())
@test isequal(v8, Nullable(false))
@test isequal(v9, Nullable{Bool}())

# | truth table
u1 = SQ.lift(|, Nullable(true), Nullable(true))
u2 = SQ.lift(|, Nullable(true), Nullable(false))
u3 = SQ.lift(|, Nullable(true), Nullable{Bool}())
u4 = SQ.lift(|, Nullable(false), Nullable(true))
u5 = SQ.lift(|, Nullable(false), Nullable(false))
u6 = SQ.lift(|, Nullable(false), Nullable{Bool}())
u7 = SQ.lift(|, Nullable{Bool}(), Nullable(true))
u8 = SQ.lift(|, Nullable{Bool}(), Nullable(false))
u9 = SQ.lift(|, Nullable{Bool}(), Nullable{Bool}())

@test isequal(u1, Nullable(true))
@test isequal(u2, Nullable(true))
@test isequal(u3, Nullable(true))
@test isequal(u4, Nullable(true))
@test isequal(u5, Nullable(false))
@test isequal(u6, Nullable{Bool}())
@test isequal(u7, Nullable(true))
@test isequal(u8, Nullable{Bool}())
@test isequal(u9, Nullable{Bool}())

# others

x1 = Nullable(1)
x2 = Nullable(2)
y = Nullable{Int}()
z1 = 1
z2 = 2

@test SQ.lift(isnull, x1) == false
@test SQ.lift(isnull, y) == true

@test SQ.lift(isless, x1, y) == true
@test SQ.lift(isless, y, x1) == false
@test SQ.lift(isless, x1, x2) == true
@test SQ.lift(isless, x2, x1) == false
@test SQ.lift(isless, y, y) == false
@test SQ.lift(isless, x1, z2) == true
@test SQ.lift(isless, x2, z1) == false
@test SQ.lift(isless, z1, x2) == true
@test SQ.lift(isless, z2, x1) == false

@test SQ.lift(get, x1) == 1
@test_throws NullException SQ.lift(get, y)
@test SQ.lift(get, y, 1) == 1

end


macro array(ex, num_sets)
    data = (Expr)[]
    for i = 1:eval(num_sets)
        push!(data, eval(ex))
    end
    map(eval, data)
end

function timer(data, weights)
    num_tests = length(data)
    @time time_weighted_median(data, weights, num_tests)
end

function time_weighted_median(data, weights, num_tests)
    for i = 1:num_tests
        weighted_median(data[i], weights[i])
    end
end


abstract type Order end

"""
Trait container for dimension and array ordering in AlignedGrid.

The default is `Ordered(Forward()`, `Forward())`

All combinations of forward and reverse order for data and indices seem to occurr
in real datasets, as strange as that seems. We cover these possibilities by specifying
the order of both explicitly.

Knowing the order of indices is important for using methods like `searchsortedfirst()`
to find indices in sorted lists of values. Knowing the order of the data is then
required to map to the actual indices. It's also used to plot the data later - which
always happens in smallest to largest order.

Base also defines Forward and Reverse, but they seem overly complicated for our purposes.
"""
struct Ordered{D,A,R} <: Order
    index::D
    array::A
    relation::R
end
Ordered(; index=Forward(), array=Forward(), relation=Forward()) =
    Ordered(index, array, relation)

indexorder(order::Ordered) = order.index
arrayorder(order::Ordered) = order.array
relationorder(order::Ordered) = order.relation

"""
Trait indicating that the array or dimension has no order.
"""
struct Unordered{R} <: Order
    relation::R
end
Unordered() = Unordered(Forward())

indexorder(order::Unordered) = order
arrayorder(order::Unordered) = order
relationorder(order::Unordered) = order.relation

"""
Trait indicating that the array or dimension is in the normal forward order.
"""
struct Forward <: Order end

"""
Trait indicating that the array or dimension is in the reverse order.
Selector lookup or plotting will be reversed.
"""
struct Reverse <: Order end

Base.reverse(::Reverse) = Forward()
Base.reverse(::Forward) = Reverse()
# Base.reverse(o::Ordered) =
    # Ordered(indexorder(o), reverse(relationorder(o)), reverse(arrayorder(o)))
# Base.reverse(o::Unordered) =
    # Unordered(reverse(relationorder(o)))

reverseindex(o::Unordered) =
    Unordered(reverse(relationorder(o)))
reverseindex(o::Ordered) =
    Ordered(reverse(indexorder(o)), arrayorder(o), reverse(relationorder(o)))

reversearray(o::Unordered) =
    Unordered(reverse(relationorder(o)))
reversearray(o::Ordered) =
    Ordered(indexorder(o), reverse(arrayorder(o)), reverse(relationorder(o)))

isrev(::Forward) = false
isrev(::Reverse) = true

"""
Indicates wether the cell value is specific to the locus point
or is related to the whole the span.

The span may contain a value if the distance between locii if known.
This will often be identical to the distance between any two sequential
cell values, but may be distinct due to rounding errors in a vector index,
or context-dependent spans such as `Month`.
"""
abstract type Sampling end

"""
Each cell value represents a siegle discrete sample taken at the index location.
"""
struct SingleSample <: Sampling end

"""
Multiple samples from the span combined using method `M`,
where `M` is `typeof(mean)`, `typeof(sum)` etc.
"""
struct MultiSample{M} <: Sampling end
MultiSample() = MultiSample{Nothing}()

"""
The sampling method is unknown.
"""
struct UnknownSampling <: Sampling end

"""
Indicate the position of index values in grid cells.

This is frequently `Start` for time series, but may be `Center`
for spatial data.
"""
abstract type Locus end

"""
Indicates dimensions that are defined by their center coordinates/time/position.
"""
struct Center <: Locus end

"""
Indicates dimensions that are defined by their start coordinates/time/position.
"""
struct Start <: Locus end

"""
Indicates dimensions that are defined by their end coordinates/time/position.
"""
struct End <: Locus end

struct UnknownLocus <: Locus end

"""
Traits describing the grid type of a dimension.
"""
abstract type Grid end

dims(g::Grid) = nothing
arrayorder(grid::Grid) = arrayorder(order(grid))
indexorder(grid::Grid) = indexorder(order(grid))
relationorder(grid::Grid) = relationorder(order(grid))

Base.reverse(g::Grid) = rebuild(g, reverse(order(g)))
reversearray(g::Grid) = rebuild(g, reversearray(order(g)))
reverseindex(g::Grid) = rebuild(g, reverseindex(order(g)))

slicegrid(grid::Grid, index, I) = grid

"""
Fallback grid type
"""
struct UnknownGrid <: Grid end

order(::UnknownGrid) = Unordered()

# TODO handle unordered in all grids
bounds(grid::UnknownGrid, dim) = first(dim), last(dim)

"""
A grid dimension that is independent of other grid dimensions.
"""
abstract type IndependentGrid{O} <: Grid end

"""
A grid dimension aligned exactly with a standard dimension, such as lattitude or longitude.
"""
abstract type AbstractAlignedGrid{O,L,Sa} <: IndependentGrid{O} end

order(g::AbstractAlignedGrid) = g.order
locus(g::AbstractAlignedGrid) = g.locus
sampling(g::AbstractAlignedGrid) = g.sampling

"""
An [`AlignedGrid`](@ref) grid without known regular spacing. These grids will generally be paired
with a vector of coordinates along the dimension, instead of a range.

Bounds are given as the first and last points, which omits the span of one cell,
as it is not known. To fix this use either a [`BoundedGrid`](@ref) with specified
starting bounds or a [`RegularGrid`](@ref) with a known constand cell span.

## Fields
- `order::Order`: `Order` trait indicating array and index order
- `locus::Locus`: `Locus` trait indicating the position of the indexed point within the cell span
- `sampling::Sampling`: `Sampling` trait indicating wether the grid cells are single samples or means
"""
struct AlignedGrid{O<:Order,L<:Locus,Sa<:Sampling} <: AbstractAlignedGrid{O,L,Sa}
    order::O
    locus::L
    sampling::Sa
end
AlignedGrid(; order=Ordered(), locus=Start(), sampling=UnknownSampling()) =
    AlignedGrid(order, locus, sampling)

rebuild(g::AlignedGrid, order=order(g), locus=locus(g), sampling=sampling(g)) =
    AlignedGrid(order, locus, sampling)

bounds(grid::AlignedGrid, dim) = first(dim), last(dim)

"""
An alligned grid without known regular spacing and tracked bounds.
These grids will generally be paired with a vector of coordinates along the
dimension, instead of a range.

As the size of the cells is not known, the bounds must be actively tracked.

## Fields
- `order::Order`: `Order` trait indicating array and index order
- `locus::Locus`: `Locus` trait indicating the position of the indexed point within the cell span
- `sampling::Sampling`: `Sampling` trait indicating wether the grid cells are single samples or means
- `bounds`: the outer edges of the grid (different to the first and last coordinate).
"""
struct BoundedGrid{O<:Order,L<:Locus,Sa<:Sampling,B} <: AbstractAlignedGrid{O,L,Sa}
    order::O
    locus::L
    sampling::Sa
    bounds::B
end
BoundedGrid(; order=Ordered(), locus=Start(), sampling=UnknownSampling(), bounds=nothing) =
    BoundedGrid(order, locus, sampling, bounds)

bounds(g::BoundedGrid) = g.bounds

rebuild(g::BoundedGrid, order=order(g), locus=locus(g), sampling=sampling(g), bounds=bounds(g)) =
    BoundedGrid(order, locus, sampling, bounds)

# TODO: deal with unordered AbstractArray
slicegrid(g::BoundedGrid, index, I) =
    rebuild(g, order(g), locus(g), sampling(g), slicebounds(locus(g), bounds(g), index, I))

slicebounds(loci::Start, bounds, index, I) =
    index[first(I)],
    last(I) >= lastindex(index)  ? bounds[2] : index[last(I) + 1]
slicebounds(loci::End, bounds, index, I) =
    first(I) <= firstindex(index) ? bounds[1] : index[first(I) - 1],
    index[last(I)]
slicebounds(loci::Center, bounds, index, I) =
    first(I) <= firstindex(index) ? bounds[1] : (index[first(I) - 1]   + index[first(I)]) / 2,
    last(I)  >= lastindex(index)  ? bounds[2] : (index[last(I) + 1] + index[last(I)]) / 2

abstract type AbstractRegularGrid{O,L,Sa,Sp} <: AbstractAlignedGrid{O,L,Sa} end

"""
An [`AlignedGrid`](@ref) where all cells are the same size and evenly spaced.

## Fields
- `order::Order`: `Order` trait indicating array and index order
- `locus::Locus`: `Locus` trait indicating the position of the indexed point within the cell span
- `sampling::Sampling`: `Sampling` trait indicating wether the grid cells are single samples or means
- `span::Number`: the size of a grid step, such as 1u"km" or `Month(1)`
"""
struct RegularGrid{O<:Order,L<:Locus,Sa<:Sampling,Sp} <: AbstractRegularGrid{O,L,Sa,Sp}
    order::O
    locus::L
    sampling::Sa
    span::Sp
end
RegularGrid(; order=Ordered(), locus=Start(), sampling=UnknownSampling(), span=nothing) =
    RegularGrid(order, locus, sampling, span)

span(grid::RegularGrid) = grid.span

rebuild(g::RegularGrid, order=order(g), locus=locus(g), sampling=sampling(g), span=span(g)) =
    RegularGrid(order, locus, sampling, span)

bounds(grid::RegularGrid, dim) = bounds(indexorder(grid), locus(grid), grid, dim)
bounds(::Forward, ::Start, grid, dim) = first(dim), last(dim) + span(grid)
bounds(::Reverse, ::Start, grid, dim) = last(dim), first(dim) + span(grid)
bounds(::Forward, ::Center, grid, dim) = first(dim) - span(grid) / 2, last(dim) + span(grid) / 2
bounds(::Reverse, ::Center, grid, dim) = last(dim) - span(grid) / 2, first(dim) + span(grid) / 2
bounds(::Forward, ::End, grid, dim) = first(dim) - span(grid), last(dim)
bounds(::Reverse, ::End, grid, dim) = last(dim) - span(grid), first(dim)

abstract type AbstractCategoricalGrid{O} <: IndependentGrid{O} end

"""
A grid dimension where the values are categories.

## Fields
- `order`: `Order` trait indicating array and index order
"""
struct CategoricalGrid{O<:Order} <: AbstractCategoricalGrid{O}
    order::O
end
CategoricalGrid(; order=Ordered()) = CategoricalGrid(order)

order(g::CategoricalGrid) = g.order

rebuild(g::CategoricalGrid, order=order(g)) = CategoricalGrid(order)

bounds(grid::CategoricalGrid, dim) = bounds(indexorder(grid), grid, dim)
bounds(::Forward, grid, dim) = first(dim), last(dim)
bounds(::Reverse, grid, dim) = last(dim), first(dim)
bounds(::Unordered, grid, dim) = error("Cannot call `bounds` on an unordered grid")

"""
Traits describing a grid dimension that is dependent on other grid dimensions.

Indexing into a dependent dimension must provide all other dependent dimensions.
"""
abstract type DependentGrid <: Grid end

locus(g::DependentGrid) = g.locus
dims(g::DependentGrid) = g.dims
sampling(g::DependentGrid) = g.sampling

"""
Grid type using an affine transformation to convert dimension from
`dim(grid)` to `dims(array)`.

## Fields
- `dims`: a tuple containing dimenension types or symbols matching the order
          needed by the transform function.
- `sampling`: a `Sampling` trait indicating wether the grid cells are sampled points or means
"""
struct TransformedGrid{D,L,Sa<:Sampling} <: DependentGrid
    dims::D
    locus::L
    sampling::Sa
end
TransformedGrid(dims=(), locus=Start(), sampling=UnknownSampling()) =
    TransformedGrid(dims, locus, sampling)

rebuild(g::TransformedGrid, dims=dims(g), locus=locus(g), sampling=sampling(g)) =
    TransformedGrid(dims, locus, sampling)

"""
A grid dimension that uses an array lookup to convert dimension from
`dim(grid)` to `dims(array)`.

## Fields
- `dims`: a tuple containing dimenension types or symbols matching the order
          needed to index the lookup matrix.
- `sampling`: a `Sampling` trait indicating wether the grid cells are sampled points or means
"""
struct LookupGrid{D,L,Sa<:Sampling} <: DependentGrid
    dims::D
    locus::L
    sampling::Sa
end
LookupGrid(dims=(), locus=Start(), sampling=UnknownSampling()) =
    LookupGrid(dims, locus, sampling)

rebuild(g::LookupGrid; dims=dims(g), locus=locus(g), sampling=sampling(g)) =
    LookupGrid(dims, locus, sampling)


mutable struct ForceOrderedLikelihood <: MyXMLElement
    el::XMLOrNothing
    parameter::MyXMLElement

    function ForceOrderedLikelihood(p::MyXMLElement)
        return new(nothing, p)
    end
end

function make_xml(fol::ForceOrderedLikelihood)
    el = new_element("forceOrderedLikelihood")
    make_xml(fol.parameter)
    param_id = get_id(fol.parameter.el)
    add_ref_el(el, fol.parameter.el)
    set_id!(el, param_id * ".forceDescending")
    fol.el = el
    return el
end

function get_priors(fol::ForceOrderedLikelihood)
    make_xml(fol)
    return [fol.el]
end

mutable struct ColumnSwapOperator <: OperatorXMLElement
    el::XMLOrNothing
    matparam::MatrixParameter
    weight::Float64

    function ColumnSwapOperator(mp::MatrixParameter, weight::Float64)
        return new(nothing, mp, weight)
    end
end

function make_xml(cso::ColumnSwapOperator)
    el = new_element("columnSwapOperator")
    make_xml(cso.matparam)
    add_ref_el(el, cso.matparam.el)
    set_attribute(el, bn.WEIGHT, cso.weight)
    cso.el = el
    return el
end


using MultiScaleArrays
using OrdinaryDiffEq, DiffEqBase, Test, StochasticDiffEq

#=
struct Cell{B} <: AbstractMultiScaleArrayLeaf{B}
    values::Vector{B}
end
struct Population{T<:AbstractMultiScaleArray,B<:Number} <: AbstractMultiScaleArray{B}
    nodes::Vector{T}
    values::Vector{B}
    end_idxs::Vector{Int}
end
struct Tissue{T<:AbstractMultiScaleArray,B<:Number} <: AbstractMultiScaleArray{B}
    nodes::Vector{T}
    values::Vector{B}
    end_idxs::Vector{Int}
end
struct Embryo{T<:AbstractMultiScaleArray,B<:Number} <: AbstractMultiScaleArrayHead{B}
    nodes::Vector{T}
    values::Vector{B}
    end_idxs::Vector{Int}
end
=#

cell1 = Cell([1.0;2.0;3.0])
cell2 = Cell([4.0;5])
population = construct(Population,deepcopy([cell1,cell2]),[11.0,12.0,13.0]) # Make a Population from cells
cell3 = Cell([3.0;2.0;5.0])
cell4 = Cell([4.0;6])
population2 = construct(Population,deepcopy([cell3,cell4]),[8.0,9.0])

@test population[1] == 1.0
@test population[2] == 2.0
@test population[3] == 3.0
@test population[4] == 4.0
@test population[5] == 5.0
@test population[6] == 11.0
@test population[7] == 12.0
@test population[8] == 13.0

tissue1 = construct(Tissue,deepcopy([population,population2])) # Make a Tissue from Populations
tissue2 = construct(Tissue,deepcopy([population2,population]))
embryo = construct(Embryo,deepcopy([tissue1,tissue2]),[31.0,32.0]) # Make an embryo from Tissues

@test tissue1[1] == 1.0
@test tissue1[2] == 2.0
@test tissue1[3] == 3.0
@test tissue1[4] == 4.0
@test tissue1[5] == 5.0
@test tissue1[6] == 11.0
@test tissue1[7] == 12.0
@test tissue1[8] == 13.0

@test tissue1[9]  == 3.0
@test tissue1[10] == 2.0
@test tissue1[11] == 5.0
@test tissue1[12] == 4.0
@test tissue1[13] == 6.0
@test tissue1[14] == 8.0
@test tissue1[15] == 9.0

@test embryo[1] == 1.0
@test embryo[2] == 2.0
@test embryo[3] == 3.0
@test embryo[4] == 4.0
@test embryo[5] == 5.0
@test embryo[6] == 11.0
@test embryo[7] == 12.0
@test embryo[8] == 13.0

@test embryo[9]  == 3.0
@test embryo[10] == 2.0
@test embryo[11] == 5.0
@test embryo[12] == 4.0
@test embryo[13] == 6.0
@test embryo[14] == 8.0
@test embryo[15] == 9.0

@test embryo[16] == 3.0
@test embryo[17] == 2.0
@test embryo[18] == 5.0
@test embryo[19] == 4.0
@test embryo[20] == 6.0
@test embryo[21] == 8.0
@test embryo[22] == 9.0

@test embryo[23] == 1.0
@test embryo[24] == 2.0
@test embryo[25] == 3.0
@test embryo[26] == 4.0
@test embryo[27] == 5.0
@test embryo[28] == 11.0
@test embryo[29] == 12.0
@test embryo[30] == 13.0

@test embryo[31] == 31.0
@test embryo[32] == 32.0


"""
    FENodeSetModule

Module for the finite element node set.
"""
module FENodeSetModule

__precompile__(true)

using ..FTypesModule: FInt, FFlt, FCplxFlt, FFltVec, FIntVec, FFltMat, FIntMat, FMat, FVec, FDataDict
import Base.count

"""
Finite element node set type.
"""
mutable struct FENodeSet
	"""
	Array of node locations. Indexed with the node number.
	The location of node `j` is given by `xyz[j,:]`.
	Clearly, the nodes needs to be numbered between `1` and `size(xyz, 1)`.
	The constructor makes a *copy* of the input `xyz` array for safety.
	"""
	xyz::Array{FFlt, 2}

	function FENodeSet(xyz::FFltMat)
		self = new(deepcopy(xyz)); # Need to make a COPY of the input array!
		return self
	end
end

"""
    spacedim(self::FENodeSet)

Number of dimensions of the space in which the node lives, 1, 2, or 3.
"""
spacedim(self::FENodeSet) =size(self.xyz,2)

"""
    xyz3(self::FENodeSet)

Get the  3-D coordinate that define the location  of the node.
Even if the nodes  were specified in  lower dimension (1-D, 2-D)
this function returns  a 3-D coordinate  by padding with zeros.
"""
function xyz3(self::FENodeSet)
    if (size(self.xyz,2)==1)
        val = [self.xyz zeros(size(self.xyz,1),2)];
    elseif (size(self.xyz,2)==2)
        val = [self.xyz zeros(size(self.xyz,1),1)];
    else
        val = deepcopy(self.xyz);
    end
end

"""
    count(self::FENodeSet)

Get the number of finite element nodes in the node set.
"""
function count(self::FENodeSet)
    return size(self.xyz,1)::FInt
end

end


function FiniteDifferencePropagatorConverter(arg0::PropagatorBuilder, arg1::jdouble, arg2::jint)
    return FiniteDifferencePropagatorConverter((PropagatorBuilder, jdouble, jint), arg0, arg1, arg2)
end


__precompile__()

module NamedDimensions
using FunctionalData

export NamedDims, named, array, names, at
immutable NamedDims{T,N}
    data::Array{T,N}
    names::Array{Symbol}
    function NamedDims(a, names...)
        names = collect(names)
        length(names) > ndims(a) && error("NamedDimensions: Got $(length(names)) names $names for $(ndims(a)) dims $(size(a))")
        for i = 1:len(names), j = i+1:len(names)
            names[i]==names[j] && error("NamedDimensions: Duplicate names: $names")
        end

        if length(names) < ndims(a)
            names = vcat(dimnames(names, ndims(a)-length(names)), names)
        end
        new(a, names)
    end
end

named{T}(a::AbstractArray{T,0}, named::Vector{Symbol}) = a[1]
named(a::AbstractArray, names::Vector{Symbol}) = named(a, names...)
named(a::AbstractArray, names...) = (assert(all(x->isa(x,Symbol), names)); NamedDims{eltype(a),ndims(a)}(a, names...))
named(a::NamedDims, names::Vector{Symbol}) = named(a, names...)
named(a, args...) = a

import Base: getindex, eltype, isempty
array(a) = a
array(a::NamedDims) = a.data
array(a::NamedDims, inds...) = array(named(a, inds...))
getindex(a::NamedDims, inds...) = named(a, inds...)
eltype{T,N}(a::NamedDims{T,N}) = T
isempty(a::NamedDims) = len(a) == 0

for f in [:fst, :snd, :third, :last]
    @eval import FunctionalData.$f
    @eval $f(a::NamedDims) = named(squeezer($f(a.data)), droplast(a.names))
    @eval $f(a::NamedDims, x::Symbol) = $f(named(a, x))
end

import FunctionalData.at
at(a::NamedDims, i::Int) = a[last(a.names) => i]

for f in [:take, :takelast, :drop, :droplast, :partition, :partsoflen]
    @eval import FunctionalData.$f
    @eval $f(a::NamedDims, i::Int) = named($f(a.data,i), a.names)
end

for f in [:part, :trimmedpart]
    @eval import FunctionalData.$f
    @eval $f{T}(a::NamedDims, i::DenseArray{T,2}) = named($f(a.data,i), a.names)
    @eval $f(a::NamedDims, i::UnitRange) = named($f(a.data,i), a.names)
    @eval $f(a::NamedDims, i::AbstractVector) = named($f(a.data,i), a.names)
end

import FunctionalData.concat
function concat(a::NamedDims, b::NamedDims)
    assert(a.names == b.names)
    assert(size(fst(a)) == size(fst(b)))
    named(concat(a.data, b.data), a.names)
end

import FunctionalData.flatten
function flatten{T,N}(a::Vector{NamedDims{T,N}})
    assert(all(x->x.names == fst(a).names, a))
    assert(all(x->size(x)[1:end-1] == size(fst(a))[1:end-1], a))
    named(flatten(map(a,array)), fst(a).names)
end

import FunctionalData.stack
function stack(a, name::Symbol)
    assert(all(x->x.names == fst(a).names, a))
    assert(all(x->size(x) == size(fst(a)), a))
    named(stack(map(a,array)), concat(fst(a).names, name))
end

function stack(r, a::NamedDims)
    isempty(r) && return []
    if isa(fst(r), NamedDims)
        return stack(typed(r), last(a.names))
    else
        return stack(r)
    end
end

import FunctionalData: map, map2, map3, map4, map5

function map{T,N}(a::NamedDims{T,N}, f::Function)
    r = [f(at(a,i)) for i in 1:len(a)]
    return stack(r, a)
end
mapper2(f, N, a, b, fa, fb) = stack([f(fa(at(a,i)), fb(at(b,i))) for i in 1:len(N)], N)
squeezer(a::AbstractArray) = size(a,ndims(a)) == 1 ? squeeze(a,ndims(a)) : a
squeezer(a) = a
map2(a::NamedDims, b::NamedDims, f::Function) = mapper2(f,a,a,b,id,id)
map2(a::NamedDims, b, f::Function) = mapper2(f,a,a,b,id,squeezer)
map2(a, b::NamedDims, f::Function) = mapper2(f,b,a,b,squeezer,id)

at(a::NamedDims, inds::Tuple) = at(a.data, inds)
at(a::NamedDims, inds...) = named(a, inds...)
call(a::NamedDims, inds...) = named(a, inds...)

names(a::NamedDims) = a.names

import Base.==
==(a::NamedDims, b::NamedDims) = a.names == b.names && a.data == b.data

import Base.size
size(a::NamedDims) = size(a.data)
size(a::NamedDims, dim::Int) = size(a.data, dim)
size(a::NamedDims, dim::Symbol) = size(a.data, getind(a, dim))
import Base.ndims
ndims(a::NamedDims) = ndims(a.data)
import Base.length
length(a::NamedDims) = length(a.data)
import FunctionalData.len
len(a::NamedDims) = len(a.data)

function getind(a::NamedDims, x::Symbol)
    ind = findfirst(x.==a.names)
    if ind == 0
        error("NamedDimensions: No dimension $x in $(a.names)")
    end
    ind
end

function named(a::NamedDims, inds...)
    inds = [isa(x, Pair) && x.second == :end ? Pair(x.first, size(a, x.first)) : x for x in inds]
    isname(x) = (isa(x, Pair) && isa(x.first, Symbol)) || isa(x, Symbol)
    issqueezer(x) = isa(x, Pair) && isa(x.second, Integer)
    if length(inds)==1 && !isname(inds[1])
        return a.data[inds...]
    end
    if all(x->!isa(x, Pair), inds) && any(x->!isa(x, Symbol), inds)  # pairs of arguments
        if length(inds) % 2 != 0
            error("NamedDimensions: Expected an even number of parameters, got $(inds)")
        end
        inds = @p map2 inds[1:2:end] inds[2:2:end] Pair
        return named(a, inds...)
    end

    [assert(isa(x, Pair) || isa(x, Symbol)) for x in inds]
    inds = [isa(x, Pair) ? x : Pair(x, Colon()) for x in inds]
    ordered    = @p filter inds not*issqueezer | map fst
    squeezed   = @p filter inds issqueezer | map fst
    invalid = @p concat ordered squeezed | filter not*inside a.names
    if !isempty(invalid)
        error("NamedDimensions: Invalid dimensions $invalid . Valid dimensions are $(a.names)")
    end
    implicitorder = @p filter a.names not*inside squeezed | filter not*inside ordered
    ordered = @p concat implicitorder ordered

    d = Dict(inds...)
    inds = [get(d, x, Colon())  for x in a.names]
    newnames = Symbol[]
    for i in 1:length(a.names)
        isa(inds[i], Integer) ? nothing : push!(newnames, a.names[i])
    end

    b = (VERSION < v"0.5-" ? slice : view)(a.data, inds...)
    if ordered == newnames
        return named(b, newnames)
    else
        perm = [findfirst(x .== newnames) for x in ordered]
        return named(permutedims(b, perm), newnames[perm])
    end
end

import Base.summary
function summary{T,N}(a::NamedDims{T,N})
    @p mapvec2 a.names size(a.data) ((n,s) -> "$s $n") | join " x " | concat " :: NamedDims{$T,$N}"
end

import Base.show
show(io::IO, a::NamedDims) = (println(io, summary(a)); show(io,a.data))

import FunctionalData.showinfo
function showinfo(io::IO, a::NamedDims, comment::AbstractString = "")
    print(io, comment, length(comment) > 0 ? "  --  ": "----  ")
    @p mapvec2 a.names size(a.data) ((n,s) -> "$s $n") | join " x " | println io _
    showinfo(io, a.data; showheader = false)
    a
end

for f in [:sqrt]
    @eval import Base.$f
    @eval $f(a::NamedDims, args...) = named($f(a.data, args...), a.names)
end

for f in [:power, :square]
    @eval import FunctionalData.$f
    @eval $f(a::NamedDims, args...) = named($f(a.data, args...), a.names)
end

for f in [:mean, :maximum, :minimum, :median, :std, :var, :sum]
    @eval import Base.$f
    @eval $f(a::NamedDims) = $f(a.data)
    @eval $f(a::NamedDims, dim::Int) = named(squeeze($f(a.data,dim),dim), dropat(a.names,dim))
    @eval $f(a::NamedDims, dim::Symbol) = $f(a, getind(a, dim))
end

function broadcast(f::Function, smaller::NamedDims, larger::NamedDims)
    assert(all(x->in(x, larger.names), smaller.names))
    last = @p filter larger.names not*inside smaller.names
    orderforsmaller = @p filter larger.names inside smaller.names
    a = isempty(last) ? larger : named(larger, last)
    reorderedsmaller = smaller[orderforsmaller].data
    r = @p f reorderedsmaller a.data
    named(r, concat(orderforsmaller, last))[larger.names]
end

for f in [:.+, :.-, :.*, :./, :.\, :.^]
    @eval import Base.$f
    @eval $f(a::NamedDims, b::AbstractArray) = ndims(a.data) >= ndims(b)-1 ? named($f(a.data, b), a.names) : $f(a.data, b)
    @eval $f(a::AbstractArray, b::NamedDims) = ndims(b.data) >= ndims(a)-1 ? named($f(a, b.data), b.names) : $f(a, b.data)
    @eval function $f(a::NamedDims, b::NamedDims)
        length(a.names) < length(b.names) ? broadcast($f, a, b) : broadcast($f, b, a)
    end
end

for f in [:-, :+]
    @eval import Base.$f
    @eval $f(a::NamedDims, b::AbstractArray) = named($f(a.data, b), a.names)
    @eval $f(a::AbstractArray, b::NamedDims) = named($f(a, b.data), b.names)
    @eval function $f(a::NamedDims, b::NamedDims)
        assert(a.names == b.names)
        named($f(a.data, b.data), a.names)
   end
end

chars = collect('A':'Z')
dimnames(a::NamedDims, args...) = dimnames(a.names, args...)
function dimnames(names, n = 2)
    @p filter [Symbol("dim$(chars[i])") for i in 1:n+len(names)] not*inside names | take n
end

for (f,inds) in [(:*,[1,4]), (:/, [1,3]), (:\, [2,4])]
    @eval import Base.$f
    @eval function $f(a::NamedDims, b::NamedDims)
        named($f(a.data, b.data), concat(a.names, b.names)[$inds])
    end
    @eval $f(a::NamedDims, b::AbstractArray) = named($f(a.data, b), concat(a.names, dimnames(a))[$inds])
    @eval $f(a::AbstractArray, b::NamedDims) = named($f(a, b.data), concat(dimnames(a), b.names)[$inds])
end

end # module


function test_antidomain_singlevariable(
    model::MOI.ModelLike,
    config::MOIT.Config{T},
) where {T <: Real}
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.Integer) # x1, x2
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{T}) # c1
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.GreaterThan{T}) # c2
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.LessThan{T}) # c3
    @MOIT.requires MOI.supports_constraint(model, MOI.VectorOfVariables, CP.AllDifferent) # c4
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, CP.AntiDomain{T}) # c5

    x1, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x2, _ = MOI.add_constrained_variable(model, MOI.Integer())

    c1 = MOI.add_constraint(model, x1, MOI.EqualTo(T(1)))
    c2 = MOI.add_constraint(model, x2, MOI.GreaterThan(T(1)))
    c3 = MOI.add_constraint(model, x2, MOI.LessThan(T(3)))
    c4 = MOI.add_constraint(model, MOI.VectorOfVariables([x1, x2]), CP.AllDifferent(2))

    c5 = MOI.add_constraint(model, x2, CP.AntiDomain(Set(T[3])))

    @test MOI.is_valid(model, x1)
    @test MOI.is_valid(model, x2)
    @test MOI.is_valid(model, c1)
    @test MOI.is_valid(model, c2)
    @test MOI.is_valid(model, c3)
    @test MOI.is_valid(model, c4)
    @test MOI.is_valid(model, c5)

    if MOIT._supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT

        @test MOI.get(model, MOI.ResultCount()) >= 1
        @test MOI.get(model, MOI.VariablePrimal(), x1) == 1
        @test MOI.get(model, MOI.VariablePrimal(), x2) == 2
    end
end

function MOIT.setup_test(
    ::typeof(test_antidomain_singlevariable),
    mock::MOIU.MockOptimizer,
    ::Config{T},
) where {T <: Real}
    MOIU.set_mock_optimize!(
        mock,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, T[1, 2]),
    )
    return
end

function test_antidomain_scalaraffinefunction(
    model::MOI.ModelLike,
    config::MOIT.Config{T},
) where {T <: Real}
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.Integer) # x1, x2
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.EqualTo{Int}) # c1
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.GreaterThan{Int}) # c2
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.LessThan{Int}) # c3
    @MOIT.requires MOI.supports_constraint(model, MOI.ScalarAffineFunction{Int}, CP.AllDifferent) # c4
    @MOIT.requires MOI.supports_constraint(model, MOI.VariableIndex, CP.AntiDomain{Int}) # c5

    x1, _ = MOI.add_constrained_variable(model, MOI.Integer())
    x2, _ = MOI.add_constrained_variable(model, MOI.Integer())

    c1 = MOI.add_constraint(model, x1, MOI.EqualTo(T(1)))
    c2 = MOI.add_constraint(model, x2, MOI.GreaterThan(T(1)))
    c3 = MOI.add_constraint(model, x2, MOI.LessThan(T(3)))
    c4 = MOI.add_constraint(model, MOIU.vectorize(one(T) .* [x1, x2]), CP.AllDifferent(2))

    c5 = MOI.add_constraint(
        model,
        one(T) * x2,
        CP.AntiDomain(Set([3])),
    )

    @test MOI.is_valid(model, x1)
    @test MOI.is_valid(model, x2)
    @test MOI.is_valid(model, c1)
    @test MOI.is_valid(model, c2)
    @test MOI.is_valid(model, c3)
    @test MOI.is_valid(model, c4)
    @test MOI.is_valid(model, c5)

    if MOIT._supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT

        @test MOI.get(model, MOI.ResultCount()) >= 1
        @test MOI.get(model, MOI.VariablePrimal(), x1) == 1
        @test MOI.get(model, MOI.VariablePrimal(), x2) == 2
    end
end

function MOIT.setup_test(
    ::typeof(test_antidomain_scalaraffinefunction),
    mock::MOIU.MockOptimizer,
    ::Config{T},
) where {T <: Real}
    MOIU.set_mock_optimize!(
        mock,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, T[1, 2]),
    )
    return
end


#Constants

const accl_g = 9.80665

# ---------------------------------------------------------------------------------------------
# Theodorsen solver input
type TheoDef
    alpha_amp :: Float64
    h_amp :: Float64
    alpha_mean :: Float64
    alpha_zl :: Float64
    k :: Float64
    phi :: Float64
    pvt :: Float64
end

type DelVortDef
    flag :: Int8
    limit :: Int16
    dist :: Float64
end

type TheoDefwFlap
    alpha_amp :: Float64
    h_amp :: Float64
    alpha_mean :: Float64
    alpha_zl :: Float64
    k :: Float64
    phi :: Float64
    pvt :: Float64
    beta_amp :: Float64
    xf :: Float64
    psi :: Float64
end
# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
# Data structures used to represent the airfoil kinematics
type KinemPar
    alpha :: Float64
    h :: Float64
    alphadot :: Float64
    hdot :: Float64
    u :: Float64
    udot :: Float64
end

# Added by Laura Merchant 2016
type KinemParwFlap
    alpha :: Float64
    h :: Float64
    alphadot :: Float64
    hdot :: Float64
    u :: Float64
    udot :: Float64
    n :: Float64
    ndot :: Float64
end

#For airfoil with 2DOF in pitch and plunge
type KinemPar2DOF
    alpha :: Float64
    h :: Float64
    alphadot :: Float64
    hdot :: Float64
    u :: Float64
    udot :: Float64
    alphaddot :: Float64
    hddot :: Float64
    alpha_pr :: Float64
    alpha_pr2 :: Float64
    alpha_pr3 :: Float64
    h_pr :: Float64
    h_pr2 :: Float64
    h_pr3 :: Float64
    alphadot_pr :: Float64
    alphadot_pr2 :: Float64
    alphadot_pr3 :: Float64
    hdot_pr :: Float64
    hdot_pr2 :: Float64
    hdot_pr3 :: Float64
    alphaddot_pr :: Float64
    alphaddot_pr2 :: Float64
    alphaddot_pr3 :: Float64
    hddot_pr :: Float64
    hddot_pr2 :: Float64
    hddot_pr3 :: Float64
end

type KinemPar2DFree
    alpha :: Float64
    h :: Float64
    alphadot :: Float64
    hdot :: Float64
    u :: Float64
    udot :: Float64
    alphaddot :: Float64
    hddot :: Float64
    alpha_pr :: Float64
    h_pr :: Float64
    alphadot_pr :: Float64
    alphadot_pr2 :: Float64
    alphadot_pr3 :: Float64
    hdot_pr :: Float64
    hdot_pr2 :: Float64
    hdot_pr3 :: Float64
    alphaddot_pr :: Float64
    alphaddot_pr2 :: Float64
    alphaddot_pr3 :: Float64
    hddot_pr :: Float64
    hddot_pr2 :: Float64
    hddot_pr3 :: Float64
    u_pr :: Float64
    udot_pr :: Float64
    udot_pr2 ::Float64
    udot_pr3 :: Float64
end

type TwoDOFPar
    x_alpha :: Float64
    r_alpha :: Float64
    kappa :: Float64
    w_alpha :: Float64
    w_h :: Float64
    w_alphadot :: Float64
    w_hdot :: Float64
    cubic_h_1 :: Float64
    cubic_h_3 :: Float64
    cubic_alpha_1 :: Float64
    cubic_alpha_3 :: Float64
end

type TwoDFreePar
    r_g :: Float64
    x_g :: Float64
    kappa :: Float64
end

# ---------------------------------------------------------------------------------------------
# Various motion kinematics definitions
abstract MotionDef

immutable EldUpDef <: MotionDef
    amp :: Float64
    K :: Float64
    a :: Float64
end

immutable EldUptstartDef <: MotionDef
    amp :: Float64
    K :: Float64
    a :: Float64
    tstart :: Float64
end

immutable EldRampReturnDef <: MotionDef
    amp :: Float64
    K :: Float64
    a :: Float64
end

immutable ConstDef <: MotionDef
    amp :: Float64
end

function (eld::EldUpDef)(t)
    sm = pi*pi*eld.K/(2*(eld.amp)*(1 - eld.a))
    t1 = 1.
    t2 = t1 + ((eld.amp)/(2*eld.K))
    ((eld.K/sm)*log(cosh(sm*(t - t1))/cosh(sm*(t - t2))))+(eld.amp/2)
end

function (eld::EldUptstartDef)(t)
    sm = pi*pi*eld.K/(2*(eld.amp)*(1 - eld.a))
    t1 = eld.tstart
    t2 = t1 + ((eld.amp)/(2*eld.K))
    ((eld.K/sm)*log(cosh(sm*(t - t1))/cosh(sm*(t - t2))))+(eld.amp/2)
end

function (cons::ConstDef)(t)
    cons.amp
end

function (eld::EldRampReturnDef)(tt)
    fr = eld.K/(pi*abs(eld.amp));
    t1 = 1.
    t2 = t1 + (1./(2*pi*fr));
    t3 = t2 + ((1/(4*fr)) - (1/(2*pi*fr)));
    t4 = t3 + (1./(2*pi*fr));
    t5 = t4+1.;

    nstep = round(Int,t5/0.015) + 1
    g = zeros(nstep)
    t = zeros(nstep)

    for i = 1:nstep
        t[i] = (i-1.)*0.015
        g[i] = log((cosh(eld.a*(t[i] - t1))*cosh(eld.a*(t[i] - t4)))/(cosh(eld.a*(t[i] - t2))*cosh(eld.a*(t[i] - t3))))
    end
    maxg = maximum(g);

    gg = log((cosh(eld.a*(tt - t1))*cosh(eld.a*(tt - t4)))/(cosh(eld.a*(tt - t2))*cosh(eld.a*(tt - t3))))

    return eld.amp*gg/(maxg);

end

immutable SinDef <: MotionDef
  mean :: Float64
  amp :: Float64
  k :: Float64
  phi :: Float64
end

immutable CosDef <: MotionDef
  mean :: Float64
  amp :: Float64
  k :: Float64
  phi :: Float64
end

function (kin::SinDef)(t)
  (kin.mean) + (kin.amp)*sin(2*kin.k*t + kin.phi)
end

function (kin::CosDef)(t)
  (kin.mean) + (kin.amp)*cos(2*kin.k*t + kin.phi)
end
# ---------------------------------------------------------------------------------------------

# Added by Laura Merchant 2016
type KinemDefwFlap
    alpha :: MotionDef
    h :: MotionDef
    u :: MotionDef
    n :: MotionDef
end

type KinemDef
    alpha :: MotionDef
    h :: MotionDef
    u :: MotionDef
end

type KinemDef3D
    alpha :: MotionDef
    h :: MotionDef
    u :: MotionDef
    vartype :: String
    vary ::Int8
    add :: Array{Float64,2}

    function KinemDef3D(alpha :: MotionDef, h::MotionDef, u::MotionDef, vartype = "Constant", vary = 0, add = zeros(1,1))
        new(alpha, h, u, vartype, vary, add)
    end
end

# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
#Definition of a 2D vortex blob
type TwoDVort
    x :: Float64
    z :: Float64
    s :: Float64
    vc :: Float64
    vx :: Float64
    vz :: Float64
end
# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
#Definition of a 3D vortex blob
type ThreeDVort
    x :: Vector{Float64}
    s :: Vector{Float64}
    vc :: Float64
    v :: Vector{Float64}
    ds :: Vector{Float64}
end
# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
#Definition of flowfield in 2D
immutable TwoDFlowField
    velX :: MotionDef
    velZ :: MotionDef
    u :: Vector{Float64}
    w :: Vector{Float64}
    tev :: Vector{TwoDVort}
    lev :: Vector{TwoDVort}
    extv :: Vector{TwoDVort}
    function TwoDFlowField(velX = ConstDef(0.), velZ = ConstDef(0.))
        u = [0;]
        w = [0;]
        tev = TwoDVort[]
        lev = TwoDVort[]
        extv = TwoDVort[]
        new(velX, velZ, u, w, tev, lev, extv)
    end
end

#Definition of flowfield in 3D
immutable ThreeDFieldSimple
    f2d :: Vector{TwoDFlowField}
    function ThreeDFieldSimple()
        f2d = TwoDFlowField[]
    new(f2d)
    end
end

# immutable ThreeDFlowField
#     velX :: MotionDef
#     velY :: MotionDef
#     velZ :: MotionDef
#     u :: Vector{Float64}
#     v :: Vector{Float64}
#     w :: Vector{Float64}
#     tev :: Vector{ThreeDVort}
#     lev :: Vector{ThreeDVort}
#     extv :: Vector{ThreeDVort}
#     function ThreeDFlowField(velX = ConstDef(0.), velY = ConstDef(0.), velZ = ConstDef(0.))
#         u = [0;]
#         v = [0;]
#         w = [0;]
#         tev = ThreeDVort[]
#         lev = ThreeDVort[]
#         extv = ThreeDVort[]
#         new(velX, velY, velZ, u, v, w, tev, lev, extv)
#     end
# end

immutable TwoDFlowData
    tev :: Vector{TwoDVort}
    lev :: Vector{TwoDVort}
    extv :: Vector{TwoDVort}
    bv :: Vector{TwoDVort}
end

# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
# BEGIN TwoDSurfwFlap
# Added by Laura Merchant 2016
immutable TwoDSurfwFlap
    c :: Float64
    uref :: Float64
    coord_file :: String
    pvt :: Float64
    ndiv :: Int8
    naterm :: Int8
    dynamics_type :: String
    kindef :: KinemDefwFlap
    cam_af :: Vector{Float64}
    cam :: Vector{Float64}
    cam_slope :: Vector{Float64}
    cam_tder :: Vector{Float64}
    theta :: Vector{Float64}
    x :: Vector{Float64}
    x_b :: Vector{Float64}
    kinem :: KinemParwFlap
    bnd_x :: Vector{Float64}
    bnd_z :: Vector{Float64}
    uind :: Vector{Float64}
    wind :: Vector{Float64}
    downwash :: Vector{Float64}
    a0 :: Vector{Float64}
    aterm :: Vector{Float64}
    a0dot :: Vector{Float64}
    adot :: Vector{Float64}
    a0prev :: Vector{Float64}
    aprev :: Vector{Float64}
    bv :: Vector{TwoDVort}
    bv_prev :: Vector{TwoDVort}
    lespcrit :: Vector{Float64}
    levflag :: Vector{Int8}

    function TwoDSurfwFlap(c, uref, coord_file, pvt, ndiv, naterm, dynamics_type, kindef, x_b=1., lespcrit=zeros(1))
        x_b[1] = x_b[1]*c #Dimensional value
        theta = zeros(ndiv)
        x = zeros(ndiv)
        cam = zeros(ndiv)
        cam_af = zeros(ndiv)
        cam_slope = zeros(ndiv)
        cam_tder = zeros(ndiv)
        bnd_x = zeros(ndiv)
        bnd_z = zeros(ndiv)
        kinem = KinemParwFlap(0, 0, 0, 0, 0, 0, 0, 0)

        dtheta = pi/(ndiv-1)
        for ib = 1:ndiv
            theta[ib] = real(ib-1.)*dtheta
            x[ib] = c/2.*(1-cos(theta[ib]))
        end
        if (coord_file != "FlatPlate")
            cam, cam_slope = camber_calc(x, coord_file)
        end

        # Calculates the initial condition for airfoil pitch
        # Various options for prescribing the airfoil pitch kinematics
        if (typeof(kindef.alpha) == EldUpDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == EldUptstartDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == EldRampReturnDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == ConstDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = 0.
        elseif (typeof(kindef.alpha) == SinDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == CosDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        end
        # ---------------------------------------------------------------------------------------------

        # Calculates the initial condition for airfoil plunge
        # Various options for prescribing the airfoil plunge kinematics
        if (typeof(kindef.h) == EldUpDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == EldUptstartDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == EldUpIntDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == EldUpInttstartDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == EldRampReturnDef)
            kinem.h= kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == ConstDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = 0.
        elseif (typeof(kindef.h) == SinDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == CosDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        end
        # ---------------------------------------------------------------------------------------------

        # Calculates the initial condition for forward velocity of airfoil
        # Various options for prescribing the airfoil forward motion
        if (typeof(kindef.u) == EldUpDef)
            kinem.u = kindef.u(0.)*uref
            kinem.udot = ForwardDiff.derivative(kindef.u,0.)*uref*uref/c
        elseif (typeof(kindef.u) == EldRampReturnDef)
            kinem.u, kinem.udot = kindef.u(0.)
            kinem.u = kinem.u*uref
            kinem.udot = kinem.udot*uref*uref/c
        elseif (typeof(kindef.u) == ConstDef)
            kinem.u = kindef.u(0.)*uref
            kinem.udot = 0.
        end
        # ---------------------------------------------------------------------------------------------

        # ---------------------------------------------------------------------------------------------
        # Calculates the initial condition for flap deflection beta (n)
        # Various options for prescribing the flap kinematics
        if (typeof(kindef.n) == EldUpDef)
            kinem.n = kindef.n(0.)
            kinem.ndot = ForwardDiff.derivative(kindef.n,0.)*uref/c
        elseif (typeof(kindef.n) == EldRampReturnDef)
            kinem.n = kindef.n(0.)
            kinem.n = ForwardDiff.derivative(kindef.n,0.)*uref/c
        elseif (typeof(kindef.n) == ConstDef)
            kinem.n = kindef.n(0.)
            kinem.n = 0.
        elseif (typeof(kindef.n) == SinDef)
            kinem.n = kindef.n(0.)
            kinem.ndot = ForwardDiff.derivative(kindef.n,0.)*uref/c
        elseif (typeof(kindef.n) == CosDef)
            kinem.n = kindef.n(0.)
            kinem.ndot = ForwardDiff.derivative(kindef.n,0.)*uref/c
        end
# ---------------------------------------------------------------------------------------------

# Defines the aerofoil as a single flat plate
# Calls the calculation of the camber and it's derivatives (spatial and temporal)
#Deflection of flap is modelled as camber variation
if (coord_file != "FlatPlate")
    (cam_af, cam_slope) = camber_calc(x, coord_file)
end

for i = 1:ndiv
    if x[i] < x_b[1]
        cam[i] = cam_af[i];
    else
        cam[i] = cam_af[i] + (x_b[1] - x[i])*tan(kinem.n);
        cam_tder[i] = (x_b[1] - x[i])*kinem.ndot*sec(kinem.n)*sec(kinem.n);
    end
end
# ---------------------------------------------------------------------------------------------

# Populates the arrays for bnd_x, bnd_z based on initial conditions
for i = 1:ndiv
    bnd_x[i] = -((c - pvt*c)+((pvt*c - x[i])*cos(kinem.alpha))) + (cam[i]*sin(kinem.alpha))
    bnd_z[i] = kinem.h + ((pvt*c - x[i])*sin(kinem.alpha))+(cam[i]*cos(kinem.alpha))
end
# ---------------------------------------------------------------------------------------------

# Defines the arrays for various parameters such that they can be later populated
uind = zeros(ndiv)
wind = zeros(ndiv)
downwash = zeros(ndiv)
a0 = zeros(1)
a0dot = zeros(1)
aterm = zeros(naterm)
adot = zeros(3)
a0prev = zeros(1)
aprev = zeros(3)
bv = TwoDVort[]
bv_prev = TwoDVort[]
for i = 1:ndiv-1
    push!(bv,TwoDVort(0,0,0,0.02*c,0,0))
    push!(bv_prev,TwoDVort(0,0,0,0.02*c,0,0))
end
# ---------------------------------------------------------------------------------------------
levflag = [0]

new(c, uref, coord_file, pvt, ndiv, naterm, dynamics_type, kindef, cam_af, cam, cam_slope, cam_tder, theta, x, x_b, kinem, bnd_x, bnd_z, uind, wind, downwash, a0, aterm, a0dot, adot, a0prev, aprev, bv, bv_prev, lespcrit,levflag)
end
end
# ---------------------------------------------------------------------------------------------
# END TwoDSurfwFlap

immutable TwoDSurf
    c :: Float64
    uref :: Float64
    coord_file :: String
    pvt :: Float64
    ndiv :: Int8
    naterm :: Int8
    kindef :: KinemDef
    cam :: Vector{Float64}
    cam_slope :: Vector{Float64}
    theta :: Vector{Float64}
    x :: Vector{Float64}
    kinem :: KinemPar
    bnd_x :: Vector{Float64}
    bnd_z :: Vector{Float64}
    uind :: Vector{Float64}
    wind :: Vector{Float64}
    downwash :: Vector{Float64}
    a0 :: Vector{Float64}
    aterm :: Vector{Float64}
    a0dot :: Vector{Float64}
    adot :: Vector{Float64}
    a0prev :: Vector{Float64}
    aprev :: Vector{Float64}
    bv :: Vector{TwoDVort}
    lespcrit :: Vector{Float64}
    levflag :: Vector{Int8}

    function TwoDSurf(coord_file, pvt, kindef,lespcrit=zeros(1), c=1., uref=1., ndiv=70, naterm=35)
        theta = zeros(ndiv)
        x = zeros(ndiv)
        cam = zeros(ndiv)
        cam_slope = zeros(ndiv)
        bnd_x = zeros(ndiv)
        bnd_z = zeros(ndiv)
        kinem = KinemPar(0, 0, 0, 0, 0, 0)

        dtheta = pi/(ndiv-1)
        for ib = 1:ndiv
            theta[ib] = real(ib-1.)*dtheta
            x[ib] = c/2.*(1-cos(theta[ib]))
        end
        if (coord_file != "FlatPlate")
            cam, cam_slope = camber_calc(x, coord_file)
        end

        if (typeof(kindef.alpha) == EldUpDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == EldRampReturnDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == ConstDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = 0.
        elseif (typeof(kindef.alpha) == SinDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        elseif (typeof(kindef.alpha) == CosDef)
            kinem.alpha = kindef.alpha(0.)
            kinem.alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/c
        end

        if (typeof(kindef.h) == EldUpDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
          elseif (typeof(kindef.h) == EldUpIntDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == EldRampReturnDef)

            kinem.h= kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == ConstDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = 0.
        elseif (typeof(kindef.h) == SinDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        elseif (typeof(kindef.h) == CosDef)
            kinem.h = kindef.h(0.)*c
            kinem.hdot = ForwardDiff.derivative(kindef.h,0.)*uref
        end

        if (typeof(kindef.u) == EldUpDef)
            kinem.u = kindef.u(0.)*uref
            kinem.udot = ForwardDiff.derivative(kindef.u,0.)*uref*uref/c
        elseif (typeof(kindef.u) == EldRampReturnDef)
            kinem.u, kinem.udot = kindef.u(0.)
            kinem.u = kinem.u*uref
            kinem.udot = kinem.udot*uref*uref/c
        elseif (typeof(kindef.u) == ConstDef)
            kinem.u = kindef.u(0.)*uref
            kinem.udot = 0.
        end

        for i = 1:ndiv
            bnd_x[i] = -((c - pvt*c)+((pvt*c - x[i])*cos(kinem.alpha))) + (cam[i]*sin(kinem.alpha))
            bnd_z[i] = kinem.h + ((pvt*c - x[i])*sin(kinem.alpha))+(cam[i]*cos(kinem.alpha))
        end
        uind = zeros(ndiv)
        wind = zeros(ndiv)
        downwash = zeros(ndiv)
        a0 = zeros(1)
        a0dot = zeros(1)
        aterm = zeros(naterm)
        adot = zeros(3)
        a0prev = zeros(1)
        aprev = zeros(3)
        bv = TwoDVort[]
        for i = 1:ndiv-1
            push!(bv,TwoDVort(0,0,0,0.02*c,0,0))
        end
        levflag = [0]
        new(c, uref, coord_file, pvt, ndiv, naterm, kindef, cam, cam_slope, theta, x, kinem, bnd_x, bnd_z, uind, wind, downwash, a0, aterm, a0dot, adot, a0prev, aprev, bv,lespcrit,levflag)
    end
end

immutable TwoDSurf_2DOF
    c :: Float64
    uref :: Float64
    coord_file :: String
    pvt :: Float64
    ndiv :: Int8
    naterm :: Int8
    cam :: Vector{Float64}
    cam_slope :: Vector{Float64}
    theta :: Vector{Float64}
    x :: Vector{Float64}
    strpar :: TwoDOFPar
    kinem :: KinemPar2DOF
    bnd_x :: Vector{Float64}
    bnd_z :: Vector{Float64}
    uind :: Vector{Float64}
    wind :: Vector{Float64}
    downwash :: Vector{Float64}
    a0 :: Vector{Float64}
    aterm :: Vector{Float64}
    a0dot :: Vector{Float64}
    adot :: Vector{Float64}
    a0prev :: Vector{Float64}
    aprev :: Vector{Float64}
    bv :: Vector{TwoDVort}
    lespcrit :: Vector{Float64}
    levflag :: Vector{Int8}

    function TwoDSurf_2DOF(c, uref, coord_file, pvt, ndiv, naterm, strpar, kinem ,lespcrit=zeros(1))
        theta = zeros(ndiv)
        x = zeros(ndiv)
        cam = zeros(ndiv)
        cam_slope = zeros(ndiv)
        bnd_x = zeros(ndiv)
        bnd_z = zeros(ndiv)

        dtheta = pi/(ndiv-1)
        for ib = 1:ndiv
            theta[ib] = real(ib-1.)*dtheta
            x[ib] = c/2.*(1-cos(theta[ib]))
        end
        if (coord_file != "FlatPlate")
            cam, cam_slope = camber_calc(x, coord_file)
        end

        for i = 1:ndiv
            bnd_x[i] = -((c - pvt*c)+((pvt*c - x[i])*cos(kinem.alpha))) + (cam[i]*sin(kinem.alpha))
            bnd_z[i] = kinem.h + ((pvt*c - x[i])*sin(kinem.alpha))+(cam[i]*cos(kinem.alpha))
        end
        uind = zeros(ndiv)
        wind = zeros(ndiv)
        downwash = zeros(ndiv)
        a0 = zeros(1)
        a0dot = zeros(1)
        aterm = zeros(naterm)
        adot = zeros(3)
        a0prev = zeros(1)
        aprev = zeros(3)
        bv = TwoDVort[]
        for i = 1:ndiv-1
            push!(bv,TwoDVort(0,0,0,0.02*c,0,0))
        end
        levflag = [0]
        new(c, uref, coord_file, pvt, ndiv, naterm, cam, cam_slope, theta, x, strpar, kinem, bnd_x, bnd_z, uind, wind, downwash, a0, aterm, a0dot, adot, a0prev, aprev, bv,lespcrit,levflag)
    end
end

immutable TwoDFreeSurf
    c :: Float64
    uref :: Float64
    coord_file :: String
    pvt :: Float64
    ndiv :: Int8
    naterm :: Int8
    cam :: Vector{Float64}
    cam_slope :: Vector{Float64}
    theta :: Vector{Float64}
    x :: Vector{Float64}
    strpar :: TwoDFreePar
    kinem :: KinemPar2DFree
    bnd_x :: Vector{Float64}
    bnd_z :: Vector{Float64}
    uind :: Vector{Float64}
    wind :: Vector{Float64}
    downwash :: Vector{Float64}
    a0 :: Vector{Float64}
    aterm :: Vector{Float64}
    a0dot :: Vector{Float64}
    adot :: Vector{Float64}
    a0prev :: Vector{Float64}
    aprev :: Vector{Float64}
    bv :: Vector{TwoDVort}
    lespcrit :: Vector{Float64}
    levflag :: Vector{Int8}

    function TwoDFreeSurf(c, uref, coord_file, pvt, ndiv, naterm, strpar, kinem ,lespcrit=zeros(1))
        theta = zeros(ndiv)
        x = zeros(ndiv)
        cam = zeros(ndiv)
        cam_slope = zeros(ndiv)
        bnd_x = zeros(ndiv)
        bnd_z = zeros(ndiv)

        dtheta = pi/(ndiv-1)
        for ib = 1:ndiv
            theta[ib] = real(ib-1.)*dtheta
            x[ib] = c/2.*(1-cos(theta[ib]))
        end
        if (coord_file != "FlatPlate")
            cam, cam_slope = camber_calc(x, coord_file)
        end

        for i = 1:ndiv
            bnd_x[i] = -((c - pvt*c)+((pvt*c - x[i])*cos(kinem.alpha))) + (cam[i]*sin(kinem.alpha))
            bnd_z[i] = kinem.h + ((pvt*c - x[i])*sin(kinem.alpha))+(cam[i]*cos(kinem.alpha))
        end
        uind = zeros(ndiv)
        wind = zeros(ndiv)
        downwash = zeros(ndiv)
        a0 = zeros(1)
        a0dot = zeros(1)
        aterm = zeros(naterm)
        adot = zeros(3)
        a0prev = zeros(1)
        aprev = zeros(3)
        bv = TwoDVort[]
        for i = 1:ndiv-1
            push!(bv,TwoDVort(0,0,0,0.02*c,0,0))
        end
        levflag = [0]
        new(c, uref, coord_file, pvt, ndiv, naterm, cam, cam_slope, theta, x, strpar, kinem, bnd_x, bnd_z, uind, wind, downwash, a0, aterm, a0dot, adot, a0prev, aprev, bv,lespcrit,levflag)
    end
end

#----------------------------------------------------------------------------------------------
# Definition of a 3D surface
immutable patch
    x :: Float64
    y :: Float64
    z :: Float64
    pvt :: Float64
    coord_file :: String
    c :: Float64
    twist :: Float64
    lc :: Float64
    nspan :: Int8
end

# immutable ThreeDSurf
#     cref :: Float64
#     bref :: Float64
#     sref :: Float64
#     uref :: Float64
#     patchdata :: Vector{patch}
#     ndiv :: Int8
#     nspan :: Int8
#     naterm :: Int8
#     kindef :: KinemDef3D
#     c :: Vector{Float64}
#     pvt :: Vector{Float64}
#     cam :: Array{Float64,2}
#     cam_slope :: Array{Float64,2}
#     theta :: Vector{Float64}
#     psi :: Vector{Float64}
#     x :: Array{Float64,2}
#     xle :: Vector{Float64}
#     yle :: Vector{Float64}
#     zle :: Vector{Float64}
#     kinem :: Vector{KinemPar}
#     bnd_x :: Array{Float64,2}
#     bnd_z :: Array{Float64,2}
#     uind :: Array{Float64,2}
#     vind :: Array{Float64,2}
#     wind :: Array{Float64,2}
#     downwash :: Array{Float64,2}
#     a0 :: Vector{Float64}
#     aterm :: Array{Float64,2}
#     a0dot :: Vector{Float64}
#     adot :: Array{Float64,2}
#     a0prev :: Vector{Float64}
#     aprev :: Array{Float64,2}
#     bv :: Array{ThreeDVort,2}
#     lespcrit :: Vector{Float64}
#     levflag :: Vector{Int8}

#     function ThreeDSurf(cref, bref, sref, patchdata, kindef, uref=1., ndiv=70, naterm=35)
#         nspan = 0
#         for i = 1:length(patchdata)-1
#             nspan += patchdata[i].nspan
#         end

#         lespcrit = zeros(nspan)

#         psi = zeros(nspan)
#         cam = zeros(nspan,ndiv)
#         cam_slope = zeros(nspan,ndiv)
#         cam1 = zeros(ndiv)
#         cam_slope1 = zeros(ndiv)
#         cam2 = zeros(ndiv)
#         cam_slope2 = zeros(ndiv)
#         bnd_x = zeros(nspan,ndiv)
#         bnd_z = zeros(nspan,ndiv)
#         c = zeros(nspan)
#         pvt = zeros(nspan)
# 	yle = zeros(nspan)
# 	xle = zeros(nspan)
# 	zle = zeros(nspan)
#        	twist = zeros(nspan)

#         kinem = KinemPar[]

# 	theta = zeros(ndiv)
#         x = zeros(nspan,ndiv)

#         dtheta = pi/(ndiv-1)
#         for ib = 1:ndiv
#             theta[ib] = real(ib-1.)*dtheta
# 	    for is = 1:nspan
#   	        x[is,ib] = c[is]/2.*(1-cos(theta[ib]))
# 	    end
# 	end

# 	nspan = 0
#         for i = 1:length(patchdata) - 1
#             if i != 1
#                 nspan += 1
#                 psi[nspan] = (patchdata[i].y - patchdata[1].y)*pi/(patchdata[length(patchdata)].y - patchdata[1].y)
#                 yle[nspan] = patchdata[i].y
#                 xle[nspan] = patchdata[i].x
#                 zle[nspan] = patchdata[i].z
#                 c[nspan] = patchdata[i].c
#                 twist[nspan] = patchdata[i].twist
#                 if (patchdata[i].coord_file != "FlatPlate")
#                     cam[nspan,:], cam_slope[nspan,:] = camber_calc(0.5*(1-cos(theta)), patchdata[i].coord_file)
#                 end
#                 lespcrit[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].lc, patchdata[i+1].lc, yle[nspan])
#                 pvt[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].pvt, patchdata[i+1].pvt, yle[nspan])
#             end
#             startpsi = (patchdata[i].y - patchdata[1].y)*pi/(patchdata[length(patchdata)].y - patchdata[1].y)
#             lenpsi = (patchdata[i+1].y - patchdata[i].y)*pi/(patchdata[length(patchdata)].y - patchdata[1].y)
#             dpsi =  lenpsi/(patchdata[i].nspan + 1.)
# #            divspan = (patchdata[i+1].y - patchdata[i].y)/patchdata[i].nspan
#             for j = 1:patchdata[i].nspan
#                 nspan += 1
#                 psi[nspan] = startpsi + real(j)*dpsi
#                 #                yle[nspan] = patchdata[i].y + (i-1)*divspan
#                 yle[nspan] = -bref*cos(psi[nspan])/2.
#                 xle[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].x, patchdata[i+1].x, yle[nspan])
#                 zle[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].z, patchdata[i+1].z, yle[nspan])
#                 c[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].c, patchdata[i+1].c, yle[nspan])
#                 twist[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].twist, patchdata[i+1].twist, yle[nspan])
#                 if (patchdata[i].coord_file != "FlatPlate")
#                     cam1, cam_slope1 = camber_calc(0.5*(1-cos(theta)), patchdata[i].coord_file)
#                 end
#                 if (patchdata[i+1].coord_file != "FlatPlate")
#                     cam2, cam_slope2 = camber_calc(0.5*(1-cos(theta)), patchdata[i+1].coord_file)
#                 end
#                 for k = 1:ndiv
#                     cam[nspan,k] = interp(patchdata[i].y, patchdata[i+1].y, cam1[k], cam2[k], yle[nspan])*c[nspan]
#                     cam_slope[nspan,k] = interp(patchdata[i].y, patchdata[i+1].y, cam_slope1[k], cam_slope2[k], yle[nspan])*c[nspan]
#                 end
#                 lespcrit[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].lc, patchdata[i+1].lc, yle[nspan])
#                 pvt[nspan] = interp(patchdata[i].y, patchdata[i+1].y, patchdata[i].pvt, patchdata[i+1].pvt, yle[nspan])
#             end
#         end

#         inkim = KinemPar(0, 0, 0, 0, 0, 0)
#         for i = 1:nspan
#             push!(kinem, inkim)
#         end

#         if (kindef.vartype == "Constant")
#             i = 1
#             if (typeof(kindef.alpha) == EldUpDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             elseif (typeof(kindef.alpha) == EldRampReturnDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             elseif (typeof(kindef.alpha) == ConstDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = 0.
#             elseif (typeof(kindef.alpha) == SinDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             elseif (typeof(kindef.alpha) == CosDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             end

#             if (typeof(kindef.h) == EldUpDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == EldUpIntDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == EldRampReturnDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == ConstDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = 0.
#             elseif (typeof(kindef.h) == SinDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == CosDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             end

#             if (typeof(kindef.u) == EldUpDef)
#                 kinem[i].u = kindef.u(0.)*uref
#                 kinem[i].udot = ForwardDiff.derivative(kindef.u,0.)*uref*uref/cref
#             elseif (typeof(kindef.u) == EldRampReturnDef)
#                 kinem[i].u, kinem[i].udot = kindef.u(0.)
#                 kinem[i].u = kinem[i].u*uref
#                 kinem[i].udot = kinem[i].udot*uref*uref/cref
#             elseif (typeof(kindef.u) == ConstDef)
#                 kinem[i].u = kindef.u(0.)*uref
#                 kinem[i].udot = 0.
#             end

#             for i = 2:nspan
#                 kinem[i] = kinem[1]
#             end
#         elseif (typeof(kindef.vartype) == "Read")
#             # Provide the max amplitude as the input, and the read array goes from 0 to 1 as a function of yle
#             i = nspan
#             if (typeof(kindef.alpha) == EldUpDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             elseif (typeof(kindef.alpha) == EldRampReturnDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             elseif (typeof(kindef.alpha) == ConstDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = 0.
#             elseif (typeof(kindef.alpha) == SinDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             elseif (typeof(kindef.alpha) == CosDef)
#                 kinem[i].alpha = kindef.alpha(0.)
#                 kinem[i].alphadot = ForwardDiff.derivative(kindef.alpha,0.)*uref/cref
#             end

#             if (typeof(kindef.h) == EldUpDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == EldUpIntDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == EldRampReturnDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == ConstDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = 0.
#             elseif (typeof(kindef.h) == SinDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             elseif (typeof(kindef.h) == CosDef)
#                 kinem[i].h = kindef.h(0.)*cref
#                 kinem[i].hdot = ForwardDiff.derivative(kindef.h,0.)*uref
#             end

#             if (typeof(kindef.u) == EldUpDef)
#                 kinem[i].u = kindef.u(0.)*uref
#                 kinem[i].udot = ForwardDiff.derivative(kindef.u,0.)*uref*uref/cref
#             elseif (typeof(kindef.u) == EldRampReturnDef)
#                 kinem[i].u, kinem[i].udot = kindef.u(0.)
#                 kinem[i].u = kinem[i].u*uref
#                 kinem[i].udot = kinem[i].udot*uref*uref/cref
#             elseif (typeof(kindef.u) == ConstDef)
#                 kinem[i].u = kindef.u(0.)*uref
#                 kinem[i].udot = 0.
#             end

# addspl = Spline1D(kindef.add[:,1], kindef.add[:,2])

# if (kindef.vary == 1)

#     for i = 1:nspan
#         kinem.h[i] = kinem.h[nspan]*evaluate(addspl,yle[i])
#     end
# elseif (kindef.vary == 2)
#     addspl = Spline1D(kinem.add[:,1], kinem.add[:,2])
#     for i = 1:nspan
#         kinem.alpha[i] = kinem.alpha[nspan]*evaluate(addspl,yle[i])
#     end
# end

# end

# for j = 1:nspan
#     for i = 1:ndiv
#         bnd_x[j,i] = -((c[j] - pvt[j]*c[j])+((pvt[j]*c[j] - x[j,i])*cos(kinem[j].alpha))) + (cam[j,i]*sin(kinem[j].alpha))
#         bnd_z[j,i] = kinem[j].h + ((pvt[j]*c[j] - x[j,i])*sin(kinem[j].alpha))+(cam[j,i]*cos(kinem[j].alpha))
#     end
# end

# uind = zeros(nspan,ndiv)
# vind = zeros(nspan,ndiv)
# wind = zeros(nspan,ndiv)
# downwash = zeros(nspan,ndiv)
# a0 = zeros(nspan)
# a0dot = zeros(nspan)
# aterm = zeros(nspan,naterm)
# adot = zeros(nspan,3)
# a0prev = zeros(nspan)
# aprev = zeros(nspan,3)
# bv = Array(ThreeDVort,nspan,ndiv-1)

# for j = 1:nspan
#     for i = 1:ndiv-1
#         bv[j,i] = ThreeDVort([0; 0; 0;], [0; 0; 0;], 0.02*cref, [0; 0; 0;], [0; 0; 0;])
#     end
# end
# levflag = zeros(nspan)

# new(cref, bref, sref, uref, patchdata, ndiv, nspan, naterm, nbterm, kindef, c, pvt, cam, cam_slope, theta, psi, x, xle, yle, zle, kinem, bnd_x, bnd_z, uind, vind, wind, downwash, a0, aterm, a0dot, adot, a0prev, aprev, bv,lespcrit,levflag)
# end
# end

immutable ThreeDSurfSimple
    cref :: Float64
    AR :: Float64
    uref :: Float64
    pvt :: Float64
    lespcrit :: Vector{Float64}
    coord_file :: String
    ndiv :: Int8
    nspan :: Int8
    naterm :: Int8
    kindef :: KinemDef
    psi :: Vector{Float64}
    yle :: Vector{Float64}
    s2d :: Vector{TwoDSurf}
    a03d :: Vector{Float64}
    bc :: Vector{Float64}
    nshed :: Vector{Float64}

    function ThreeDSurfSimple(AR, kindef, coord_file, pvt, lespcrit = [10.;], nspan = 20, cref = 1., uref=1., ndiv=70, naterm=35)

        bref = AR*cref

        psi = zeros(nspan)
        yle = zeros(nspan)

        s2d = TwoDSurf[]

	for i = 1:nspan
            psi[i] = real(i)*(pi/2)/nspan
            yle[i] = -bref*cos(psi[i])/2.
        end

        for i = 1:nspan
            push!(s2d, TwoDSurf(coord_file, pvt,  kindef, lespcrit, cref, uref, ndiv, naterm))
        end

        a03d = zeros(nspan)
        bc = zeros(nspan)
        nshed = [0.;]

new(cref, AR, uref, pvt, lespcrit, coord_file,  ndiv, nspan, naterm, kindef, psi, yle, s2d, a03d, bc, nshed)
end
end

# ---------------------------------------------------------------------------------------------
# BEGIN EldUpIntDef
immutable EldUpIntDef <: MotionDef
    amp :: Float64
    K :: Float64
    a :: Float64
end

immutable EldUpInttstartDef <: MotionDef
    amp :: Float64
    K :: Float64
    a :: Float64
    tstart :: Float64
end

function (eld::EldUpIntDef)(t)

    dt = 0.015
    nsteps = t/dt + 1
    nsteps = round(Int,nsteps)
    dt = t/(nsteps-1)
    sm = pi*pi*eld.K/(2*eld.amp*(1 - eld.a))
    t1 = 1.
    t2 = t1 + ((eld.amp)/(2*eld.K))

    prev_h = 0
    amp = 0
    for i = 1:nsteps
      tmpt = (i-1)*dt
      if (eld.amp == 0.)
      	 hdot = 0.
      else
         hdot = ((eld.K/sm)*log(cosh(sm*(tmpt - t1))/cosh(sm*(tmpt - t2))))+(eld.amp/2.)
      end
      amp = prev_h + hdot*dt
      prev_h = amp
    end
    if (nsteps == 1)
      amp = 0.
    end
    amp
end

function (eld::EldUpInttstartDef)(t)

    dt = 0.015
    nsteps = t/dt + 1
    nsteps = round(Int,nsteps)
    dt = t/(nsteps-1)
    sm = pi*pi*eld.K/(2*eld.amp*(1 - eld.a))
    t1 = eld.tstart
    t2 = t1 + ((eld.amp)/(2*eld.K))

    prev_h = 0
    amp = 0
    for i = 1:nsteps
      tmpt = (i-1)*dt
      if (eld.amp == 0.)
      	 hdot = 0.
      else
         hdot = ((eld.K/sm)*log(cosh(sm*(tmpt - t1))/cosh(sm*(tmpt - t2))))+(eld.amp/2.)
      end
      amp = prev_h + hdot*dt
      prev_h = amp
    end
    if (nsteps == 1)
      amp = 0.
    end
    amp
end

# END EldUpIntDef
# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
# BEGIN KelvinCondition
immutable KelvinCondition
    surf :: TwoDSurf
    field :: TwoDFlowField
end

immutable KelvinConditionwFlap
    surf :: TwoDSurfwFlap
    field :: TwoDFlowField
end

immutable KelvinCondition2DOF
    surf :: TwoDSurf_2DOF
    field :: TwoDFlowField
    kelv_enf :: Float64
end

immutable KelvinCondition2DFree
    surf :: TwoDFreeSurf
    field :: TwoDFlowField
    kelv_enf :: Float64
end

immutable KelvinConditionLLTldvm
    surf :: ThreeDSurfSimple
    field :: ThreeDFieldSimple
end

function (kelv::KelvinCondition)(tev_iter::Array{Float64})
    #Update the TEV strength
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = tev_iter[1]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf, [kelv.field.u[1],kelv.field.w[1]])

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val = val + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val = val + kelv.field.lev[iv].s
    end

    #Add kelv_enforced if necessary - merging will be better
    # val is the value of Gam_b + sum Gam_tev + Gam_lev which will equal zero
    # if the condition is satified

    return val
end

function (kelv::KelvinCondition2DOF)(tev_iter::Array{Float64})
    #Update the TEV strength
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = tev_iter[1]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf)

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val = val + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val = val + kelv.field.lev[iv].s
    end
    val = val + kelv.kelv_enf

    #Add kelv_enforced if necessary - merging will be better
    # val is the value of Gam_b + sum Gam_tev + Gam_lev which will equal zero
    # if the condition is satified

    return val
end

function (kelv::KelvinCondition2DFree)(tev_iter::Array{Float64})
    #Update the TEV strength
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = tev_iter[1]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf)

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val = val + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val = val + kelv.field.lev[iv].s
    end
    val = val + kelv.kelv_enf

    #Add kelv_enforced if necessary - merging will be better
    # val is the value of Gam_b + sum Gam_tev + Gam_lev which will equal zero
    # if the condition is satified

    return val
end

function (kelv::KelvinConditionwFlap)(tev_iter::Array{Float64})
    #Update the TEV strength
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = tev_iter[1]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf)

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val = val + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val = val + kelv.field.lev[iv].s
    end

    #Add kelv_enforced if necessary - merging will be better
    # val is the value of Gam_b + sum Gam_tev + Gam_lev which will equal zero
    # if the condition is satified

    return val
end

function (kelv::KelvinConditionLLTldvm)(tev_iter::Array{Float64})
    val = zeros(2*kelv.surf.nspan)
    lhs = zeros(kelv.surf.nspan,kelv.surf.nspan)
    rhs = zeros(kelv.surf.nspan)

    #Assume symmetry condition for now
    for i = 1:kelv.surf.nspan
        nlev = length(kelv.field.f2d[i].lev)
        ntev = length(kelv.field.f2d[i].tev)
        kelv.field.f2d[i].tev[ntev].s = tev_iter[i]

        #Update incduced velocities on airfoil
        update_indbound(kelv.surf.s2d[i], kelv.field.f2d[i])

        #Calculate downwash
        update_downwash(kelv.surf.s2d[i], [kelv.field.f2d[i].u[1], kelv.field.f2d[i].w[1]])

        #Calculate first two fourier coefficients
        update_a0anda1(kelv.surf.s2d[i])

        kelv.surf.bc[i] = kelv.surf.s2d[i].a0[1] + 0.5*kelv.surf.s2d[i].aterm[1]

        kelv.surf.a03d[i] = 0

        for n = 1:kelv.surf.nspan
            nn = 2*n - 1
            kelv.surf.a03d[i] = kelv.surf.a03d[i] - real(nn)*tev_iter[n+kelv.surf.nspan]*sin(nn*kelv.surf.psi[i])/sin(kelv.surf.psi[i])
        end

        val[i] = kelv.surf.s2d[i].uref*kelv.surf.s2d[i].c*pi*(kelv.surf.bc[i] + kelv.surf.a03d[i])

        for iv = 1:ntev
            val[i] = val[i] + kelv.field.f2d[i].tev[iv].s
        end
        for iv = 1:nlev
            val[i] = val[i] + kelv.field.f2d[i].lev[iv].s
        end
    end

    for i = 1:kelv.surf.nspan
        for n = 1:kelv.surf.nspan
            nn = 2*n - 1
            lhs[i,n] = sin(nn*kelv.surf.psi[i])*(sin(kelv.surf.psi[i]) + (nn*pi/(2*kelv.surf.AR)))
        end
        rhs[i] = pi*sin(kelv.surf.psi[i])*kelv.surf.bc[i]/(2*kelv.surf.AR)
    end

    val[kelv.surf.nspan+1:2*kelv.surf.nspan] = lhs*tev_iter[kelv.surf.nspan+1:2*kelv.surf.nspan] - rhs

    return val
end

# END KelvinCondition
# ---------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------
# BEGIN KelvinKutta
immutable KelvinKutta
    surf :: TwoDSurf
    field :: TwoDFlowField
end

immutable KelvinKutta2DOF
    surf :: TwoDSurf_2DOF
    field :: TwoDFlowField
    kelv_enf :: Float64
end

immutable KelvinKutta2DFree
    surf :: TwoDFreeSurf
    field :: TwoDFlowField
    kelv_enf :: Float64
end

immutable KelvinKuttawFlap
    surf :: TwoDSurfwFlap
    field :: TwoDFlowField
end

immutable KelvinKuttaLLTldvm
    surf :: ThreeDSurfSimple
    field :: Vector{TwoDFlowField}
 end

function (kelv::KelvinKutta)(v_iter::Array{Float64})
    val = zeros(2)

    #Update the TEV and LEV strengths
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = v_iter[1]
    kelv.field.lev[nlev].s = v_iter[2]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf ,[kelv.field.u[1],kelv.field.w[1]])

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val[1] = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val[1] = val[1] + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val[1] = val[1] + kelv.field.lev[iv].s
    end

    if (kelv.surf.a0[1] > 0)
        lesp_cond = kelv.surf.lespcrit[1]
    else
        lesp_cond = -kelv.surf.lespcrit[1]
    end
    val[2] = kelv.surf.a0[1]-lesp_cond

    #Add kelv_enforced if necessary - merging will be better
    return val
end

function (kelv::KelvinKutta2DOF)(v_iter::Array{Float64})
    val = zeros(2)

    #Update the TEV and LEV strengths
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = v_iter[1]
    kelv.field.lev[nlev].s = v_iter[2]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf)

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val[1] = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val[1] = val[1] + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val[1] = val[1] + kelv.field.lev[iv].s
    end
    val[1] = val[1] + kelv.kelv_enf

    if (kelv.surf.a0[1] > 0)
        lesp_cond = kelv.surf.lespcrit[1]
    else
        lesp_cond = -kelv.surf.lespcrit[1]
    end
    val[2] = kelv.surf.a0[1]-lesp_cond

    #Add kelv_enforced if necessary - merging will be better
    return val
end

function (kelv::KelvinKutta2DFree)(v_iter::Array{Float64})
    val = zeros(2)

    #Update the TEV and LEV strengths
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = v_iter[1]
    kelv.field.lev[nlev].s = v_iter[2]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf)

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val[1] = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val[1] = val[1] + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val[1] = val[1] + kelv.field.lev[iv].s
    end
    val[1] = val[1] + kelv.kelv_enf

    if (kelv.surf.a0[1] > 0)
        lesp_cond = kelv.surf.lespcrit[1]
    else
        lesp_cond = -kelv.surf.lespcrit[1]
    end
    val[2] = kelv.surf.a0[1]-lesp_cond

    #Add kelv_enforced if necessary - merging will be better
    return val
end

function (kelv::KelvinKuttawFlap)(v_iter::Array{Float64})
    val = zeros(2)

    #Update the TEV and LEV strengths
    nlev = length(kelv.field.lev)
    ntev = length(kelv.field.tev)
    kelv.field.tev[ntev].s = v_iter[1]
    kelv.field.lev[nlev].s = v_iter[2]

    #Update incduced velocities on airfoil
    update_indbound(kelv.surf, kelv.field)

    #Calculate downwash
    update_downwash(kelv.surf)

    #Calculate first two fourier coefficients
    update_a0anda1(kelv.surf)

    val[1] = kelv.surf.uref*kelv.surf.c*pi*(kelv.surf.a0[1] + kelv.surf.aterm[1]/2.)

    for iv = 1:ntev
        val[1] = val[1] + kelv.field.tev[iv].s
    end
    for iv = 1:nlev
        val[1] = val[1] + kelv.field.lev[iv].s
    end

    if (kelv.surf.a0[1] > 0)
        lesp_cond = kelv.surf.lespcrit[1]
    else
        lesp_cond = -kelv.surf.lespcrit[1]
    end
    val[2] = kelv.surf.a0[1]-lesp_cond

    #Add kelv_enforced if necessary - merging will be better
    return val
end

function (kelv::KelvinKuttaLLTldvm)(tev_iter::Array{Float64})
    val = zeros(2*kelv.surf3d.nspan + kelv.nshed)
    bc = zeros(kelv.surf3d.nspan)
    AR = kelv.surf3d.bref/kelv.surf3d.cref
    lhs = zeros(kelv.surf3d.nspan,kelv.surf3d.nspan)
    rhs = zeros(kelv.surf3d.nspan)
    bcoeff = zeros(kelv.surf3d.nspan)
    a03d = zeros(kelv.surf3d.nspan)

    cntr = kelv.surf3d.nspan + 1

    #Assume symmetry condition for now
    for i = 1:kelv.surf3d.nspan
        nlev = length(kelv.field[i].lev)
        ntev = length(kelv.field[i].tev)
        kelv.field[i].tev[ntev].s = tev_iter[i]
        if kelv.surf[i].levflag[1] == 1
            kelv.field[i].lev[nlev].s = tev_iter[cntr]
            cntr += 1
        end

        #Update incduced velocities on airfoil
        update_indbound(kelv.surf[i], kelv.field[i])

        #Calculate downwash
        update_downwash(kelv.surf[i], [kelv.field[i].u[1],kelv.field[i].w[1]])

        #Calculate first two fourier coefficients
        update_a0anda1(kelv.surf[i])

        bc[i] = kelv.surf[i].a0[1] + 0.5*kelv.surf[i].aterm[1]

        for i = 1:kelv.surf3d.nspan
        a03d[i] = 0
            for n = 1:kelv.surf3d.nspan
                nn = 2*n - 1
                a03d[i] = a03d[i] - real(nn)*tev_iter[n+kelv.surf3d.nspan+kelv.nshed]*sin(nn*kelv.surf3d.psi[i])/sin(kelv.surf3d.psi[i])
            end
        end

        val[i] = kelv.surf[i].uref*kelv.surf[i].c*pi*(bc[i] + a03d[i])

        for iv = 1:ntev
            val[i] = val[i] + kelv.field[i].tev[iv].s
        end
        for iv = 1:nlev
            val[i] = val[i] + kelv.field[i].lev[iv].s
        end

        if kelv.surf[i].levflag[1] == 1
            if kelv.surf[i].a0[1] > 0
                lesp_cond = kelv.surf[i].lespcrit[1]
            else
                lesp_cond = -kelv.surf[i].lespcrit[1]
            end
            val[cntr-1] = kelv.surf[i].a0[1] + a03d[i] - lesp_cond
        end
    end

    for i = 1:kelv.surf3d.nspan
        for n = 1:kelv.surf3d.nspan
            nn = 2*n - 1
            lhs[i,n] = sin(nn*kelv.surf3d.psi[i])*(sin(kelv.surf3d.psi[i]) + (nn*pi/(2*AR)))
        end
        rhs[i] = pi*sin(kelv.surf3d.psi[i])*bc[i]/(2*AR)
    end

    val[kelv.surf3d.nspan+kelv.nshed+1:2*kelv.surf3d.nspan+kelv.nshed] = lhs*tev_iter[kelv.surf3d.nspan+kelv.nshed+1:2*kelv.surf3d.nspan+kelv.nshed] - rhs

    return val
end

# END KelvinKutta
# ---------------------------------------------------------------------------------------------


"""
    Localization

A single point identified by SMLM.
"""
struct Localization <: DataEntity
    index::Int            # unique identifier within this dataset
    channel::String
    x::Float64
    y::Float64
    z::Float64
    accuracy::Float64     # computed lateral uncertainty based on detected photons
    frame::Int            # first frame observed
    length::Int           # number of adjacent frames observed over
end