using NullableArrays
using DataArrays

srand(1)
A = rand(5_000_000)
B = rand(Bool, 5_000_000)
mu_A = mean(A)
X = NullableArray(A)
Y = NullableArray(A, B)
D = DataArray(A)
E = DataArray(A, B)

f(x) = 5 * x
f{T<:Number}(x::Nullable{T}) =
    ifelse(isnull(x), Nullable{typeof(5 * x.value)}(), Nullable(5 * x.value))

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

function profile_reduce_methods()
    A = rand(5_000_000)
    B = rand(Bool, 5_000_000)
    X = NullableArray(A)
    Y = NullableArray(A, B)
    D = DataArray(A)
    E = DataArray(A, B)

    profile_mapreduce(A, X, Y, D, E)
    profile_reduce(A, X, Y, D, E)

    for method in (
        sum,
        prod,
        minimum,
        maximum,
    )
        (method)(A)
        (method)(X)
        (method)(D)
        println("Method: $method(A) (0 missing entries)")
        print("  for Array{Float64}:          ")
        @time((method)(A))
        print("  for NullableArray{Float64}:  ")
        @time((method)(X))
        print("  for DataArray{Float64}:      ")
        @time((method)(D))
        println()

        (method)(f, A)
        (method)(f, X)
        (method)(f, D)
        println("Method: $method(f, A) (0 missing entries)")
        print("  for Array{Float64}:          ")
        @time((method)(f, A))
        print("  for NullableArray{Float64}:  ")
        @time((method)(f, X))
        print("  for DataArray{Float64}:      ")
        @time((method)(f, D))
        println()
    end

    for method in (
        sum,
        prod,
        minimum,
        maximum,
    )
        (method)(Y)
        println("Method: $method(A) (~half missing entries, skip=false)")
        print("  for NullableArray{Float64}:  ")
        @time((method)(Y))
        (method)(E)
        print("  for DataArray{Float64}:      ")
        @time((method)(E))
        println()

        (method)(f, Y)
        println("Method: $method(f, A) (~half missing entries, skip=false)")
        print("  for NullableArray{Float64}:  ")
        @time((method)(f, Y))
        if in(method, (sum, prod))
            (method)(f, E)
            print("  for DataArray{Float64}:      ")
            @time((method)(f, E))
        else
            println("  $method(f, A::DataArray) currently incurs error")
        end
        println()
    end

    for method in (
        sum,
        prod,
        minimum,
        maximum,
    )
        (method)(Y, skipnull=true)
        println("Method: $method(A) (~half missing entries, skip=true)")
        print("  for NullableArray{Float64}:  ")
        @time((method)(Y, skipnull=true))
        (method)(E, skipna=true)
        print("  for DataArray{Float64}:      ")
        @time((method)(E, skipna=true))
        println()

        (method)(f, Y, skipnull=true)
        println("Method: $method(f, A) (~half missing entries, skip=true)")
        print("  for NullableArray{Float64}:  ")
        @time((method)(f, Y, skipnull=true))
        (method)(f, E, skipna=true)
        print("  for DataArray{Float64}:      ")
        @time((method)(f, E, skipna=true))
        println()
    end

    for method in (
        sumabs,
        sumabs2
    )
        (method)(A)
        (method)(X)
        (method)(D)
        println("Method: $method(A) (0 missing entries)")
        print("  for Array{Float64}:          ")
        @time((method)(A))
        print("  for NullableArray{Float64}:  ")
        @time((method)(X))
        print("  for DataArray{Float64}:      ")
        @time((method)(D))
        println()
    end

    for method in (
        sumabs,
        sumabs2
    )
        (method)(Y)
        println("Method: $method(A) (~half missing entries, skip=false)")
        print("  for NullableArray{Float64}:  ")
        @time((method)(Y))
        (method)(E)
        print("  for DataArray{Float64}:      ")
        @time((method)(E))
        println()
    end

    for method in (
        sumabs,
        sumabs2
    )
        (method)(Y, skipnull=true)
        println("Method: $method(A) (~half missing entries, skip=true)")
        print("  for NullableArray{Float64}:  ")
        @time((method)(Y, skipnull=true))
        (method)(E, skipna=true)
        print("  for DataArray{Float64}:      ")
        @time((method)(E, skipna=true))
        println()
    end
end

function profile_mapreduce(A, X, Y, D, E)
    println("Method: mapreduce(f, op, A) (0 missing entries)")
    mapreduce(f, Base.(:+), A)
    print("  for Array{Float64}:          ")
    @time(mapreduce(f, Base.(:+), A))
    mapreduce(f, Base.(:+), X)
    print("  for NullableArray{Float64}:  ")
    @time(mapreduce(f, Base.(:+), X))
    mapreduce(f, Base.(:+), D)
    print("  for DataArray{Float64}:      ")
    @time(mapreduce(f, Base.(:+), D))
    println()

    println("Method: mapreduce(f, op, A) (~half missing entries, skip=false)")
    mapreduce(f, Base.(:+), Y)
    print("  for NullableArray{Float64}:  ")
    @time(mapreduce(f, Base.(:+), Y))
    mapreduce(f, Base.(:+), E)
    print("  for DataArray{Float64}:      ")
    @time(mapreduce(f, Base.(:+), E))
    println()

    println("Method: mapreduce(f, op, A) (~half missing entries, skip=true)")
    mapreduce(f, Base.(:+), Y, skipnull=true)
    print("  for NullableArray{Float64}:  ")
    @time(mapreduce(f, Base.(:+), Y, skipnull=true))
    mapreduce(f, Base.(:+), E, skipna=true)
    print("  for DataArray{Float64}:      ")
    @time(mapreduce(f, Base.(:+), E, skipna=true))
    println()
end

function profile_reduce(A, X, Y, D, E)
    println("Method: reduce(f, op, A) (0 missing entries)")
    reduce(Base.(:+), A)
    print("  for Array{Float64}:          ")
    @time(reduce(Base.(:+), A))
    reduce(Base.(:+), X)
    print("  for NullableArray{Float64}:  ")
    @time(reduce(Base.(:+), X))
    reduce(Base.(:+), D)
    print("  for DataArray{Float64}:      ")
    @time(reduce(Base.(:+), D))
    println()

    println("Method: reduce(f, op, A) (~half missing entries, skip=false)")
    reduce(Base.(:+), Y)
    print("  for NullableArray{Float64}:  ")
    @time(reduce(Base.(:+), Y))
    reduce(Base.(:+), E)
    print("  for DataArray{Float64}:      ")
    @time(reduce(Base.(:+), E))
    println()

    println("Method: reduce(f, op, A) (~half missing entries, skip=true)")
    reduce(Base.(:+), Y, skipnull=true)
    print("  for NullableArray{Float64}:  ")
    @time(reduce(Base.(:+), Y, skipnull=true))
    reduce(Base.(:+), E, skipna=true)
    print("  for DataArray{Float64}:      ")
    @time(reduce(Base.(:+), E, skipna=true))
    println()
end

# # NullableArray vs. DataArray comparison
function profile_skip(skip::Bool)
    println("Comparison of skipnull/skipNA methods")
    println()
    println("f := identity, op := +")
    println("mapreduce(f, op, X; skipnull/skipNA=$skip) (0 missing entries)")

    mapreduce(identity, +, X, skipnull=skip)
    print("  for NullableArray{Float64}:  ")
    @time(mapreduce(identity, +, X, skipnull=skip))

    mapreduce(identity, +, D, skipna=skip)
    print("  for DataArray{Float64}:      ")
    @time(mapreduce(identity, +, D, skipna=skip))

    println()
    println("reduce(op, X; skipnull/skipNA=$skip) (0 missing entries)")
    reduce(+, X, skipnull=skip)
    print("  for NullableArray{Float64}:  ")
    @time(reduce(+, X, skipnull=skip))

    reduce(+, D, skipna=skip)
    print("  for DataArray{Float64}:      ")
    @time(reduce(+, D, skipna=skip))

    println()
    println("mapreduce(f, op, X; skipnull/skipNA=$skip) (~half missing entries)")
    mapreduce(identity, +, Y, skipnull=skip)
    print("  for NullableArray{Float64}:  ")
    @time(mapreduce(identity, +, Y, skipnull=skip))

    mapreduce(identity, +, E, skipna=skip)
    print("  for DataArray{Float64}:      ")
    @time(mapreduce(identity, +, E, skipna=skip))

    println()
    println("reduce(op, X; skipnull/skipNA=$skip) (~half missing entries)")
    reduce(+, Y, skipnull=skip)
    print("  for NullableArray{Float64}:  ")
    @time(reduce(+, Y, skipnull=skip))

    reduce(+, E, skipna=true)
    print("  for DataArray{Float64}:      ")
    @time(reduce(+, E, skipna=true))
    nothing
end

function profile_skip_impl()
    println("Comparison of internal skip methods:")
    println("mapreduce_impl_skipnull(f, op, X) (0 missing entries)")
    NullableArrays.mapreduce_impl_skipnull(identity, +, X)
    print("  for NullableArray{Float64}:  ")
    @time(NullableArrays.mapreduce_impl_skipnull(identity, +, X))

    DataArrays.mapreduce_impl_skipna(identity, +, D)
    print("  for DataArray{Float64}:      ")
    @time(DataArrays.mapreduce_impl_skipna(identity, +, D))

    println()
    println("mapreduce_impl_skipnull(f, op, X) (~half missing entries)")
    NullableArrays.mapreduce_impl_skipnull(identity, +, Y)
    print("  for NullableArray{Float64}:  ")
    @time(NullableArrays.mapreduce_impl_skipnull(identity, +, Y))

    DataArrays.mapreduce_impl_skipna(identity, +, E)
    print("  for DataArray{Float64}:      ")
    @time(DataArrays.mapreduce_impl_skipna(identity, +, E))
    nothing
end


InputFolder = './Images/CellsCyto/';
OutputFolder = './Results/Images/CellsCyto/';
Lbl = 1;

@iA = '*_C0000*.tif';		% Image filter
@iS = '*_C0001*.tif';		% Image seeds filter

@fxgs_lSeededPropagate [iA, iS] > [L, P];
params.Thr = 0;
params.Power = 0.5;
params.AnalyzeCC = 0;
/endf

%show iA > iS;
/show iA > L;
/keep L > tif;


# Here goes all the stuff that connects this code to ExtendableGrids like
# - definition of additional adjacency types and their instantiation
# - connections between CellGeometries and FaceGeometries of their faces
# - formulas to compute Volumes and Normal Vectors etc.
# (some of this might become native in the ExtendableGrids module itself at some point)

# additional ElementGeometryTypes with parent information
#abstract type Vertex0DWithParent{Parent <: AbstractElementGeometry} <: Vertex0D end
#abstract type Vertex0DWithParents{Parent1 <: AbstractElementGeometry, Parent2 <: AbstractElementGeometry} <: Vertex0D end
#export Vertex0DWithParent, Vertex0DWithParents

#function AddParent(FEG::Type{<:Vertex0D}, CEG::Type{<:AbstractElementGeometry})
#    return Vertex0DWithParent{CEG}
#end

#abstract type Edge1DWithParent{Parent <: AbstractElementGeometry} <: Edge1D end
#abstract type Edge1DWithParents{Parent1 <: AbstractElementGeometry, Parent2 <: AbstractElementGeometry} <: Edge1D end
#export Edge1DWithParent, Edge1DWithParents

#function AddParent(FEG::Type{<:Edge1D}, CEG::Type{<:AbstractElementGeometry})
#    return Edge1DWithParent{CEG}
#end

const GridEGTypes = Vector{DataType}
const GridRegionTypes{Ti} = Union{VectorOfConstants{Ti}, Array{Ti,1}}

# additional ExtendableGrids adjacency types
abstract type CellEdges <: AbstractGridAdjacency end
abstract type CellFaces <: AbstractGridAdjacency end
abstract type CellFaceSigns <: AbstractGridAdjacency end
abstract type CellFaceOrientations <: AbstractGridAdjacency end
abstract type CellEdgeSigns <: AbstractGridAdjacency end
abstract type CellVolumes <: AbstractGridFloatArray1D end
abstract type UniqueCellGeometries <: AbstractElementGeometries end

abstract type FaceNodes <: AbstractGridAdjacency end
abstract type FaceVolumes <: AbstractGridFloatArray1D end
abstract type FaceCells <: AbstractGridAdjacency end
abstract type FaceEdges <: AbstractGridAdjacency end
abstract type FaceNormals <: AbstractGridFloatArray2D end
abstract type FaceGeometries <: AbstractElementGeometries end
abstract type FaceRegions <: AbstractElementRegions end
abstract type UniqueFaceGeometries <: AbstractElementGeometries end

abstract type BFaceFaces <: AbstractGridIntegerArray1D end
abstract type BFaceCellPos <: AbstractGridIntegerArray1D end # position of bface in adjacent cell
abstract type BFaceVolumes <: AbstractGridFloatArray1D end
abstract type UniqueBFaceGeometries <: AbstractElementGeometries end

abstract type EdgeNodes <: AbstractGridAdjacency end
abstract type EdgeVolumes <: AbstractGridFloatArray1D end
abstract type EdgeCells <: AbstractGridAdjacency end
abstract type EdgeTangents <: AbstractGridFloatArray2D end
abstract type EdgeRegions <: AbstractElementRegions end
abstract type EdgeGeometries <: AbstractElementGeometries end
abstract type UniqueEdgeGeometries <: AbstractElementGeometries end

abstract type BEdgeNodes <: AbstractGridAdjacency end
abstract type BEdgeEdges <: AbstractGridIntegerArray1D end
#abstract type BEdgeCellPos <: AbstractGridIntegerArray1D end # position of bface in adjacent cell
abstract type BEdgeVolumes <: AbstractGridFloatArray1D end
abstract type BEdgeRegions <: AbstractElementRegions end
abstract type BEdgeGeometries <: AbstractElementGeometries end
abstract type UniqueBEdgeGeometries <: AbstractElementGeometries end

abstract type NodePatchGroups <: AbstractGridIntegerArray1D end

## grid item types to dispatch certain things to the correct GridComponents
abstract type ITEMTYPE_NODE end
abstract type ITEMTYPE_CELL end
abstract type ITEMTYPE_FACE end
abstract type ITEMTYPE_BFACE end
abstract type ITEMTYPE_EDGE end
abstract type ITEMTYPE_BEDGE end

abstract type PROPERTY_NODES end
abstract type PROPERTY_VOLUME end
abstract type PROPERTY_REGION end
abstract type PROPERTY_GEOMETRY end
abstract type PROPERTY_UNIQUEGEOMETRY end

GridComponent4TypeProperty(::Type{ITEMTYPE_CELL},::Type{PROPERTY_NODES}) = CellNodes
GridComponent4TypeProperty(::Type{ITEMTYPE_CELL},::Type{PROPERTY_VOLUME}) = CellVolumes
GridComponent4TypeProperty(::Type{ITEMTYPE_CELL},::Type{PROPERTY_REGION}) = CellRegions
GridComponent4TypeProperty(::Type{ITEMTYPE_CELL},::Type{PROPERTY_GEOMETRY}) = CellGeometries
GridComponent4TypeProperty(::Type{ITEMTYPE_CELL},::Type{PROPERTY_UNIQUEGEOMETRY}) = UniqueCellGeometries

GridComponent4TypeProperty(::Type{ITEMTYPE_FACE},::Type{PROPERTY_NODES}) = FaceNodes
GridComponent4TypeProperty(::Type{ITEMTYPE_FACE},::Type{PROPERTY_VOLUME}) = FaceVolumes
GridComponent4TypeProperty(::Type{ITEMTYPE_FACE},::Type{PROPERTY_REGION}) = FaceRegions
GridComponent4TypeProperty(::Type{ITEMTYPE_FACE},::Type{PROPERTY_GEOMETRY}) = FaceGeometries
GridComponent4TypeProperty(::Type{ITEMTYPE_FACE},::Type{PROPERTY_UNIQUEGEOMETRY}) = UniqueFaceGeometries

GridComponent4TypeProperty(::Type{ITEMTYPE_BFACE},::Type{PROPERTY_NODES}) = BFaceNodes
GridComponent4TypeProperty(::Type{ITEMTYPE_BFACE},::Type{PROPERTY_VOLUME}) = BFaceVolumes
GridComponent4TypeProperty(::Type{ITEMTYPE_BFACE},::Type{PROPERTY_REGION}) = BFaceRegions
GridComponent4TypeProperty(::Type{ITEMTYPE_BFACE},::Type{PROPERTY_GEOMETRY}) = BFaceGeometries
GridComponent4TypeProperty(::Type{ITEMTYPE_BFACE},::Type{PROPERTY_UNIQUEGEOMETRY}) = UniqueBFaceGeometries

GridComponent4TypeProperty(::Type{ITEMTYPE_EDGE},::Type{PROPERTY_NODES}) = EdgeNodes
GridComponent4TypeProperty(::Type{ITEMTYPE_EDGE},::Type{PROPERTY_VOLUME}) = EdgeVolumes
GridComponent4TypeProperty(::Type{ITEMTYPE_EDGE},::Type{PROPERTY_REGION}) = EdgeRegions
GridComponent4TypeProperty(::Type{ITEMTYPE_EDGE},::Type{PROPERTY_GEOMETRY}) = EdgeGeometries
GridComponent4TypeProperty(::Type{ITEMTYPE_EDGE},::Type{PROPERTY_UNIQUEGEOMETRY}) = UniqueEdgeGeometries

GridComponent4TypeProperty(::Type{ITEMTYPE_BEDGE},::Type{PROPERTY_NODES}) = BEdgeNodes
GridComponent4TypeProperty(::Type{ITEMTYPE_BEDGE},::Type{PROPERTY_VOLUME}) = BEdgeVolumes
GridComponent4TypeProperty(::Type{ITEMTYPE_BEDGE},::Type{PROPERTY_REGION}) = BEdgeRegions
GridComponent4TypeProperty(::Type{ITEMTYPE_BEDGE},::Type{PROPERTY_GEOMETRY}) = BEdgeGeometries
GridComponent4TypeProperty(::Type{ITEMTYPE_BEDGE},::Type{PROPERTY_UNIQUEGEOMETRY}) = UniqueBEdgeGeometries

function get_facegrid(source_grid::ExtendableGrid{Tc,Ti}) where {Tc,Ti}
    facegrid=ExtendableGrid{Tc,Ti}()
    facegrid[Coordinates]=source_grid[Coordinates]
    facegrid[CellNodes]=source_grid[FaceNodes]
    facegrid[CoordinateSystem]=source_grid[CoordinateSystem]
    facegrid[CellGeometries]=source_grid[FaceGeometries]
    facegrid[UniqueCellGeometries]=source_grid[UniqueFaceGeometries]
    facegrid[CellRegions] = source_grid[FaceRegions]
    # todo: facegrid[CellFaces] = source_grid[FaceEdges]
    return facegrid
end

function get_bfacegrid(source_grid::ExtendableGrid{Tc,Ti})  where {Tc,Ti}
    bfacegrid=ExtendableGrid{Tc,Ti}()
    bfacegrid[Coordinates]=source_grid[Coordinates]
    bfacegrid[CellNodes]=source_grid[BFaceNodes]
    bfacegrid[CoordinateSystem]=source_grid[CoordinateSystem]
    bfacegrid[CellGeometries]=source_grid[BFaceGeometries]
    bfacegrid[UniqueCellGeometries]=source_grid[UniqueBFaceGeometries]
    bfacegrid[CellRegions] = source_grid[BFaceRegions]
    # todo: bfacegrid[CellFaces] = source_grid[BFaceEdges] (or sub-view of FaceEdges ?)
    return bfacegrid
end

function get_edgegrid(source_grid::ExtendableGrid{Tc,Ti}) where {Tc,Ti}
    edgegrid=ExtendableGrid{Tc,Ti}()
    edgegrid[Coordinates]=source_grid[Coordinates]
    edgegrid[CellNodes]=source_grid[EdgeNodes]
    edgegrid[CoordinateSystem]=source_grid[CoordinateSystem]
    edgegrid[CellGeometries]=source_grid[EdgeGeometries]
    edgegrid[UniqueCellGeometries]=source_grid[UniqueedgeGeometries]
    edgegrid[CellRegions] = source_grid[EdgeRegions]
    return edgegrid
end

# show function for ExtendableGrids and defined Components in its Dict
function showmore(io::IO, xgrid::ExtendableGrid{Tc,Ti}) where {Tc,Ti}

    dim = size(xgrid[Coordinates],1)
    nnodes = num_sources(xgrid[Coordinates])
    ncells = num_sources(xgrid[CellNodes])

	println("ExtendableGrid information");
    println("==========================");
	println("dim: $(dim)")
	println("nnodes: $(nnodes)")
    println("ncells: $(ncells)")
    if haskey(xgrid.components,FaceNodes)
        nfaces = num_sources(xgrid[FaceNodes])
        println("nfaces: $(nfaces)")
    else
        println("nfaces: (FaceNodes not instantiated)")
    end
    if haskey(xgrid.components,EdgeNodes)
        nedges = num_sources(xgrid[EdgeNodes])
        println("nedges: $(nedges)")
    else
        println("nedges: (EdgeNodes not instantiated)")
    end
    println("")
    println("Components");
    println("==========");
    for tuple in xgrid.components
        println("> $(tuple[1])")
    end
end

# FaceNodes = nodes for each face (implicitly defines the enumerations of faces)
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceNodes}) where {Tc,Ti}

    xCellNodes = xgrid[CellNodes]
    ncells = num_sources(xCellNodes)
    nnodes = num_sources(xgrid[Coordinates])
    xCellGeometries = xgrid[CellGeometries]

    # transpose CellNodes to get NodeCells
    xNodeCells = atranspose(xCellNodes)
    max_ncell4node::Ti = max_num_targets_per_source(xNodeCells)

    # instantiate new empty adjacency fields
    xFaceCells = zeros(Ti,0) # cells are appended and at the end rewritten into 2,nfaces array

    # find unique face enumeration rules
    EG = unique(xCellGeometries)
    dim::Ti = dim_element(EG[1])
    face_rules = Array{Array{Ti,2},1}(undef,length(EG))
    maxfacenodes::Ti = 0
    FEG = []
    for j = 1 : length(EG)
        face_rules[j] = local_cellfacenodes(EG[j])
        maxfacenodes = max(size(face_rules[j],2),maxfacenodes)
        for k = 1 : num_faces(EG[j])
            append!(FEG,[facetype_of_cellface(EG[j],j)])
        end
    end
    unique!(FEG)

    # check if only one type of cell geometry is present in grid
    if length(EG) == 1
        singleEG = true
    else
        singleEG = false
    end
    # check if only one type of face geometry is present in grid
    if length(FEG) == 1
        singleFEG = true
    else
        singleFEG = false
    end

    if singleFEG
        xFaceNodes = zeros(Ti,0)
    else
        xFaceNodes = VariableTargetAdjacency(Ti)
        xFaceGeometries::Array{DataType,1} = []
    end
    if singleEG == true && singleFEG == true
        # only one geometry type allows for much faster code
        xCellFaces = zeros(Ti,num_faces(EG[1]),ncells)
        xCellFaceSigns = zeros(Ti,num_faces(EG[1]),ncells)
    else
        xCellFaces = VariableTargetAdjacency(Ti)
        xCellFaceSigns = VariableTargetAdjacency(Ti)
        # pre-allocate xCellFaces
        cellEG = xCellGeometries[1]
        for cell = 1 : ncells
            cellEG = xCellGeometries[cell]
            append!(xCellFaces,zeros(Ti,num_faces(cellEG)))
            append!(xCellFaceSigns,zeros(Ti,num_faces(cellEG)))
        end
    end

    # temporary variables
    # pre-initialised ready to work for singleEG and singleFEG
    node::Ti = 0
    face::Ti = 0
    cell::Ti = 0
    cell2::Ti = 0
    cellEG = EG[1]
    cell2EG = EG[1]
    faceEG = FEG[1]
    faceEG2 = FEG[1]
    face_rule::Array{Ti,2} = face_rules[1]
    face_rule2::Array{Ti,2} = face_rules[1]
    iEG::Ti = 1
    nneighbours::Ti = 0
    faces_per_cell::Ti = num_faces(cellEG)
    faces_per_cell2::Ti = num_faces(cellEG)
    nodes_per_cellface::Ti = num_nodes(faceEG)
    current_item::Array{Ti,1} = zeros(Ti,maxfacenodes) # should be large enough to store largest nnodes per cellface
    flag4item::Array{Bool,1} = zeros(Bool,nnodes)
    no_neighbours_found::Bool = true
    same_face::Bool = false

    # loop over cells
    for cell = 1 : ncells
        # find EG index for geometry
        if !singleEG
            cellEG = xCellGeometries[cell]
            faces_per_cell = num_faces(cellEG)
            for j=1:length(EG)
                if cellEG == EG[j]
                    iEG = j
                    break;
                end
            end
            face_rule = face_rules[iEG]
        end

        # loop over cell faces
        for k = 1 : faces_per_cell

            # check if face is already known to cell
            if xCellFaces[k,cell] > 0
                continue;
            end

            # get face geometry
            if !singleFEG
                faceEG = facetype_of_cellface(cellEG, k)
                nodes_per_cellface = num_nodes(faceEG)
            end

            # flag face nodes and commons4cells
            for j = nodes_per_cellface:-1:1
                node = xCellNodes[face_rule[j,k],cell]
                current_item[j] = node
                flag4item[node] = true;
            end

            # get neighbours for first node
            nneighbours = num_targets(xNodeCells,node)

            # loop over neighbours
            no_neighbours_found = true
            for n = 1 : nneighbours
                cell2 = xNodeCells[n,node]

                # skip if cell2 is the same as cell
                if (cell == cell2)
                    continue;
                end

                # find face enumeration rule
                if !singleEG
                    cell2EG = xCellGeometries[cell2]
                    faces_per_cell2 = num_faces(cell2EG)
                    for j=1:length(EG)
                        if cell2EG == EG[j]
                            iEG = j
                            break;
                        end
                    end
                    face_rule2 = face_rules[iEG]
                end

                # loop over faces face2 of adjacent cell2
                for f2 = 1 : faces_per_cell2

                    # check if face f2 is already known to cell2
                    if xCellFaces[f2,cell2] != 0
                        continue;
                    end

                    # check if face f2 has same geometry
                    if !singleFEG
                        faceEG2 = facetype_of_cellface(cell2EG, f2)
                        if faceEG != faceEG2
                            continue;
                        end
                    end

                    # otherwise compare nodes of face and face2
                    same_face = true
                    for j = 1 : nodes_per_cellface
                        if flag4item[xCellNodes[face_rule2[j,f2],cell2]] == false
                            same_face = false
                            break;
                        end
                    end

                    # if all nodes are the same, register face
                    if (same_face)
                        no_neighbours_found = false
                        face += 1
                        push!(xFaceCells,cell)
                        push!(xFaceCells,cell2)
                        if singleEG == false
                            xCellFaces.colentries[xCellFaces.colstart[cell]+k-1] = face
                            xCellFaces.colentries[xCellFaces.colstart[cell2]+f2-1] = face
                            xCellFaceSigns.colentries[xCellFaceSigns.colstart[cell]+k-1] = 1
                            xCellFaceSigns.colentries[xCellFaceSigns.colstart[cell2]+f2-1] = -1
                            if singleFEG == false
                                push!(xFaceGeometries,faceEG)
                            end
                        else
                            xCellFaces[k,cell] = face
                            xCellFaces[f2,cell2] = face
                            xCellFaceSigns[k,cell] = 1
                            xCellFaceSigns[f2,cell2] = -1
                        end
                        append!(xFaceNodes,view(current_item,1:nodes_per_cellface))
                        break;
                    end
                end
            end

            # if no common neighbour cell is found, register face (boundary faces)
            if no_neighbours_found == true
                face += 1
                push!(xFaceCells,cell)
                push!(xFaceCells,0)
                if singleEG == false
                    xCellFaces.colentries[xCellFaces.colstart[cell]+k-1] = face
                    xCellFaceSigns.colentries[xCellFaceSigns.colstart[cell]+k-1] = 1
                    if singleFEG == false
                        push!(xFaceGeometries,faceEG)
                    end
                else
                    xCellFaces[k,cell] = face
                    xCellFaceSigns[k,cell] = 1
                end
                append!(xFaceNodes,view(current_item,1:nodes_per_cellface))
            end

            # reset flag4item
            for j = 1:nodes_per_cellface
                flag4item[current_item[j]] = false
            end
        end
    end

    if singleFEG
        xFaceNodes = reshape(xFaceNodes,(nodes_per_cellface,Ti(length(xFaceNodes)/nodes_per_cellface)))
        xgrid[FaceGeometries] = VectorOfConstants(facetype_of_cellface(EG[1], 1), face)
    else
        xgrid[FaceGeometries] = xFaceGeometries
    end
    xgrid[CellFaces] = xCellFaces
    xgrid[CellFaceSigns] = xCellFaceSigns
    xgrid[FaceCells] = reshape(xFaceCells,(2,face))
    xFaceNodes
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{NodePatchGroups}) where {Tc,Ti}
    xCellNodes::Adjacency = xgrid[CellNodes]
    xNodeCells = atranspose(xCellNodes)
    nnodes = size(xgrid[Coordinates],2)
    ncells = num_sources(xCellNodes)
    ncells4node::Ti = 0
    group4node = zeros(Ti,nnodes)
    cgroup::Ti = 0
    cell_in_group = zeros(Bool,ncells)
    take_into = false
    cgroup = 0
    while minimum(group4node) == 0
        cell_in_group .= false
        cgroup += 1
        for node = 1 : nnodes
            if group4node[node] == 0
                ncells4node = num_targets(xNodeCells,node)
                take_into = true
                for c = 1 : ncells4node
                    if cell_in_group[xNodeCells[c,node]] == true
                        take_into = false
                        break
                    end
                end
                if take_into
                    group4node[node] = cgroup
                    for c = 1 : ncells4node
                        cell_in_group[xNodeCells[c,node]] = true
                    end
                end
            end
        end
    end
    group4node
end

# FaceNodes = nodes for each face (implicitly defines the enumerations of faces)
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{EdgeNodes}) where {Tc,Ti}

    xCellNodes = xgrid[CellNodes]
    ncells::Ti = num_sources(xCellNodes)
    nnodes::Ti = num_sources(xgrid[Coordinates])
    xCellGeometries = xgrid[CellGeometries]
    dim::Ti = dim_element(xCellGeometries[1])

    # in 1D edges = cells
    if dim == 1
        xgrid[EdgeNodes] = xgrid[CellNodes]
        xgrid[EdgeGeometries] = xgrid[CellGeometries]
        xgrid[CellEdges] = reshape(collect(Ti,1:size(xCellNodes,2)), (1,size(xCellNodes,2)))
        xgrid[EdgeCells] = xgrid[CellEdges]
        return xgrid[EdgeNodes]
    end

    # in 2D edges = faces
    if dim == 2
        xgrid[CellEdges] = xgrid[CellFaces]
        xgrid[EdgeCells] = xgrid[FaceCells]
        xgrid[EdgeNodes] = xgrid[FaceNodes]
        xgrid[EdgeGeometries] = xgrid[FaceGeometries]
        return xgrid[EdgeNodes]
#        return  prepare_edges!(xgrid) # keep compatible to VoronoiFVM
    end

    # transpose CellNodes to get NodeCells
    xNodeCells = atranspose(xCellNodes)
    max_ncell4node::Ti = max_num_targets_per_source(xNodeCells)

    # init EdgeCells
    xEdgeCells = VariableTargetAdjacency(Ti)

    # find unique edge enumeration rules for each cell geometry
    EG::Array{DataType,1} = unique(xCellGeometries)
    edge_rules::Array{Array{Ti,2},1} = Array{Array{Ti,2},1}(undef,length(EG))
    maxedgenodes::Ti = 0
    for j = 1 : length(EG)
        edge_rules[j] = local_celledgenodes(EG[j])
        maxedgenodes = max(size(edge_rules[j],1),maxedgenodes)
    end

    # pre-allocate xCellEdges and xCellEdgeSigns
    if length(EG) == 1
        singleEG = true
        xCellEdges = zeros(Ti,num_edges(EG[1]),ncells)
        xCellEdgeSigns = zeros(Ti,num_edges(EG[1]),ncells)
    else
        singleEG = false
        xCellEdges = VariableTargetAdjacency(Ti)
        cellEG = xCellGeometries[1]
        for cell = 1 : ncells
            cellEG = xCellGeometries[cell]
            append!(xCellEdges,zeros(Ti,num_edges(cellEG)))
        end
        xCellEdgeSigns = deepcopy(xCellEdges)
    end

    # init xEdgeNodes
    xEdgeNodes::Array{Ti,1} = zeros(Ti,0)

    edge_rule::Array{Ti,2} = edge_rules[1]
    edge_rule2::Array{Ti,2} = edge_rules[1]
    current_item::Array{Ti,1} = zeros(Ti,2)
    flag4item::Array{Bool,1} = zeros(Bool,nnodes)
    cellEG = EG[1]
    node::Ti = 0
    cell2::Ti = 0
    nneighbours::Ti = 0
    edges_per_cell::Ti = num_edges(EG[1])
    edges_per_cell2::Ti = num_edges(EG[1])
    common_nodes::Ti = 0
    cells_with_common_edge::Array{Ti,1} = zeros(Ti,max_ncell4node)
    pos_in_cells_with_common_edge::Array{Ti,1} = zeros(Ti,max_ncell4node)
    sign_in_cells_with_common_edge::Array{Ti,1} = zeros(Ti,max_ncell4node)
    ncells_with_common_edge::Ti = 0
    edge::Ti = 0
    iEG::Ti = 0

    # loop over cells
    for cell = 1 : ncells

        # find EG index for geometry
        if singleEG == false
            cellEG = xCellGeometries[cell]
            edges_per_cell = num_edges(cellEG)
            for j=1:length(EG)
                if cellEG == EG[j]
                    iEG = j
                    break;
                end
            end
            edge_rule = edge_rules[iEG]
        end

        # loop over cell edges
        for k = 1 : edges_per_cell

            # check if edge is already known to cell
            if xCellEdges[k,cell] > 0
                continue;
            end
            ncells_with_common_edge = 1
            cells_with_common_edge[1] = cell
            pos_in_cells_with_common_edge[1] = k
            sign_in_cells_with_common_edge[1] = 1

            # flag edge nodes and commons4cells
            for j = 2 : - 1 : 1
                node = xCellNodes[edge_rule[j,k],cell]
                current_item[j] = node
                flag4item[node] = true;
            end

            # get first node and its neighbours
            nneighbours = num_targets(xNodeCells,node)

            # loop over neighbours
            for n = 1 : nneighbours
                cell2 = xNodeCells[n,node]

                # skip if cell2 is the same as cell
                if (cell == cell2)
                    continue;
                end

                # loop over edges of cell2
                if singleEG == false
                    cellEG = xCellGeometries[cell2]
                    edges_per_cell2 = num_edges(cellEG)

                    # find edge enumeration rule
                    for j=1:length(EG)
                        if cellEG == EG[j]
                            iEG = j
                            break;
                        end
                    end
                    edge_rule2 = edge_rules[iEG]
                end

                for f2 = 1 : edges_per_cell2
                    # compare nodes of edge and edge2
                    common_nodes = 0
                    for j = 1 : 2
                        if flag4item[xCellNodes[edge_rule2[j,f2],cell2]]
                            common_nodes += 1
                        else
                            continue;
                        end
                    end

                    # if all nodes are the same, register edge
                    if (common_nodes == 2)
                        ncells_with_common_edge += 1
                        cells_with_common_edge[ncells_with_common_edge] = cell2
                        pos_in_cells_with_common_edge[ncells_with_common_edge] = f2
                        if xCellNodes[edge_rule2[1,f2],cell2] == current_item[1]
                            sign_in_cells_with_common_edge[ncells_with_common_edge] = 1
                        else
                            sign_in_cells_with_common_edge[ncells_with_common_edge] = -1
                        end
                    end
                end
            end

            # register edge
            edge += 1
            for c = 1 : ncells_with_common_edge
                xCellEdges[pos_in_cells_with_common_edge[c],cells_with_common_edge[c]] = edge
                xCellEdgeSigns[pos_in_cells_with_common_edge[c],cells_with_common_edge[c]] = sign_in_cells_with_common_edge[c]
            end
            append!(xEdgeCells,view(cells_with_common_edge,1:ncells_with_common_edge))
            append!(xEdgeNodes,current_item)

            #reset flag4item
            for j = 1 : 2
                flag4item[current_item[j]] = false
            end
        end
    end
    xgrid[CellEdges] = xCellEdges
    xgrid[EdgeCells] = xEdgeCells
    xgrid[CellEdgeSigns] = xCellEdgeSigns
    xgrid[EdgeGeometries] = VectorOfConstants(Edge1D,edge)
    reshape(xEdgeNodes,(2,edge))
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{CellFaceOrientations}) where {Tc,Ti}
    xCellFaceSigns = xgrid[CellFaceSigns]
    xCellGeometries = xgrid[CellGeometries]
    xCellFaces = xgrid[CellFaces]
    xFaceNodes = xgrid[FaceNodes]
    xCellNodes = xgrid[CellNodes]
    ncells = num_sources(xCellNodes)

    EG = unique(xCellGeometries)
    face_rules = Array{Array{Ti,2},1}(undef,length(EG))
    maxfacenodes = 0
    for j = 1 : length(EG)
        face_rules[j] = local_cellfacenodes(EG[j])
        maxfacenodes = max(size(face_rules[j],2),maxfacenodes)
    end
    singleEG = false
    if typeof(xCellFaceSigns) <: VariableTargetAdjacency
        xCellFaceOrientations = deepcopy(xCellFaceSigns)
    else
        singleEG = true
        xCellFaceOrientations = zeros(Ti, size(xCellFaceSigns,1), size(xCellFaceSigns,2))
    end

    cellEG = EG[1]
    face_rule = face_rules[1]
    ncellfaces::Ti = 0
    nfacenodes::Ti = 0
    face::Ti = 0
    facenodes = zeros(Ti,maxfacenodes)
    found_configuration::Bool = false
    n::Ti = 0
    iEG::Ti = 0
    for cell = 1 : ncells
        cellEG = xCellGeometries[cell]

        # find EG index for geometry
        for j=1:length(EG)
            if cellEG == EG[j]
                iEG = j
                break;
            end
        end
        face_rule = face_rules[iEG] # determines local enumeration of faces

        # determine orientation
        ncellfaces = num_targets(xCellFaces,cell)
        for j = 1 : ncellfaces
            face = xCellFaces[j,cell]
            nfacenodes = num_targets(xFaceNodes,face)
            if xCellFaceSigns[j,cell] == 1
                if singleEG == false
                    xCellFaceOrientations.colentries[xCellFaceOrientations.colstart[cell]+j-1] = 1
                else
                    xCellFaceOrientations[j, cell] = 1
                end
            else
                for k = 1 : nfacenodes
                    facenodes[nfacenodes + 1 - k] = xFaceNodes[k,face]
                end
                found_configuration = false
                n = 0
                while !found_configuration
                    n += 1
                    if facenodes[n] == xCellNodes[face_rule[1,j],cell]
                        found_configuration = true
                    end
                end
                n = mod(n-1,3) + 1
                if singleEG == false
                    xCellFaceOrientations.colentries[xCellFaceOrientations.colstart[cell]+j-1] = 1+n
                else
                    xCellFaceOrientations[j, cell] = 1+n
                end
            end
        end

    end
    return xCellFaceOrientations
end

# FaceEdges = Edges for each face (in 3D)
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceEdges}) where {Tc,Ti}
    xFaceNodes = xgrid[FaceNodes]
    xFaceCells = xgrid[FaceCells]
    xEdgeNodes = xgrid[EdgeNodes]
    xCellEdges = xgrid[CellEdges]
    xFaceEdges = VariableTargetAdjacency(Ti)
    xFaceGeometries = xgrid[FaceGeometries]
    nfaces::Ti = num_sources(xFaceNodes)
    nnodes::Ti = size(xgrid[Coordinates],2)

    # find unique edge enumeration rules
    EG = unique(xFaceGeometries)
    edge_rules = Array{Array{Ti,2},1}(undef,length(EG))
    maxedgenodes = 0
    for j = 1 : length(EG)
        edge_rules[j] = local_cellfacenodes(EG[j]) # face edges are faces of the face
        maxedgenodes = max(size(edge_rules[j],2),maxedgenodes)
    end
    edge_rule::Array{Ti,2} = edge_rules[1]
    if length(EG) == 1
        singleEG = true
        xFaceEdges = zeros(Ti,num_faces(EG[1]),nfaces)
    else
        singleEG = false
        faceEG = xCellGeometries[1]
        for face = 1 : nfaces
            faceEG = xCellGeometries[cell]
            append!(xCellEdges,zeros(Ti,num_faces(cellEG)))
        end
    end

    nfacenodes::Ti = num_targets(xFaceNodes,1)
    ncelledges::Ti = 0
    nedgenodes::Ti = 0
    nfaceedges::Ti = num_edges(EG[1])
    node::Ti = 0
    cell::Ti = 0
    edge::Ti = 0
    faceedge::Ti = 0
    edge_is_in_face::Bool = false
    found_pos::Bool = false
    flag4face = zeros(Bool,nnodes)
    flag4edge = zeros(Bool,nnodes)
    faceEG = EG[1]
    iEG::Ti = 1
    pos::Ti = 0
    for face = 1 : nfaces

        if singleEG == false
            faceEG = xFaceGeometries[face]
            nfaceedges = num_edges(faceEG)
            # find EG index for geometry
            for j=1:length(EG)
                if faceEG == EG[j]
                    iEG = j
                    break;
                end
            end
            edge_rule = edge_rules[iEG] # determines local enumeration of face edges
            nfacenodes = num_targets(xFaceNodes,face)
        end

        # mark nodes of face
        for j = 1 : nfacenodes
            node = xFaceNodes[j, face]
            flag4face[node] = true;
        end

        # find edges in first adjacent cell
        cell = xFaceCells[1,face]
        ncelledges = num_targets(xCellEdges,cell)
        faceedge = 0
        for cedge = 1 : ncelledges
            edge = xCellEdges[cedge,cell]
            nedgenodes = num_targets(xEdgeNodes,edge)
            edge_is_in_face = true
            for j = 1 : nedgenodes
                if flag4face[xEdgeNodes[j, edge]] == false
                    edge_is_in_face = false
                    break;
                end
            end

            if edge_is_in_face

                # register edge
                # it is important obey same local ordering as in edge_rule
                # to ensure correct dof handling (e.g. for P2-FEM on boundary)

                # mark nodes of edge
                for j = 1 : nedgenodes
                    node = xEdgeNodes[j, edge]
                    flag4edge[node] = true;
                end

                pos = 0
                found_pos = false
                while found_pos == false
                    pos += 1
                    found_pos = true
                    for k = 1 : nedgenodes
                        if flag4edge[xFaceNodes[edge_rule[k,pos], face]] == false
                            found_pos = false
                            break;
                        end
                    end
                end
                if singleEG
                    xFaceEdges[pos,face] = edge
                else
                    xFaceEdges.colentries[xFaceEdges.colstart[face]+pos-1] = edge
                end

                # reset flag4edge
                for j = 1 : nedgenodes
                    node = xEdgeNodes[j, edge]
                    flag4edge[node] = false;
                end

                faceedge += 1
                if faceedge == nfaceedges
                    break;
                end
            end
        end

        #reset flag4face
        for j = 1 : nfacenodes
            node = xFaceNodes[j, face]
            flag4face[node] = false;
        end

    end

    xFaceEdges
end

# CellFaces = faces for each cell
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{CellFaces}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, FaceNodes)
    xgrid[CellFaces]
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceGeometries}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, FaceNodes)
    xgrid[FaceGeometries]
end

# CellEdges = edges for each cell
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{CellEdges}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, EdgeNodes)
    xgrid[CellEdges]
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{EdgeCells}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, EdgeNodes)
    xgrid[EdgeCells]
end

# CellEdgeSigns = edges for each cell
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{CellEdgeSigns}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, EdgeNodes)
    xgrid[CellEdgeSigns]
end

# CellFaceSigns = orientation signs for each face on each cell
function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{CellFaceSigns}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, FaceNodes)
    xgrid[CellFaceSigns]
end

function collectVolumes4Geometries(T::Type{<:Real}, xgrid::ExtendableGrid{Tc,Ti}, ItemType) where {Tc,Ti}
    # get links to other stuff
    xCoordinates = xgrid[Coordinates]
    xCoordinateSystem = xgrid[CoordinateSystem]
    xItemNodes = xgrid[GridComponent4TypeProperty(ItemType,PROPERTY_NODES) ]
    xGeometries = xgrid[GridComponent4TypeProperty(ItemType,PROPERTY_GEOMETRY) ]
    EG = xgrid[GridComponent4TypeProperty(ItemType,PROPERTY_UNIQUEGEOMETRY) ]
    nitems::Ti = num_sources(xItemNodes)

    # get Volume4ElemType handlers
    handlers = Array{Function,1}(undef, length(EG))
    for j = 1: length(EG)
        handlers[j] = Volume4ElemType(xCoordinates, xItemNodes, EG[j], xCoordinateSystem)
    end

    # init Volumes
    xVolumes::Array{T,1} = zeros(T,nitems)

    # loop over items and call handlers
    iEG::Ti = 1
    itemEG = EG[1]
    for item = 1 : nitems
        if length(EG) > 1
            itemEG = xGeometries[item]
            for j=1:length(EG)
                if itemEG == EG[j]
                    iEG = j
                    break;
                end
            end
        end
        xVolumes[item] = handlers[iEG](item)
    end

    xVolumes
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{CellVolumes}) where {Tc,Ti}
    collectVolumes4Geometries(Tc, xgrid, ITEMTYPE_CELL)
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceVolumes}) where {Tc,Ti}
    collectVolumes4Geometries(Tc, xgrid, ITEMTYPE_FACE)
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BFaceVolumes}) where {Tc,Ti}
    collectVolumes4Geometries(Tc, xgrid, ITEMTYPE_BFACE)
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{EdgeVolumes}) where {Tc,Ti}
    collectVolumes4Geometries(Tc, xgrid, ITEMTYPE_EDGE)
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BEdgeVolumes}) where {Tc,Ti}
    collectVolumes4Geometries(Tc, xgrid, ITEMTYPE_BEDGE)
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BFaceFaces}) where {Tc,Ti}
    # get links to other stuff
    xCoordinates = xgrid[Coordinates]
    xFaceNodes = xgrid[FaceNodes]
    xBFaceNodes = xgrid[BFaceNodes]
    nnodes::Ti = num_sources(xCoordinates)
    nbfaces::Ti = num_sources(xBFaceNodes)

    # init BFaceFaces
    xBFaceFaces::Array{Ti,1} = zeros(Ti,nbfaces)
    #xBFaceGeometries = xgrid[BFaceGeometries]
    #if typeof(xBFaceGeometries) == VectorOfConstants{DataType}
    #    EG = xBFaceGeometries[1]
    #    xBFaceGeometries = Array{DataType,1}(undef,nbfaces)
    #    for j = 1 : nbfaces
    #        xBFaceGeometries[j] = EG
    #    end
    #end

    # transpose FaceNodes to get NodeFaces
    xNodeFaces = atranspose(xFaceNodes)

    flag4item::Array{Bool,1} = zeros(Bool,nnodes)
    nodes_per_bface::Ti = 0
    nodes_per_face::Ti = 0
    common_nodes::Ti = 0
    node::Ti = 0
    nneighbours::Ti = 0
    for bface = 1 : nbfaces
        nodes_per_bface = num_targets(xBFaceNodes,bface)
        for j = 1 : nodes_per_bface
            node = xBFaceNodes[j,bface]
            flag4item[node] = true
        end

        # get faces for last node of bface
        nneighbours = num_targets(xNodeFaces,node)

        # loop over faces and find the one that matches the bface
        for n = 1 : nneighbours
            face = xNodeFaces[n,node]
            nodes_per_face = num_targets(xFaceNodes,face)
            common_nodes = 0
            for k = 1 : nodes_per_face
                if flag4item[xFaceNodes[k,face]] == true
                    common_nodes += 1
                else
                    break
                end
            end
            if common_nodes == nodes_per_face
                xBFaceFaces[bface] = face
                break
            end
        end

        if xBFaceFaces[bface] == 0
            println("WARNING(BFaceFaces): found no matching face for bface $bface with nodes $(xBFaceNodes[:,bface])")
        end

        for j = 1 : nodes_per_bface
            flag4item[xBFaceNodes[j,bface]] = false
        end
    end

    # enforce that BFaceNodes have same ordering as FaceNodes
    newBFaceNodes = deepcopy(xBFaceNodes)
    for bface = 1 : nbfaces
        nodes_per_face = num_targets(xBFaceNodes,bface)
        for j = 1 : nodes_per_bface
            if typeof(xBFaceNodes) <: VariableTargetAdjacency
                newBFaceNodes.colentries[newBFaceNodes.colstart[bface]+j-1] = xFaceNodes[j,xBFaceFaces[bface]]
            else
                newBFaceNodes[j,bface] = xFaceNodes[j,xBFaceFaces[bface]]
            end
        end
    end
    xgrid[BFaceNodes] = newBFaceNodes

   # xgrid[BFaceGeometries] = xBFaceGeometries
    xBFaceFaces
end

function instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BEdgeNodes}) where {Tc,Ti}

    dim = size(xgrid[Coordinates],1)
    if dim==1
        xgrid[BEdgeEdges]=zeros(Ti,0)
        xgrid[BEdgeNodes]=zeros(Ti,0,0)
        return xgrid[BEdgeNodes]
    end

    if dim==2
        xgrid[BEdgeEdges] = xgrid[BFaceFaces]
        xgrid[BEdgeNodes] = xgrid[BFaceNodes]
        return xgrid[BEdgeNodes]
    end

    xBFaceFaces = xgrid[BFaceFaces]
    xEdgeNodes = xgrid[EdgeNodes]
    xFaceEdges = xgrid[FaceEdges]
    xBFaceGeometries = xgrid[BFaceGeometries]
    nbfaces = length(xBFaceFaces)
    xBEdgeEdges = zeros(Ti,0)

    EG = Triangle2D
    edge::Ti = 0
    face::Ti = 0
    nfaceedges::Ti = 0

    for bface = 1 : nbfaces
        EG = xBFaceGeometries[bface]
        nfaceedges = num_edges(EG)
        face = xBFaceFaces[bface]
        for k = 1 : nfaceedges
            edge = xFaceEdges[k,face]
            if !(edge in xBEdgeEdges)
                push!(xBEdgeEdges, edge)
            end

        end
    end

    nbedges = length(xBEdgeEdges)
    xBEdgeNodes = zeros(Ti,2,nbedges)
    for bedge = 1 : nbedges, k = 1 : 2
        xBEdgeNodes[k,bedge] = xEdgeNodes[k,xBEdgeEdges[bedge]]
    end

    xgrid[BEdgeEdges] = xBEdgeEdges
    xBEdgeNodes
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BEdgeEdges}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, BEdgeNodes)
    xgrid[BEdgeEdges]
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceCells}) where {Tc,Ti}
    ExtendableGrids.instantiate(xgrid, FaceNodes)
    xgrid[FaceCells]
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceRegions}) where {Tc,Ti}
    # interior faces get region number 0, boundary faces get their boundary region
    xBFaceFaces = xgrid[BFaceFaces]
    xBFaceRegions = xgrid[BFaceRegions]
    xFaceRegions = zeros(Ti,num_sources(xgrid[FaceNodes]))
    for j = 1 : length(xBFaceFaces)
        xFaceRegions[xBFaceFaces[j]] = xBFaceRegions[j]
    end
    xFaceRegions
end

### Not a good idea to have them as vectors of constants
# function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{EdgeRegions}) where {Tc,Ti}
#     return VectorOfConstants(Ti(0),num_sources(xgrid[EdgeNodes]))
# end

# function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BEdgeRegions}) where {Tc,Ti}
#     return VectorOfConstants(Ti(0),num_sources(xgrid[BEdgeNodes]))
# end

# function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BEdgeGeometries}) where {Tc,Ti}
#     return VectorOfConstants(Edge1D,num_sources(xgrid[BEdgeNodes]))
# end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{BFaceCellPos}) where {Tc,Ti}
    # gets the local position of a bface in the CellFaces row of its adjacent cell

    # get links to other stuff
    xCellFaces = xgrid[CellFaces]
    xFaceCells = xgrid[FaceCells]
    xBFaceFaces = xgrid[BFaceFaces]
    nbfaces = length(xBFaceFaces)

    # init BFaceFaces
    xBFaceCellPos = zeros(Ti,nbfaces)

    cface = 0
    cell = 0
    nfaces4cell = 0
    for bface = 1 : nbfaces
        cface = xBFaceFaces[bface]
        cell = xFaceCells[1,cface]
        nfaces4cell = num_targets(xCellFaces,cell)
        for face = 1 : nfaces4cell
            if cface == xCellFaces[face,cell]
                xBFaceCellPos[bface] = face
                break
            end
        end
    end

    xBFaceCellPos
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{FaceNormals}) where {Tc,Ti}
    dim::Int = size(xgrid[Coordinates],1)
    xCoordinates::Array{Tc,2} = xgrid[Coordinates]
    xFaceNodes::Adjacency{Ti} = xgrid[FaceNodes]
    nfaces::Int = num_sources(xFaceNodes)
    xFaceGeometries::GridEGTypes = xgrid[FaceGeometries]
    xCoordinateSystem = xgrid[CoordinateSystem]
    xFaceNormals::Array{Tc,2} = zeros(Tc,dim,nfaces)
    normal::Array{Tc,1} = zeros(Tc,dim)
    EG = xFaceGeometries[1]
    for face = 1 : nfaces
        EG = xFaceGeometries[face]
        Normal4ElemType!(normal,xCoordinates,xFaceNodes,face,EG,xCoordinateSystem)
        for k = 1 : dim
            xFaceNormals[k, face] = normal[k]
        end
    end
    xFaceNormals
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{EdgeTangents}) where {Tc,Ti}
    dim::Int = size(xgrid[Coordinates],1)
    xCoordinates::Array{Tc,2} = xgrid[Coordinates]
    xEdgeNodes::Adjacency{Ti} = xgrid[EdgeNodes]
    nedges::Int = num_sources(xEdgeNodes)
    xEdgeGeometries::GridEGTypes = xgrid[EdgeGeometries]
    xCoordinateSystem = xgrid[CoordinateSystem]
    xEdgeTangents::Array{Tc,2} = zeros(Tc,dim,nedges)
    EG = xEdgeGeometries[1]
    tangent::Array{Tc,1} = zeros(Tc,dim)
    for edge = 1 : nedges
        EG = xEdgeGeometries[edge]
        Tangent4ElemType!(tangent,xCoordinates,xEdgeNodes,edge,EG,xCoordinateSystem)
        for k = 1 : dim
            xEdgeTangents[k, edge] = tangent[k]
        end
    end
    xEdgeTangents
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{UniqueCellGeometries}) where {Tc,Ti}
    xUniqueCellGeometries = unique(xgrid[CellGeometries])
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{UniqueFaceGeometries}) where {Tc,Ti}
    xUniqueFaceGeometries = unique(xgrid[FaceGeometries])
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{UniqueBFaceGeometries}) where {Tc,Ti}
    xUniqueBFaceGeometries = unique(xgrid[BFaceGeometries])
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{UniqueEdgeGeometries}) where {Tc,Ti}
    xUniqueEdgeGeometries = unique(xgrid[EdgeGeometries])
end

function ExtendableGrids.instantiate(xgrid::ExtendableGrid{Tc,Ti}, ::Type{UniqueBEdgeGeometries}) where {Tc,Ti}
    xUniqueBEdgeGeometries = unique(xgrid[BEdgeGeometries])
end


#!/usr/bin/julia
doc = """Usage:
    dpd_sim.py [--L <L> --N <N> --steps <ns> --dt <dt> --thermo <th> --gamma <g>]
               [--read <xyz> --config <config>]

Options:
    --N <N>            Number of atoms [default: 500]
    --L <L>            Box size [default: 5]
    --steps <ns>       Number of steps [default: 300]
    --dt <dt>          Timestep [default: 0.01]
    --thermo <th>      Frequency of printing to screen and file [default: 10]
    --gamma <g>        Friction [default: 0.0]
    --read <xyz>       Read the xyz file
    --config <config>  Read the DL_MESO CONFIG file

08/07/17
"""
using DocOpt
include("dpd_functions.jl")
include("dpd_io.jl")
using dpd_functions.init_pos, dpd_functions.init_vel,
    dpd_functions.integrate_verlet, dpd_functions.integrate_euler

args = docopt(doc)
#for (k, v) in args; println(k, "  ", v); end
L = parse(Float64, args["--L"])
N = parse(Int, args["--N"])
dt = parse(Float64, args["--dt"])
Ns = parse(Int, args["--steps"])
thermo = parse(Int, args["--thermo"])
gamma = parse(Float64, args["--gamma"])
kT = 1.0
rc = 1.0

rho = N / L^3
seed = 1234
srand(seed)

@printf "===== DPD simulations =====\n"
@printf "Beads: %i | Box: %.2f | rho: %.2f | gamma: %.1f\n" N L rho gamma
@printf "Steps: %i | dt: %.3f | thermo: %i\n" Ns dt thermo

A = 25.0 * ones(2, 2)

if args["--read"] != nothing
    fname = args["--read"]
    println("Reading initial xyz from $fname...")
    blist, X = read_xyz(fname)
    N = size(X, 1)
    V = init_vel(N, kT)
elseif args["--config"] != nothing
    fname = args["--config"]
    println("Reading initial positions and velocities from $fname...")
    X, V = read_config(fname)
    N = size(X, 1)
    blist = ones(Int, N)

else
    println("Random initialisation of positions and velocities.")
    blist = ones(Int, N)
    X = init_pos(N, L)
    V = init_vel(N, kT)
end

ti = time()
#T, KE, PE = integrate_euler(X, V, A, blist, L, gamma, kT, dt, rc, Ns, thermo)
T, KE, PE = integrate_verlet(X, V, A, blist, L, gamma, kT, dt, rc, Ns, thermo)

println("Simulation finished.Time: $(time() - ti) s.")
writedlm("data.out", [1:Ns+1 T KE PE])


using AlphaGo

function translate_gtp_color(gtp_color)
  if lowercase(gtp_color) ∈ ["b", "black"] return go.BLACK end
  if lowercase(gtp_color) ∈ ["w", "white"] return go.WHITE end
  throw(error("invalid color $gtp_color")
end

# GTP command handler for basic play commands
mutable struct GTPHandler
  _komi
  _player
  _courtesy_pass

  function GTPHandler(player, courtesy_pass = false)
    new(6.5, player, courtesy_pass)
  end
end

function cmd_boardsize(gtp_handler::GTPHandler, n::Int)
  n == go.N ? nothing : throw(AssertionError("unsupported board size: $n"))
end

cmd_clear_board(gtp_handler::GTPHandler) =
          initialize_game!(gtp_handler._player, go.Position(komi = self._komi))

cmd_komi(gtp_handler::GTPHandler, komi::Float32)
  gtp_handler._komi = komi
  get_position(gtp_handler._player).komi = komi

function cmd_play(gtp_handler::GTPHandler, arg0::String, arg1 = nothing)
  if arg1 == nothing
    move = arg0
  else
    _accomodate_out_of_turn(gtp_handler, translate_gtp_color(arg0))
    move = arg1
  end
  return play_move!(gtp_handler._player, go.from_kgs(move))
end

function cmd_genmove(gtp_handler::GTPHandler, color = nothing)
  if color is not None:
    self._accomodate_out_of_turn(color)
  end

  if gtp_handler._courtesy_pass
    # If courtesy pass is True and the previous move was a pass, we'll
    # pass too, regardless of score or our opinion on the game.
    pos = get_position(gtp_handler._player)
    if pos.recent && position.recent[end].move == nothing
      return "pass"
    end
  end

  move = suggest_move(gtp_handler._player)
  if gtp_handler._player.should_resign()
    set_result!(gtp_handler._player, -get_position(gtp_handler._player).to_play,
                            was_resign = true)
    return "resign"
  end

  play_move(gtp_handler._player, move)
  if gtp_handler._player.root.is_done()
    set_result!(result(get_position(gtp_handler._player)), was_resign = false)
  end
  return go.to_kgs(move)
end

cmd_undo(get_position(gtp_handler) = throw(error("Not Implemented"))

cmd_final_score(gtp_handler) = gtp_handler._player.result_string

function _accomodate_out_of_turn(gtp_handler::GTPHandler, color::String)
  pos = get_position(gtp_handler._player)
  if translate_gtp_color(color) != pos.to_play
    flip_playerturn!(pos; mutate = true)
  end
end

mutable struct KGSHandler
  _player
end

function cmd_showboard(kgs_handler::KGSHandler)
  println("\n\n" * string(get_position(kgs_handler._player)) * "\n\n")
  return true
end


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

# Scenario-decomposition generation #
# ================================= #
function generate!(stochasticprogram::StochasticProgram{N}, structure::ScenarioDecompositionStructure{N}) where N
    # Generate all stages
    for stage in 2:N
        generate!(stochasticprogram, structure, stage)
    end
    return nothing
end

function generate!(stochasticprogram::TwoStageStochasticProgram, structure::ScenarioDecompositionStructure{2}, stage::Integer)
    stage == 2 || error("Stage $stage not available in two-stage model.")
    # Check generators
    has_generator(stochasticprogram, :stage_1) || error("First-stage problem not defined in stochastic program. Consider @stage 1.")
    has_generator(stochasticprogram, :stage_2) || error("Second-stage problem not defined in stochastic program. Consider @stage 2.")
    # Sanity check on scenario probabilities
    if num_scenarios(structure, stage) > 0
        p = stage_probability(structure, stage)
        abs(p - 1.0) <= 1e-6 || @warn "Scenario probabilities do not add up to one. The probability sum is given by $p"
    end
    # Generate
    generate_horizontal!(scenarioproblems(structure, stage),
                         generator(stochasticprogram, :stage_1),
                         generator(stochasticprogram, :stage_2),
                         stage_parameters(stochasticprogram, 1),
                         stage_parameters(stochasticprogram, 2),
                         structure.decisions[stage-1],
                         subproblem_optimizer(stochasticprogram))
    return nothing
end

function generate_horizontal!(scenarioproblems::ScenarioProblems,
                              stage_one_generator::Function,
                              stage_two_generator::Function,
                              stage_one_params::Any,
                              stage_two_params::Any,
                              decision_map::DecisionMap,
                              optimizer)
    for i in num_subproblems(scenarioproblems)+1:num_scenarios(scenarioproblems)
        push!(scenarioproblems.problems, _WS(stage_one_generator,
                                             stage_two_generator,
                                             stage_one_params,
                                             stage_two_params,
                                             scenario(scenarioproblems,i),
                                             decision_map,
                                             DecisionMap(),
                                             optimizer))
    end
    return nothing
end
function generate_horizontal!(scenarioproblems::DistributedScenarioProblems,
                              stage_one_generator::Function,
                              stage_two_generator::Function,
                              stage_one_params::Any,
                              stage_two_params::Any,
                              ::DecisionMap,
                              optimizer)
    @sync begin
        for w in workers()
            @async remotecall_fetch(
                w,
                scenarioproblems[w-1],
                stage_one_generator,
                stage_two_generator,
                stage_one_params,
                stage_two_params,
                scenarioproblems.decisions[w-1],
                optimizer) do sp, gen_one, gen_two, one_params, two_params, decisions, opt
                    generate_horizontal!(fetch(sp),
                                         gen_one,
                                         gen_two,
                                         one_params,
                                         two_params,
                                         fetch(decisions),
                                         opt)
                end
        end
    end
    return nothing
end

function clear!(structure::ScenarioDecompositionStructure{N}) where N
    # Clear decisions
    clear!(structure.decisions)
    # Clear all stages
    for stage in 2:N
        clear_stage!(structure, stage)
    end
    return nothing
end

function clear_stage!(structure::ScenarioDecompositionStructure{N}, s::Integer) where N
    1 <= s <= N || error("Stage $s not in range 1 to $N.")
    clear!(scenarioproblems(structure, s))
    return nothing
end


export example_domain, init_nnfem, example_global_data, example_static_domain1, find_boundary

@doc raw"""
init_nnfem(domain::Domain)

Prepares `domain` for use in custom operators.
"""
function init_nnfem(domain::Domain)
    LIB = joinpath(@__DIR__, "../../deps/CustomOp/build/libDataLib")

    if Sys.iswindows()
        LIB = joinpath(@__DIR__, "../../deps/CustomOp/build/DataLib")
    end

    LIB = abspath(LIB)
    tf.load_op_library(LIB*"."*dlext) # ensure that the tensorflow library is first loaded

    @eval begin
        ccall((:init_mesh, $LIB), Cvoid, ())

        for (iele,e) in enumerate($domain.elements)

            el_eqns = getEqns($domain, iele)
            el_eqns_active = findall(el_eqns .>= 1)
            ccall((:create_mesh, $LIB), Cvoid,
            (
            Ptr{Cint}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Cint, Cint,
            Ptr{Cdouble}, Cint, Ptr{Cint}
            ), Int32.(e.elnodes[:]), e.coords[:], vcat([x[:] for x in e.dhdx]...), e.weights, hcat([x[:] for x in e.hs]...), Int32(length(e.elnodes)), Int32(length(e.weights)),
            Int32.(el_eqns_active), Int32(length(el_eqns_active)), Int32.(el_eqns))
        end

        ccall((:create_domain, $LIB), Cvoid,
        (
        Ptr{Cdouble}, Cint, Cint, Cint
        ), $domain.nodes[:], Int32.($domain.nnodes), Int32.($domain.neqs), Int32.($domain.neles))
        ccall((:print_mesh, $LIB), Cvoid, ())

    end
end

function example_domain(m::Int64 = 10, n::Int64 = 10, h::Float64 = 1.0)
    # Create a very simple mesh
    elements = []
    prop = Dict("name"=> "PlaneStrain", "rho"=> 1.0, "E"=> 2.0, "nu"=> 0.35)
    coords = zeros((m+1)*(n+1), 2)
    for j = 1:n
        for i = 1:m
            idx = (m+1)*(j-1)+i
            elnodes = [idx; idx+1; idx+1+m+1; idx+m+1]
            ngp = 2
            nodes = [
            (i-1)*h (j-1)*h
            i*h (j-1)*h
            i*h j*h
            (i-1)*h j*h
            ]
            coords[elnodes, :] = nodes
            push!(elements, SmallStrainContinuum(nodes, elnodes, prop, ngp))
        end
    end

    # fixed on the bottom, push on the right
    EBC = zeros(Int64, (m+1)*(n+1), 2)
    FBC = zeros(Int64, (m+1)*(n+1), 2)
    g = zeros((m+1)*(n+1), 2)
    f = zeros((m+1)*(n+1), 2)

    for i = 2:m
        idx = n*(m+1)+i
        FBC[idx,:] .= -1
        FBC[i,:] .= -1
    end

    for j = 1:n+1
        idx = (j-1)*(m+1)+1
        FBC[idx,:] .= -1
        idx = (j-1)*(m+1)+m+1
        FBC[idx,:] .= -1
    end

    ndims = 2
    domain = Domain(coords, elements, ndims, EBC, g, FBC, f)
end

function example_static_domain1(m::Int64 = 10, n::Int64 = 10, h::Float64 = 1.0)
    # Create a very simple mesh
    elements = []
    prop = Dict("name"=> "Scalar1D", "kappa"=>1.0 )
    coords = zeros((m+1)*(n+1), 2)
    for j = 1:n
        for i = 1:m
            idx = (m+1)*(j-1)+i
            elnodes = [idx; idx+1; idx+1+m+1; idx+m+1]
            ngp = 2
            nodes = [
                (i-1)*h (j-1)*h
                i*h (j-1)*h
                i*h j*h
                (i-1)*h j*h
            ]
            coords[elnodes, :] = nodes
            push!(elements, SmallStrainContinuum(nodes, elnodes, prop, ngp))
        end
    end

    # free boundary on all sides
    EBC = zeros(Int64, (m+1)*(n+1))
    FBC = zeros(Int64, (m+1)*(n+1))
    g = zeros((m+1)*(n+1))
    f = zeros((m+1)*(n+1))

    for j = 1:n+1
        FBC[(j-1)*(m+1) + 1] = -1
        FBC[(j-1)*(m+1) + m + 1] = -1
    end

    for i = 1:m+1
        FBC[  i ] = -1
        FBC[ n*(m+1) + i ] = -1
    end

    ndims = 1
    domain = StaticDomain1(coords, elements, EBC, g, FBC, f)
end

"""
    example_global_data(domain::Domain)

Returns an examplary global data structure. Initial displacement, velocity, and acceleration are zero. Time-dependent displacement and force function are set to `missing`.
"""
function example_global_data(domain::Domain)
    Dstate = zeros(domain.neqs)
    state = zeros(domain.neqs)
    velo = zeros(domain.neqs)
    acce = zeros(domain.neqs)
    gt = nothing
    ft = nothing
    globdat = GlobalData(state, Dstate, velo, acce, domain.neqs, gt, ft)
end

"""
    find_boundary(nodes::Array{Float64, 2}, elements::Array{Int64, 2})

Given nodes and elements arrays, this function returns the boundary indices.
"""
function find_boundary(nodes::Array{Float64, 2}, elements::Array{Int64, 2})
    ef = (i, j)->(min(i, j), max(i, j))
    push_pop! = x -> (x in eset ? pop!(eset, x) : push!(eset, x))
    eset = Set([])
    for k = 1:size(elements, 1)
        push_pop!(ef(elements[k,1], elements[k,2]))
        push_pop!(ef(elements[k,2], elements[k,3]))
        push_pop!(ef(elements[k,3], elements[k,4]))
        push_pop!(ef(elements[k,4], elements[k,1]))
    end
    nset = Set(Int64[])
    for e in collect(eset)
        push!(nset, e[1])
        push!(nset, e[2])
    end
    return collect(nset)
end


module DeepQLearning

using Random
using StatsBase
using Printf
using Parameters
using Flux
using BSON
using POMDPModelTools
using POMDPPolicies
using POMDPLinter
using LinearAlgebra
using TensorBoardLogger: TBLogger, log_value
using EllipsisNotation

using CommonRLInterface: AbstractEnv, reset!, actions, observe, act!, terminated
using POMDPs

export DeepQLearningSolver,
       AbstractNNPolicy,
       NNPolicy,
       getnetwork,
       resetstate!,
       DQExperience,
       restore_best_model,
       PrioritizedReplayBuffer,
       EpisodeReplayBuffer,

       # helpers
       flattenbatch,
       huber_loss,
       isrecurrent,
       batch_trajectories

include("helpers.jl")
include("policy.jl")
include("exploration_policy.jl")
include("evaluation_policy.jl")
include("prioritized_experience_replay.jl")
include("episode_replay.jl")
include("dueling.jl")
include("solver.jl")

end # module DeepQLearning


#!/usr/bin/env julia

using MIToS.Utils.Scripts

Args = parse_commandline(
# TO DO -----------------------------------------------------------------------
    description="""
    Creates a file in Stockholm format with the aligned columns from a Pfam Stockholm file.
    Insertions are deleted, as they are unaligned in a proﬁle HMM.
    The output file *.aligned.* contains UniProt residue numbers and original column numbers in its annotations.
    """,
    output=".aligned."
# -----------------------------------------------------------------------------
    )

set_parallel(Args["parallel"])

@everywhere begin

    const args = remotecall_fetch(1,()->Args)

    import MIToS.Utils.Scripts: script

    # TO DO -----------------------------------------------------------------------
    using MIToS.MSA

    function script(input::Union{Base.LibuvStream,  AbstractString},
                    args,
                    fh_out::Union{Base.LibuvStream, IO})
        try
            aln = readorparse(input, Stockholm, generatemapping=true, useidcoordinates=true, deletefullgaps=true)
            print(fh_out, aln, Stockholm)
        catch err
            warn(string("ERROR for ", input, ": ", err))
        end

    end
    # -----------------------------------------------------------------------------

end

runscript(args)


module jcontrol
# using Term
# install_term_logger()

using Plots
using Interpolations

include("utils.jl")
include("kinematics.jl")
include("track.jl")
include("bike.jl")
include("control.jl")
include("forward_model.jl")
include("comparisons.jl")
include("run.jl")
include("trial.jl")
include("visuals.jl")
include("io.jl")

export Track, get_track_borders, FULLTRACK
export State, Bicycle
export ControlOptions, create_and_solve_control, Bounds
export Solution, run_forward_model
export summary_plot
export PATHS, load_trials, load_cached_trials
export arena
export DynamicsProblem, KinematicsProblem, realistict_control_options
export get_comparison_points
export run_mtm
export Trial, trimtrial, get_varvalue_at_frameidx

using .bicycle: State, Bicycle
using .control:
    ControlOptions,
    create_and_solve_control,
    Bounds,
    State,
    DynamicsProblem,
    KinematicsProblem,
    realistict_control_options

using .forwardmodel: Solution, run_forward_model
using .comparisons: ComparisonPoints, ComparisonPoint, track_segments, TrackSegment
using .visuals
using .Run: run_mtm
using .trial: Trial, trimtrial, get_varvalue_at_frameidx
using .io: PATHS, load_trials, load_cached_trials

end # module


###
### LongSequence specific specializations of src/biosequence/transformations.jl
###

"""
    resize!(seq, size, [force::Bool])

Resize a biological sequence `seq`, to a given `size`. Does not resize the underlying data
array unless the new size does not fit. If `force`, always resize underlying data array.
"""
function Base.resize!(seq::LongSequence{A}, size::Integer, force::Bool=false) where {A}
    if size < 0
        throw(ArgumentError("size must be non-negative"))
    elseif seq.shared
        # May seem wasteful to orphan here, but if you resize! a shared seq,
        # you're probably going to write to it anyway.
        return _orphan!(seq, size)
    else
        if force | (seq_data_len(A, size) > seq_data_len(A, length(seq)))
            resize!(seq.data, seq_data_len(A, size))
        end
        seq.part = 1:size
        return seq
    end
end

function Base.filter!(f::Function, seq::LongSequence{A}) where {A}
    orphan!(seq)

    len = 0
    next = bitindex(seq, 1)
    j = index(next)
    datum::UInt64 = 0
    for i in 1:lastindex(seq)
        x = inbounds_getindex(seq, i)
        if f(x)
            datum |= enc64(seq, x) << offset(next)
            len += 1
            #TODO: Resolve use of bits_per_symbol.
            next += bits_per_symbol(A())
            if index(next) != j
                seq.data[j] = datum
                datum = 0
                j = index(next)
            end
        end
    end
    if offset(next) > 0
        seq.data[j] = datum
    end
    resize!(seq, len)

    return seq
end

function Base.map!(f::Function, seq::LongSequence)
    orphan!(seq)
    for i in 1:lastindex(seq)
        unsafe_setindex!(seq, f(inbounds_getindex(seq, i)), i)
    end
    return seq
end

"""
    reverse!(seq::LongSequence)

Reverse a biological sequence `seq` in place.
"""
Base.reverse!(seq::LongSequence{<:Alphabet}) = _reverse!(orphan!(seq), BitsPerSymbol(seq))

"""
    reverse(seq::LongSequence)

Create reversed copy of a biological sequence.
"""
Base.reverse(seq::LongSequence{<:Alphabet}) = _reverse(orphan!(seq), BitsPerSymbol(seq))

# Fast path for non-inplace reversion
@inline function _reverse(seq::LongSequence{A}, B::BT) where {A <: Alphabet,
    BT <: Union{BitsPerSymbol{2}, BitsPerSymbol{4}, BitsPerSymbol{8}}}
    cp = LongSequence{A}(unsigned(length(seq)))
    reverse_data_copy!(identity, cp.data, seq.data, B)
    return zero_offset!(cp)
end

_reverse(seq::LongSequence{<:Alphabet}, ::BitsPerSymbol) = reverse!(copy(seq))

# Generic fallback
function _reverse!(seq::LongSequence{<:Alphabet}, ::BitsPerSymbol)
    i, j = 1, lastindex(seq)
    @inbounds while i < j
        seq[i], seq[j] = seq[j], seq[i]
        i += 1
        j -= 1
    end
    return seq
end

@inline function _reverse!(seq::LongSequence{<:Alphabet}, B::BT) where {
    BT <: Union{BitsPerSymbol{2}, BitsPerSymbol{4}, BitsPerSymbol{8}}}
    reverse_data!(identity, seq.data, B)
    return zero_offset!(seq)
end

# Reversion of chunk bits may have left-shifted data in chunks, this function right shifts
# all chunks by up to 63 bits. Only works on orphan sequences.
# This is written so it SIMD parallelizes - careful with changes
@inline function zero_offset!(seq::LongSequence{A}) where A <: Alphabet
    lshift = offset(bitindex(seq, last(seq.part)) + bits_per_symbol(A()))
    rshift = 64 - lshift
    len = length(seq.data)
    @inbounds if !iszero(lshift)
        this = seq.data[1]
        for i in 1:len-1
            next = seq.data[i+1]
            seq.data[i] = (this >>> (unsigned(rshift) & 63)) | (next << (unsigned(lshift) & 63))
            this = next
        end
        seq.data[len] >>>= (unsigned(rshift) & 63)
    end
    return seq
end

# Reverse chunks in data vector and each symbol within a chunk. Chunks may have nonzero
# offset after use, so use zero_offset!
@inline function reverse_data!(pred, data::Vector{UInt64}, B::BT) where {
    BT <: Union{BitsPerSymbol{2}, BitsPerSymbol{4}, BitsPerSymbol{8}}}
    len = length(data)
    @inbounds @simd ivdep for i in 1:len >>> 1
        data[i], data[len-i+1] = pred(reversebits(data[len-i+1], B)), pred(reversebits(data[i], B))
    end
    @inbounds if isodd(len)
        data[len >>> 1 + 1] = pred(reversebits(data[len >>> 1 + 1], B))
    end
end

@inline function reverse_data_copy!(pred, dst::Vector{UInt64}, src::Vector{UInt64}, B::BT) where {
    BT <: Union{BitsPerSymbol{2}, BitsPerSymbol{4}, BitsPerSymbol{8}}}
    len = length(dst)
    @inbounds @simd for i in eachindex(dst)
        dst[i] = pred(reversebits(src[len - i + 1], B))
    end
end

"""
    complement!(seq)

Make a complement sequence of `seq` in place.
"""
function complement!(seq::LongSequence{A}) where {A<:NucleicAcidAlphabet}
    orphan!(seq)
    next = index(firstbitindex(seq))
    stop = index(lastbitindex(seq))
    seqdata = seq.data
    @inbounds for i in index(firstbitindex(seq)):index(lastbitindex(seq))
        seqdata[i] = complement_bitpar(seqdata[i], Alphabet(seq))
    end
    return seq
end

function reverse_complement!(seq::LongSequence{<:NucleicAcidAlphabet})
    orphan!(seq)
    pred = x -> complement_bitpar(x, Alphabet(seq))
    reverse_data!(pred, seq.data, BitsPerSymbol(seq))
    return zero_offset!(seq)
end

function reverse_complement(seq::LongSequence{<:NucleicAcidAlphabet})
    cp = typeof(seq)(unsigned(length(seq)))
    pred = x -> complement_bitpar(x, Alphabet(seq))
    reverse_data_copy!(pred, cp.data, seq.data, BitsPerSymbol(seq))
    return zero_offset!(cp)
end

###
### Shuffle
###

function Random.shuffle!(seq::LongSequence)
    orphan!(seq) # TODO: Is this call to orphan nessecery, given setindex should call `orphan!` for us?
    # Fisher-Yates shuffle
    for i in 1:lastindex(seq) - 1
        j = rand(i:lastindex(seq))
        seq[i], seq[j] = seq[j], seq[i]
    end
    return seq
end


function thres_show_cb(widget::Ptr,user_data::Tuple{SpikeSorting.Single_Channel})

    sc, = user_data
    mywidget = convert(CheckButton, widget)
    sc.show_thres=getproperty(mywidget,:active,Bool)
    sc.old_thres=getproperty(sc.adj_thres,:value,Int)
    sc.thres=getproperty(sc.adj_thres,:value,Int)

    nothing
end

function thres_cb(widget::Ptr,user_data::Tuple{SpikeSorting.Single_Channel})

    sc,  = user_data

    mythres=getproperty(sc.adj_thres,:value,Int)
    setproperty!(sc.thres_widgets.sb,:label,string(mythres))
    sc.thres_changed=true

    nothing
end

function gain_check_cb(widget::Ptr,user_data::Tuple{SpikeSorting.Single_Channel})

    sc, = user_data

    mygain=getproperty(sc.gain_widgets.multiply,:active,Bool)

    if mygain
        Gtk.GAccessor.increments(sc.gain_widgets.gainbox,10,10)
    else
        Gtk.GAccessor.increments(sc.gain_widgets.gainbox,1,1)
    end

    nothing
end

function sb2_cb(widget::Ptr,user_data::Tuple{Single_Channel})

    sc, = user_data

    mygain=getproperty(sc.gain_widgets.all,:active,Bool)

    gainval=getproperty(sc.gain_widgets.gainbox,:value,Int)
    mythres=getproperty(sc.adj_thres,:value,Int)

    if mygain==true
    else
    end

    sc.s = -1 .* gainval/1000

    sc.thres_changed=true

    nothing
end


function satisfy(interpreter::AbstractInterpreter,
                 domain::Domain, state::GenericState,
                 terms::AbstractVector{<:Term})
     # Quick check that avoids SLD resolution unless necessary
     sat = all(check(interpreter, domain, state, to_nnf(t)) for t in terms)
     if sat !== missing return sat end
     # Call SLD resolution only if there are free variables or axioms
     return !isempty(satisfiers(interpreter, domain, state, terms))
end

function satisfy(interpreter::AbstractInterpreter,
                 domain::Domain, state::GenericState, term::Term)
    # Convert to NNF
    term = to_nnf(term)
    # Quick check that avoids SLD resolution unless necessary
    sat = check(interpreter, domain, state, term)
    if sat !== missing return sat end
    # Call SLD resolution only if there are free variables or axioms
    return !isempty(satisfiers(interpreter, domain, state, term))
end

function satisfiers(interpreter::AbstractInterpreter,
                    domain::Domain, state::GenericState,
                    terms::AbstractVector{<:Term})
    # Reify negations
    terms = [reify_negations(to_nnf(t), domain) for t in terms]
    # Initialize Julog knowledge base
    clauses = Clause[get_clauses(domain); get_negation_clauses(domain);
                     collect(state.types); collect(state.facts)]
    # Pass in fluents and function definitions as a dictionary of functions
    funcs = merge(GLOBAL_PREDICATES, state.values, get_funcdefs(domain))
    return resolve(collect(terms), clauses; funcs=funcs, mode=:all)[2]
end

function satisfiers(interpreter::AbstractInterpreter,
                    domain::Domain, state::GenericState, term::Term)
    return satisfiers(domain, state, [term])
end

function check(interpreter::AbstractInterpreter,
               domain::Domain, state::GenericState, term::Compound)
    sat = if term.name == :and
        all(check(interpreter, domain, state, a) for a in term.args)
    elseif term.name == :or
        any(check(inte, domain, state, a) for a in term.args)
    elseif term.name == :imply
        !check(interpreter, domain, state, term.args[1]) |
        check(interpreter, domain, state, term.args[2])
    elseif term.name == :not
        !check(interpreter, domain, state, term.args[1]) |
        check(interpreter, domain, state, negate(term.args[1]))
    elseif term.name == :forall
        missing
    elseif term.name == :exists
        missing
    elseif !is_ground(term)
        missing
    elseif is_derived(term, domain)
        missing
    elseif is_type(term, domain)
        if has_subtypes(term, domain)
            missing
        elseif term in state.types
            true
        elseif !isempty(get_constants(domain))
            domain.constypes[term.args[1]] == term.name
        else
            false
        end
    elseif is_global_pred(term)
        evaluate(interpreter, domain, state, term)::Bool
    elseif is_func(term, domain)
        evaluate(interpreter, domain, state, term)::Bool
    else
        term = partialeval(domain, state, term)
        term in state.facts
    end
    return sat
end

function check(interpreter::AbstractInterpreter,
               domain::Domain, state::GenericState, term::Const)
    if term in state.facts || term in state.types || term.name == true
        return true
    elseif is_func(term, domain) || is_derived(term, domain)
        return missing
    else
        return false
    end
end

function check(interpreter::AbstractInterpreter,
               domain::Domain, state::GenericState, term::Var)
    return missing
end


using ABM
using Random
using Distributions
mutable struct BoidData
    name::String
    pos::Real2D
    orientation::Real2D
end
function fstep(state::SimState, agent::Agent)
end
pos = Real2D(rand(Uniform(0, 100)), rand(Uniform(0, 100)))
d = BoidData("Boid", pos, Real2D(rand(Uniform(0, 100)),rand(Uniform(0, 100))))
a = Agent(fstep,d)

dict1 = Dict{Int64,Int64}()
array1 = Array{Int64,1}()

dict2 = Dict{Agent,Int64}()
array2 = Array{Agent,1}()

set1 = Set{Int64}()
set2 = Set{Agent}()

println("------------------------------")

@time for i in 1:1000000
    pos = Real2D(rand(Uniform(0, 100)), rand(Uniform(0, 100)))
    d = BoidData("Boid", pos, Real2D(rand(Uniform(0, 100)),rand(Uniform(0, 100))))
    a = Agent(fstep,d)
    push!(array2, a)
end

@time for i in 1:1000000
    pos = Real2D(rand(Uniform(0, 100)), rand(Uniform(0, 100)))
    d = BoidData("Boid", pos, Real2D(rand(Uniform(0, 100)),rand(Uniform(0, 100))))
    a = Agent(fstep,d)
    push!(dict2, a=>i)
end

@time for i in 1:1000000
    pos = Real2D(rand(Uniform(0, 100)), rand(Uniform(0, 100)))
    d = BoidData("Boid", pos, Real2D(rand(Uniform(0, 100)),rand(Uniform(0, 100))))
    a = Agent(fstep,d)
    push!(set2, a)
end

@time for x in values(dict2)
end

@time for x in values(set2)
end

@time for x in array2
end