text:
#! /usr/bin/env julia

using Benchmark
using PolygonClipping
using DataFrames

function intersection1()
    polya = Polygon()
    push!(polya, Vertex(0.38717397323666614,0.9175286859623484))
    push!(polya, Vertex(0.22863911382759805,0.97369797312849))
    push!(polya, Vertex(0.411735284885411,0.67925592862104))
    push!(polya, Vertex(0.4760881945244875,0.6150149154240558))
    push!(polya, Vertex(0.05897662471214904,0.8393947489758318))
    push!(polya, Vertex(0.1138425905192968,0.4935408034874309))
    push!(polya, Vertex(0.5369414505688628,0.20213738997953312))
    push!(polya, Vertex(0.8894594154609499,0.9924712388095045))
    push!(polya, Vertex(0.9256610367848219,0.9190955722799556))
    push!(polya, Vertex(0.6895872059237731,0.43384802891551))
    push!(polya, Vertex(0.9087449635347744,0.47063971708226404))
    push!(polya, Vertex(0.9757793404848136,0.7322250783012572))
    push!(polya, Vertex(0.12339214488107642,0.11118441062092432))
    push!(polya, Vertex(0.6140766278925023,0.7892323008875815))
    push!(polya, Vertex(0.9080611442010653,0.8482511305518234))
    push!(polya, Vertex(0.10274769769723902,0.38869064624120186))
    push!(polya, Vertex(0.6082870387345407,0.9944088302986258))
    push!(polya, Vertex(0.3641405100568089,0.6359528425953462))
    push!(polya, Vertex(0.6835875991611398,0.6110124950747555))
    push!(polya, Vertex(0.7131748261757693,0.8537241667688524))
    push!(polya, Vertex(0.47313568651228555,0.84090661977734))
    push!(polya, Vertex(0.6122874995061984,0.4397916059818694))
    push!(polya, Vertex(0.9378622844157258,0.6928400362996152))
    push!(polya, Vertex(0.37988141528846486,0.3924066347636528))
    push!(polya, Vertex(0.37612785711290586,0.1262573371450586))
    push!(polya, Vertex(0.2855066522487957,0.34807448520950257))
    push!(polya, Vertex(0.3226665347220963,0.7530552496358307))
    push!(polya, Vertex(0.6260310508679137,0.9624257683012829))
    push!(polya, Vertex(0.5090596161323337,0.6293681446635448))
    push!(polya, Vertex(0.15373073711802787,0.699604530492266))
    push!(polya, Vertex(0.5916127221754168,0.5134368361443975))
    push!(polya, Vertex(0.5476501761702322,0.12025320226897085))
    push!(polya, Vertex(0.7364982683875068,0.6573742771739695))
    push!(polya, Vertex(0.3564770108133659,0.6190436269899608))
    push!(polya, Vertex(0.1942877121254134,0.7460859067795749))
    push!(polya, Vertex(0.48141346512614325,0.33226336281429103))
    push!(polya, Vertex(0.029892986776481,0.2174442631248703))
    push!(polya, Vertex(0.6110877370822994,0.6597597834785822))
    push!(polya, Vertex(0.6492858228562519,0.6866871593335842))
    push!(polya, Vertex(0.7810487364244136,0.6630376297746976))
    push!(polya, Vertex(0.7517037010056422,0.5481097354087443))
    push!(polya, Vertex(0.32595306165531923,0.827698654052716))
    push!(polya, Vertex(0.0946562934579609,0.06511398366340826))
    push!(polya, Vertex(0.4016822086257372,0.2633273020831326))
    push!(polya, Vertex(0.14840039175150355,0.26329155785679026))
    push!(polya, Vertex(0.660892803459532,0.14824001535942988))
    push!(polya, Vertex(0.7270462350880156,0.9616603642178803))
    push!(polya, Vertex(0.20565290637531142,0.45438554764185524))
    push!(polya, Vertex(0.01226767618359137,0.4346542090688914))
    push!(polya, Vertex(0.8443656165997122,0.6218309213080848))
    push!(polya, Vertex(0.36543895623038214,0.575693893269152))
    push!(polya, Vertex(0.2057450566974688,0.41382632848748657))
    push!(polya, Vertex(0.6578438918849019,0.018731630167831304))
    push!(polya, Vertex(0.9251751209532304,0.27030612773533713))
    push!(polya, Vertex(0.3698114785252209,0.08714925463572976))
    push!(polya, Vertex(0.8310050243916558,0.19245735232136196))
    push!(polya, Vertex(0.09311535434229778,0.5708872306538855))
    push!(polya, Vertex(0.2275489167423912,0.10333866005215286))
    push!(polya, Vertex(0.5691689464441063,0.07917751066510315))
    push!(polya, Vertex(0.9441999955891076,0.08863956272995877))
    push!(polya, Vertex(0.06944908047821619,0.9580858557547942))
    push!(polya, Vertex(0.13317769494718235,0.30579396918400126))
    push!(polya, Vertex(0.8916959800005415,0.49287891827534636))
    push!(polya, Vertex(0.05313909148370777,0.568467861010775))
    push!(polya, Vertex(0.8839793776888005,0.3922614940305833))
    push!(polya, Vertex(0.29947054407245455,0.13067176985025442))
    push!(polya, Vertex(0.6412436143042177,0.05964508351161513))
    push!(polya, Vertex(0.9907882336943714,0.18490674849468958))
    push!(polya, Vertex(0.3164116203917908,0.6915262366567434))
    push!(polya, Vertex(0.670340857966748,0.2494086348537079))
    push!(polya, Vertex(0.9516002726498964,0.5877410895223771))
    push!(polya, Vertex(0.37859268972845683,0.5498068716998208))
    push!(polya, Vertex(0.4585342971405306,0.009925253379711174))
    push!(polya, Vertex(0.825358328321574,0.02436860679566233))
    push!(polya, Vertex(0.2944481729419708,0.35565821826071664))
    push!(polya, Vertex(0.9479518990808133,0.022876736181673296))
    push!(polya, Vertex(0.32890511871298855,0.5903117675591205))
    push!(polya, Vertex(0.13434078534002403,0.8043761349960912))
    push!(polya, Vertex(0.36305742044837475,0.8074864926312384))
    push!(polya, Vertex(0.37006152533035075,0.810010072925335))
    push!(polya, Vertex(0.30245125983035126,0.531982907605898))
    push!(polya, Vertex(0.6061475382439927,0.3591482019894219))
    push!(polya, Vertex(0.4318224796352559,0.6906958337163789))
    push!(polya, Vertex(0.5484989647598377,0.5979198296052988))
    push!(polya, Vertex(0.3283124258298571,0.550335718203822))
    push!(polya, Vertex(0.731217871868109,0.694746265475011))
    push!(polya, Vertex(0.33503956272666824,0.7135843285873211))
    push!(polya, Vertex(0.4458879602562089,0.8271969180995566))
    push!(polya, Vertex(0.7868344276375905,0.9746067374413514))
    push!(polya, Vertex(0.12173043080454882,0.8576124078521512))
    push!(polya, Vertex(0.5907417299265332,0.6830068306826631))
    push!(polya, Vertex(0.2927496535507432,0.9373149390911051))
    push!(polya, Vertex(0.8550155012852447,0.9303058527010857))
    push!(polya, Vertex(0.30123570126170174,0.5735420574883527))
    push!(polya, Vertex(0.8967395912778122,0.018901254929095))
    push!(polya, Vertex(0.6950306441092162,0.32167381094779923))
    push!(polya, Vertex(0.8612213482620878,0.8954192932054237))
    push!(polya, Vertex(0.755007334348081,0.6441892302099188))
    push!(polya, Vertex(0.5129282217003186,0.6163889826956628))
    push!(polya, Vertex(0.5956877205258515,0.207078358084255))

    polyb = Polygon()
    push!(polyb, Vertex(0.45922312991074965,0.8634708765893035))
    push!(polyb, Vertex(0.3432506505322517,0.7610444169585342))
    push!(polyb, Vertex(0.7745733205429546,0.6432459662157686))
    push!(polyb, Vertex(0.6626248852675782,0.3756202023478652))
    push!(polyb, Vertex(0.11391157584189071,0.1175951719793249))
    push!(polyb, Vertex(0.47290352518324696,0.2521388807609466))
    push!(polyb, Vertex(0.975042508309663,0.31523396145745664))
    push!(polyb, Vertex(0.6645972006883816,0.39747175992018424))
    push!(polyb, Vertex(0.4033012612046003,0.6697684324194775))
    push!(polyb, Vertex(0.7478015750621811,0.26064185515728866))
    push!(polyb, Vertex(0.5408637693590228,0.6848821125508033))
    push!(polyb, Vertex(0.20715997569592037,0.6570627238206466))
    push!(polyb, Vertex(0.23093726175916385,0.8287208212251969))
    push!(polyb, Vertex(0.6024572633721841,0.8204147304496012))
    push!(polyb, Vertex(0.7025482053945702,0.6316840227377571))
    push!(polyb, Vertex(0.15103563731064762,0.5804190220758261))
    push!(polyb, Vertex(0.9887757894210867,0.9772711166739021))
    push!(polyb, Vertex(0.6251517614575921,0.38220971314131513))
    push!(polyb, Vertex(0.23163449016202686,0.7955298748003772))
    push!(polyb, Vertex(0.008749232698707532,0.7038966642300737))
    push!(polyb, Vertex(0.36552065983260706,0.007266458811545862))
    push!(polyb, Vertex(0.8431274574795424,0.975174390993478))
    push!(polyb, Vertex(0.9674040599289924,0.7546437140411728))
    push!(polyb, Vertex(0.6299023469869027,0.5941113636172484))
    push!(polyb, Vertex(0.3651692839350955,0.9412811548927036))
    push!(polyb, Vertex(0.46700978293718576,0.6372087264115178))
    push!(polyb, Vertex(0.09441852929672212,0.712398255967287))
    push!(polyb, Vertex(0.6594117537641844,0.8556959981933909))
    push!(polyb, Vertex(0.41749612512996004,0.7726528031089717))
    push!(polyb, Vertex(0.8230135867808732,0.9270969466572365))
    push!(polyb, Vertex(0.04389833237303109,0.5609640399741183))
    push!(polyb, Vertex(0.4125116220480387,0.5160396701522856))
    push!(polyb, Vertex(0.8836833851491399,0.7590081568215858))
    push!(polyb, Vertex(0.5428845419686217,0.5433520734475961))
    push!(polyb, Vertex(0.5473279623543394,0.48707911841886875))
    push!(polyb, Vertex(0.504975646406884,0.6269770507544459))
    push!(polyb, Vertex(0.6692031351652192,0.8269983773582317))
    push!(polyb, Vertex(0.5665824658182701,0.28971587253261544))
    push!(polyb, Vertex(0.6979596641936274,0.6963299758066004))
    push!(polyb, Vertex(0.41393013487701724,0.02515192656365972))
    push!(polyb, Vertex(0.4146094682050434,0.7760774006253788))
    push!(polyb, Vertex(0.916841516397837,0.3812664317124159))
    push!(polyb, Vertex(0.5694746452391368,0.021859026116122227))
    push!(polyb, Vertex(0.5811595135265466,0.2757689306043134))
    push!(polyb, Vertex(0.4894054388068505,0.35643214512810095))
    push!(polyb, Vertex(0.5595554636066462,0.5848619132982462))
    push!(polyb, Vertex(0.055595115965147324,0.08067075687112912))
    push!(polyb, Vertex(0.8201820592938383,0.5690753436287503))
    push!(polyb, Vertex(0.1630668008610887,0.21593074025144188))
    push!(polyb, Vertex(0.3818523211281113,0.49892970559056926))
    push!(polyb, Vertex(0.5879509816156174,0.7091657227073489))
    push!(polyb, Vertex(0.7906807122153934,0.2210363105879154))
    push!(polyb, Vertex(0.5593694790678512,0.4640752428580712))
    push!(polyb, Vertex(0.6012166172350997,0.31394337995190624))
    push!(polyb, Vertex(0.5626052673349125,0.8689869237375565))
    push!(polyb, Vertex(0.9062078706328689,0.5926285281119317))
    push!(polyb, Vertex(0.2116818069931432,0.7097461450894131))
    push!(polyb, Vertex(0.23462798149426778,0.6347647741466378))
    push!(polyb, Vertex(0.8333332026965838,0.4044637675513083))
    push!(polyb, Vertex(0.38151281356770417,0.5842041828199334))
    push!(polyb, Vertex(0.15019329095984024,0.4211489213025983))
    push!(polyb, Vertex(0.8360328648037809,0.023292893005907978))
    push!(polyb, Vertex(0.31874416444804177,0.11721397482037488))
    push!(polyb, Vertex(0.39155608346680637,0.0016294336268325438))
    push!(polyb, Vertex(0.49241666713123067,0.827810647049537))
    push!(polyb, Vertex(0.28852655549271455,0.5134511690270223))
    push!(polyb, Vertex(0.6320220046603813,0.8851410292212929))
    push!(polyb, Vertex(0.9740042546342045,0.18256471264167917))
    push!(polyb, Vertex(0.5780722916281602,0.20879214268020507))
    push!(polyb, Vertex(0.6201054810476829,0.09914904930053448))
    push!(polyb, Vertex(0.6219009431337508,0.1516866613709651))
    push!(polyb, Vertex(0.34716243679494574,0.006595091110359164))
    push!(polyb, Vertex(0.6890583759094768,0.3440450342993846))
    push!(polyb, Vertex(0.9778512374676402,0.5744641432773667))
    push!(polyb, Vertex(0.0033568324875912214,0.6148369261343873))
    push!(polyb, Vertex(0.34832869925563625,0.40428761834628935))
    push!(polyb, Vertex(0.2907179321707041,0.5340135773079302))
    push!(polyb, Vertex(0.12479109273224198,0.8885089407774147))
    push!(polyb, Vertex(0.9876184891473105,0.5950350229751693))
    push!(polyb, Vertex(0.2006836897721096,0.44523290012385375))
    push!(polyb, Vertex(0.24482339423302024,0.11468063465242362))
    push!(polyb, Vertex(0.28181652810828073,0.2035021093756293))
    push!(polyb, Vertex(0.06733186631795296,0.45001635767596593))
    push!(polyb, Vertex(0.21527661471388138,0.3520300299617407))
    push!(polyb, Vertex(0.5927741449744783,0.8525914294328816))
    push!(polyb, Vertex(0.6258613640600956,0.43656539966226804))
    push!(polyb, Vertex(0.5910220014222105,0.3521522088352207))
    push!(polyb, Vertex(0.6106632086832191,0.5346375695717998))
    push!(polyb, Vertex(0.34566121714575027,0.5845480914914436))
    push!(polyb, Vertex(0.8691587357382462,0.32313446735351126))
    push!(polyb, Vertex(0.3861919843487014,0.12972240893508835))
    push!(polyb, Vertex(0.37769973424020375,0.051694328774281484))
    push!(polyb, Vertex(0.4552128830552533,0.16435305304622982))
    push!(polyb, Vertex(0.16131619574158784,0.3971212509168853))
    push!(polyb, Vertex(0.24572158657159515,0.8610180990083223))
    push!(polyb, Vertex(0.7284331633547654,0.7187589226042599))
    push!(polyb, Vertex(0.03504548735067359,0.6029694383465394))
    push!(polyb, Vertex(0.9995434477131571,0.3499686099551058))
    push!(polyb, Vertex(0.8042033719306321,0.17999362049770196))
    push!(polyb, Vertex(0.7467041955244049,0.3309946873417935))

    results = intersection(polya, polyb)
end

outfile = Pkg.dir("PolygonClipping")*"/perf/intersection1_benchmarks.csv"

out = benchmark(intersection1, "Clipping", "Intersection 1", 10)

if isfile(outfile)
    previous = readtable(outfile)
    out = vcat(previous, out)
end
writetable(outfile, out)


text:
module VoronoiDelaunay

# Fast, robust 2D Voronoi/Delaunay tesselation
# Implementation follows algorithms described in http://arxiv.org/abs/0901.4107
# and used (for e.g.) in the Illustris Simulation
# http://www.illustris-project.org/
#
# Author: Ariel Keselman (skariel@gmail.com)
# License: MIT
# Bug reports welcome!

export
DelaunayTessellation,
DelaunayTessellation2D,
sizehint!,
isexternal,
min_coord,
max_coord,
locate,
movea,
moveb,
movec,
delaunayedges,
voronoiedges,
voronoiedgeswithoutgenerators,
iterate,
findindex,
push!,
Point,
Point2D,
AbstractPoint2D,
getx,
gety,
geta,
getb,
getc,
getgena,
getgenb,
getplotxy,
scaleShiftPoints,
expand,
Triple,
DelaunayTriangle

using GeometricalPredicates
import GeometricalPredicates: geta, getb, getc

import Base: push!, iterate, copy, sizehint!
import Colors: RGB, RGBA
using Random: shuffle!

include("VoronoiDelaunayExtensions.jl")

const min_coord = GeometricalPredicates.min_coord + eps(Float64)
const max_coord = GeometricalPredicates.max_coord - eps(Float64)

mutable struct DelaunayTriangle{T<:AbstractPoint2D} <: AbstractNegativelyOrientedTriangle
    _a::T; _b::T; _c::T
    _bx::Float64; _by::Float64
    _cx::Float64; _cy::Float64
    _px::Float64; _py::Float64
    _pr2::Float64
    _neighbour_a::Int64
    _neighbour_b::Int64
    _neighbour_c::Int64

    function DelaunayTriangle{T}(pa::T, pb::T, pc::T,
                                 na::Int64, nb::Int64, nc::Int64) where T
        t = new(pa, pb, pc, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, na, nb, nc)
        clean!(t)
        t
    end

    # this constructor is good for making copies
    function DelaunayTriangle{T}(pa::T, pb::T, pc::T,
                                 bx::Float64, by::Float64,
                                 cx::Float64, cy::Float64,
                                 px::Float64, py::Float64,
                                 pr2::Float64,
                                 na::Int64, nb::Int64, nc::Int64) where T
        new(pa, pb, pc, bx, by, cx, cy, px, py, pr2, na, nb, nc)
    end
end
function DelaunayTriangle(pa::T, pb::T, pc::T,
                          na::Int64, nb::Int64, nc::Int64) where T<:AbstractPoint2D
    DelaunayTriangle{T}(pa, pb, pc, na, nb, nc)
end
function DelaunayTriangle(pa::T, pb::T, pc::T,
                          bx::Float64, by::Float64,
                          cx::Float64, cy::Float64,
                          px::Float64, py::Float64,
                          pr2::Float64,
                          na::Int64, nb::Int64, nc::Int64) where T<:AbstractPoint2D
    DelaunayTriangle{T}(pa, pb, pc, bx, by, cx, cy, px, py, pr2, na, nb, nc)
end

function copy(t::DelaunayTriangle{T}) where T<:AbstractPoint2D
    DelaunayTriangle(
                     t._a, t._b, t._c,
                     t._bx, t._by,
                     t._cx, t._cy,
                     t._px, t._py,
                     t._pr2,
                     t._neighbour_a, t._neighbour_b, t._neighbour_c
                     )
end

function isexternal( t::DelaunayTriangle{T}, ranges::NTuple{4,Float64} ) where T<:AbstractPoint2D
    getx(geta(t)) < ranges[1] || getx(geta(t)) > ranges[2] ||
    getx(getb(t)) < ranges[1] || getx(getb(t)) > ranges[2] ||
    getx(getc(t)) < ranges[1] || getx(getc(t)) > ranges[2]
end

mutable struct DelaunayTessellation2D{T<:AbstractPoint2D}
    _trigs::Vector{DelaunayTriangle{T}}
    _last_trig_index::Int64
    _edges_to_check::Vector{Int64}
    _total_points_added::Int64
    _ranges::NTuple{4,Float64}

    function DelaunayTessellation2D{T}(n::Int64 = 100) where T
        a = T(GeometricalPredicates.min_coord, GeometricalPredicates.min_coord)
        b = T(GeometricalPredicates.min_coord, GeometricalPredicates.max_coord)
        c = T(GeometricalPredicates.max_coord, GeometricalPredicates.min_coord)
        d = T(GeometricalPredicates.max_coord, GeometricalPredicates.max_coord)
        t1 = DelaunayTriangle{T}(d,c,b, 2,1,1)
        t2 = DelaunayTriangle{T}(a,b,c, 3,1,1)
        t3 = DelaunayTriangle{T}(d,c,b, 2,1,1)
        _trigs = DelaunayTriangle{T}[t1, t2, t3]
        t = new(_trigs, 3, Int64[], 0)
        sizehint!(t._edges_to_check, 1000)
        sizehint!(t, n)
        _ranges=(min_coord,max_coord,min_coord,max_coord)
        t = new(_trigs, 3, Int64[], 0, _ranges)
        return t
    end
end
DelaunayTessellation2D(n::Int64) = DelaunayTessellation2D{Point2D}(n)
DelaunayTessellation2D(n::Int64, ::T) where {T<:AbstractPoint2D} = DelaunayTessellation2D{T}(n)
DelaunayTessellation(n::Int64=100) = DelaunayTessellation2D(n)

# tessellation function that can deal with points outside [1,2]x[1,2]
# and that always returns a convex tessellation
function DelaunayTessellation( points::Array{T,1} ) where T<:AbstractPoint2D
  scaledPoints, ranges = scaleShiftPoints( points )
  scaledTess = DelaunayTessellation2D{T}(length( points ))
  push!( scaledTess, scaledPoints )
  tess = expand( scaledTess, ranges )
  return tess
end
# in order to reduce computation, if you are interested in the edges only, you can also
# apply the expand function to the points of the edges directly.
# if this is what you want, you can (see VoronoiDelaunayExtensions.jl)
# 1. apply scaleShiftPoints to your point set
# 2. do the tessellation and push the scaled and shifted point set
# 3. get the edges with the delaunayedges function and
# 4. expand the end points of the edges with the expand function

function sizehint!(t::DelaunayTessellation2D{T}, n::Int64) where T<:AbstractPoint2D
    required_total_size = 2n + 10
    required_total_size <= length(t._trigs) && return
    sizehint!(t._trigs, required_total_size)
    while length(t._trigs) < required_total_size
        push!(t._trigs, copy(t._trigs[end]))
    end
    t
end

# growing strategy
function sizefit_at_least(t::DelaunayTessellation2D{T}, n::Int64) where T<:AbstractPoint2D
    minimal_acceptable_actual_size = 2*n+10
    minimal_acceptable_actual_size <= length(t._trigs) && return
    required_total_size = length(t._trigs)
    while required_total_size < minimal_acceptable_actual_size
        required_total_size += required_total_size >>> 1
    end
    sizehint!(t._trigs, required_total_size)
    while length(t._trigs) < required_total_size
        push!(t._trigs, copy(t._trigs[end]))
    end
    t
end

# convert the tessellation back to original scale after the tessellation
function expand( tess::DelaunayTessellation2D{T},ranges) where T<:AbstractPoint2D
  xmin = ranges[1]
  ymin = ranges[3]
  scale = max( ranges[4] - ranges[3], ranges[2] - ranges[1] ) / 0.98
  offset = 1.01
  for i in 1:length(tess._trigs)
    #tess._trigs[i]._a = Point2D( ( tess._trigs[i]._a._x - offset ) * scale + xmin, ( tess._trigs[i]._a._y - offset ) * scale + ymin )
    tess._trigs[i]._a = expand([tess._trigs[i]._a],ranges)[1]
    tess._trigs[i]._b = expand([tess._trigs[i]._b],ranges)[1]
    tess._trigs[i]._c = expand([tess._trigs[i]._c],ranges)[1]
    #tess._trigs[i]._b = Point2D( ( tess._trigs[i]._b._x - offset ) * scale + xmin, ( tess._trigs[i]._b._y - offset ) * scale + ymin )
    #tess._trigs[i]._c = Point2D( ( tess._trigs[i]._c._x - offset ) * scale + xmin, ( tess._trigs[i]._c._y - offset ) * scale + ymin )
    tess._trigs[i]._bx   = tess._trigs[i]._bx  * scale
    tess._trigs[i]._by   = tess._trigs[i]._by  * scale
    tess._trigs[i]._cx   = tess._trigs[i]._cx  * scale
    tess._trigs[i]._cy   = tess._trigs[i]._cy  * scale
    tess._trigs[i]._px   = tess._trigs[i]._px  * scale ^ 3
    tess._trigs[i]._py   = tess._trigs[i]._py  * scale ^ 3
    tess._trigs[i]._pr2  = tess._trigs[i]._pr2 * scale ^ 2
  end
  tess._ranges = ranges
  return tess
end

struct DelaunayEdge{T<:AbstractPoint2D}
    _a::T
    _b::T
end
geta(e::DelaunayEdge{T}) where {T<:AbstractPoint2D} = e._a
getb(e::DelaunayEdge{T}) where {T<:AbstractPoint2D} = e._b

struct VoronoiEdge{T<:AbstractPoint2D}
    _a::Point2D
    _b::Point2D
    _generator_a::T
    _generator_b::T
end
geta(e::VoronoiEdge{T}) where {T<:AbstractPoint2D} = e._a
getb(e::VoronoiEdge{T}) where {T<:AbstractPoint2D} = e._b
getgena(e::VoronoiEdge{T}) where {T<:AbstractPoint2D} = e._generator_a
getgenb(e::VoronoiEdge{T}) where {T<:AbstractPoint2D} = e._generator_b

struct VoronoiEdgeWithoutGenerators
    _a::Point2D
    _b::Point2D
end
geta(e::VoronoiEdgeWithoutGenerators) = e._a
getb(e::VoronoiEdgeWithoutGenerators) = e._b

# TODO: is an iterator faster?
function delaunayedges( t::DelaunayTessellation2D)
    ranges = t._ranges
    visited = zeros(Bool, t._last_trig_index)
    function delaunayiterator(c::Channel)
        @inbounds for ix in 2:t._last_trig_index
            tr = t._trigs[ix]
            isexternal( tr, ranges ) && continue
            visited[ix] && continue
            visited[ix] = true
            ix_na = tr._neighbour_a
            if !visited[ix_na]
                put!(c, DelaunayEdge(getb(tr), getc(tr)))
            end
            ix_nb = tr._neighbour_b
            if !visited[ix_nb]
                put!(c, DelaunayEdge(geta(tr), getc(tr)))
            end
            ix_nc = tr._neighbour_c
            if !visited[ix_nc]
                put!(c, DelaunayEdge(geta(tr), getb(tr)))
            end
        end
    end
    Channel(delaunayiterator)
end

# TODO: is an iterator faster?
function voronoiedges(t::DelaunayTessellation2D)
    visited = zeros(Bool, t._last_trig_index)
    visited[1] = true
    function voronoiiterator(c::Channel)
        for ix in 2:t._last_trig_index
            visited[ix] && continue
            tr = t._trigs[ix]
            visited[ix] = true
            #isexternal(tr) && continue
            cc = circumcenter(tr)

            ix_na = tr._neighbour_a
            if !visited[ix_na] #&& !isexternal(t._trigs[ix_na])
                nb = t._trigs[ix_na]
                put!(c, VoronoiEdge(cc, circumcenter(nb), getb(tr), getc(tr)))
            end
            ix_nb = tr._neighbour_b
            if !visited[ix_nb] #&& !isexternal(t._trigs[ix_nb])
                nb = t._trigs[ix_nb]
                put!(c, VoronoiEdge(cc, circumcenter(nb), geta(tr), getc(tr)))
            end
            ix_nc = tr._neighbour_c
            if !visited[ix_nc] #&& !isexternal(t._trigs[ix_nb])
                nb = t._trigs[ix_nc]
                put!(c, VoronoiEdge(cc, circumcenter(nb), geta(tr), getb(tr)))
            end
        end
    end
    Channel(voronoiiterator)
end

function voronoiedgeswithoutgenerators(t::DelaunayTessellation2D)
    visited = zeros(Bool, t._last_trig_index)
    visited[1] = true
    function voronoiiterator(c::Channel)
        for ix in 2:t._last_trig_index
            visited[ix] && continue
            tr = t._trigs[ix]
            visited[ix] = true
            #isexternal(tr) && continue
            cc = circumcenter(tr)

            ix_na = tr._neighbour_a
            if !visited[ix_na] #&& !isexternal(t._trigs[ix_na])
                nb = t._trigs[ix_na]
                put!(c, VoronoiEdgeWithoutGenerators(cc, circumcenter(nb)))
            end
            ix_nb = tr._neighbour_b
            if !visited[ix_nb] #&& !isexternal(t._trigs[ix_nb])
                nb = t._trigs[ix_nb]
                put!(c, VoronoiEdgeWithoutGenerators(cc, circumcenter(nb)))
            end
            ix_nc = tr._neighbour_c
            if !visited[ix_nc] #&& !isexternal(t._trigs[ix_nc])
                nb = t._trigs[ix_nc]
                put!(c, VoronoiEdgeWithoutGenerators(cc, circumcenter(nb)))
            end
        end
    end
    Channel(voronoiiterator)
end

mutable struct TrigIter
    ix::Int64
end

function iterate( t::DelaunayTessellation2D, it::TrigIter=TrigIter(2) ) # default it resembles old start
    ranges = t._ranges
    # resembles old done
    while isexternal(t._trigs[it.ix], ranges) && it.ix <= t._last_trig_index
        it.ix += 1
    end
    if it.ix > t._last_trig_index
        return nothing
    end
    # resembles old next
    @inbounds trig = t._trigs[it.ix]
    it.ix += 1
    return (trig, it)
end

function findindex(tess::DelaunayTessellation2D{T}, p::T) where T<:AbstractPoint2D
    i::Int64 = tess._last_trig_index
    counter::Int64 = 0
    ntriangles = length(tess._trigs)
    while true
        counter += 1
        @inbounds w = intriangle(tess._trigs[i], p)
        w > 0 && return i
        @inbounds tr = tess._trigs[i]
        if w == -1
            i = tr._neighbour_a
        elseif w == -2
            i = tr._neighbour_b
        else
            i = tr._neighbour_c
        end
        if counter > ntriangles
            throw(DomainError("Point was not found in any triangle"))
        end
    end
end

locate(t::DelaunayTessellation2D{T}, p::T) where {T<:AbstractPoint2D} = t._trigs[findindex(t, p)]

movea(tess::DelaunayTessellation2D{T},
      trig::DelaunayTriangle{T}) where {T<:AbstractPoint2D} = tess._trigs[trig._neighbour_a]
moveb(tess::DelaunayTessellation2D{T},
      trig::DelaunayTriangle{T}) where {T<:AbstractPoint2D} = tess._trigs[trig._neighbour_b]
movec(tess::DelaunayTessellation2D{T},
      trig::DelaunayTriangle{T}) where {T<:AbstractPoint2D} = tess._trigs[trig._neighbour_c]

function _pushunfixed!(tess::DelaunayTessellation2D{T}, p::T) where T<:AbstractPoint2D
    i = findindex(tess, p)
    ltrigs1::Int64 = tess._last_trig_index+1
    ltrigs2::Int64 = tess._last_trig_index+2

    @inbounds t1 = tess._trigs[i]
    old_t1_a = geta(t1)
    seta(t1, p)
    old_t1_b = t1._neighbour_b
    old_t1_c = t1._neighbour_c
    t1._neighbour_b = ltrigs1
    t1._neighbour_c = ltrigs2

    @inbounds t2 = tess._trigs[ltrigs1]
    setabc(t2, old_t1_a, p, getc(t1))
    t2._neighbour_a = i
    t2._neighbour_b = old_t1_b
    t2._neighbour_c = ltrigs2

    @inbounds t3 = tess._trigs[ltrigs2]
    setabc(t3, old_t1_a, getb(t1), p)
    t3._neighbour_a = i
    t3._neighbour_b = ltrigs1
    t3._neighbour_c = old_t1_c

    @inbounds nt2 = tess._trigs[t2._neighbour_b]
    if nt2._neighbour_a==i
        nt2._neighbour_a = ltrigs1
    elseif nt2._neighbour_b==i
        nt2._neighbour_b = ltrigs1
    else
        nt2._neighbour_c = ltrigs1
    end

    @inbounds nt3 = tess._trigs[t3._neighbour_c]
    if nt3._neighbour_a==i
        nt3._neighbour_a = ltrigs2
    elseif nt3._neighbour_b==i
        nt3._neighbour_b = ltrigs2
    else
        nt3._neighbour_c = ltrigs2
    end

    tess._last_trig_index += 2

    i
end

function _flipa!(tess::DelaunayTessellation2D, ix1::Int64, ix2::Int64)
    @inbounds ot1 = tess._trigs[ix1]
    @inbounds ot2 = tess._trigs[ix2]
    if ot2._neighbour_a == ix1
        _flipaa!(tess, ix1, ix2, ot1, ot2)
    elseif ot2._neighbour_b == ix1
        _flipab!(tess, ix1, ix2, ot1, ot2)
    else
        _flipac!(tess, ix1, ix2, ot1, ot2)
    end
end

function _endflipa!(tess::DelaunayTessellation2D,
                    ix1::Int64, ix2::Int64,
                    ot1::DelaunayTriangle, ot2::DelaunayTriangle)
    @inbounds n1 = tess._trigs[ot1._neighbour_a]
    if n1._neighbour_a==ix2
        n1._neighbour_a = ix1
    elseif n1._neighbour_b==ix2
        n1._neighbour_b = ix1
    else
        n1._neighbour_c = ix1
    end
    @inbounds n2 = tess._trigs[ot2._neighbour_c]
    if n2._neighbour_a==ix1
        n2._neighbour_a = ix2
    elseif n2._neighbour_b==ix1
        n2._neighbour_b = ix2
    else
        n2._neighbour_c = ix2
    end
end

function _flipaa!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_b = getb(ot1)
    setb(ot1, geta(ot2))
    ot1._neighbour_a = ot2._neighbour_c
    old_ot1_neighbour_c = ot1._neighbour_c
    ot1._neighbour_c = ix2

    newc = geta(ot2)
    setabc(ot2, geta(ot1), old_ot1_geom_b, newc)
    ot2._neighbour_a = ot2._neighbour_b
    ot2._neighbour_b = ix1
    ot2._neighbour_c = old_ot1_neighbour_c

    _endflipa!(tess,ix1,ix2,ot1,ot2)
end

function _flipab!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_b = getb(ot1)
    setb(ot1, getb(ot2))
    ot1._neighbour_a = ot2._neighbour_a
    old_ot1_neighbour_c = ot1._neighbour_c
    ot1._neighbour_c = ix2

    newc = getb(ot2)
    setabc(ot2, geta(ot1), old_ot1_geom_b, newc)
    ot2._neighbour_a = ot2._neighbour_c
    ot2._neighbour_b = ix1
    ot2._neighbour_c = old_ot1_neighbour_c

    _endflipa!(tess,ix1,ix2,ot1,ot2)
end

function _flipac!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_b = getb(ot1)
    setb(ot1, getc(ot2))
    ot1._neighbour_a = ot2._neighbour_b
    old_ot1_neighbour_c = ot1._neighbour_c
    ot1._neighbour_c = ix2

    setab(ot2, geta(ot1), old_ot1_geom_b)
    ot2._neighbour_b = ix1
    ot2._neighbour_c = old_ot1_neighbour_c

    _endflipa!(tess,ix1,ix2,ot1,ot2)
end

########################

function _flipb!(tess::DelaunayTessellation2D{T},
                 ix1::Int64, ix2::Int64) where T<:AbstractPoint2D
    @inbounds ot1 = tess._trigs[ix1]
    @inbounds ot2 = tess._trigs[ix2]
    if ot2._neighbour_a == ix1
        _flipba!(tess, ix1, ix2, ot1, ot2)
    elseif ot2._neighbour_b == ix1
        _flipbb!(tess, ix1, ix2, ot1, ot2)
    else
        _flipbc!(tess, ix1, ix2, ot1, ot2)
    end
end

function _endflipb!(tess::DelaunayTessellation2D{T},
                    ix1::Int64, ix2::Int64,
                    ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    @inbounds n1 = tess._trigs[ot1._neighbour_b]
    if n1._neighbour_a==ix2
        n1._neighbour_a = ix1
    elseif n1._neighbour_b==ix2
        n1._neighbour_b = ix1
    else
        n1._neighbour_c = ix1
    end
    @inbounds n2 = tess._trigs[ot2._neighbour_a]
    if n2._neighbour_a==ix1
        n2._neighbour_a = ix2
    elseif n2._neighbour_b==ix1
        n2._neighbour_b = ix2
    else
        n2._neighbour_c = ix2
    end
end

function _flipba!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_c = getc(ot1)
    setc(ot1, geta(ot2))
    old_ot1_neighbour_a = ot1._neighbour_a
    ot1._neighbour_a = ix2
    ot1._neighbour_b = ot2._neighbour_c

    setbc(ot2, getb(ot1), old_ot1_geom_c)
    ot2._neighbour_a = old_ot1_neighbour_a
    ot2._neighbour_c = ix1

    _endflipb!(tess,ix1,ix2,ot1,ot2)
end

function _flipbb!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_c = getc(ot1)
    setc(ot1, getb(ot2))
    old_ot1_neighbour_a = ot1._neighbour_a
    ot1._neighbour_a = ix2
    ot1._neighbour_b = ot2._neighbour_a

    newa = getb(ot2)
    setabc(ot2, newa, getb(ot1), old_ot1_geom_c)
    ot2._neighbour_a = old_ot1_neighbour_a
    ot2._neighbour_b = ot2._neighbour_c
    ot2._neighbour_c = ix1

    _endflipb!(tess,ix1,ix2,ot1,ot2)
end

function _flipbc!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_c = getc(ot1)
    setc(ot1, getc(ot2))
    old_ot1_neighbour_a = ot1._neighbour_a
    ot1._neighbour_a = ix2
    ot1._neighbour_b = ot2._neighbour_b

    newa = getc(ot2)
    setabc(ot2, newa, getb(ot1), old_ot1_geom_c)
    ot2._neighbour_b = ot2._neighbour_a
    ot2._neighbour_a = old_ot1_neighbour_a
    ot2._neighbour_c = ix1

    _endflipb!(tess,ix1,ix2,ot1,ot2)
end

########################

function _flipc!(tess::DelaunayTessellation2D{T},
                 ix1::Int64, ix2::Int64) where T<:AbstractPoint2D
    @inbounds ot1 = tess._trigs[ix1]
    @inbounds ot2 = tess._trigs[ix2]
    if ot2._neighbour_a == ix1
        _flipca!(tess, ix1, ix2, ot1, ot2)
    elseif ot2._neighbour_b == ix1
        _flipcb!(tess, ix1, ix2, ot1, ot2)
    else
        _flipcc!(tess, ix1, ix2, ot1, ot2)
    end
end

function _endflipc!(tess::DelaunayTessellation2D{T},
                    ix1::Int64, ix2::Int64,
                    ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    @inbounds n1 = tess._trigs[ot1._neighbour_c]
    if n1._neighbour_a==ix2
        n1._neighbour_a = ix1
    elseif n1._neighbour_b==ix2
        n1._neighbour_b = ix1
    else
        n1._neighbour_c = ix1
    end
    @inbounds n2 = tess._trigs[ot2._neighbour_b]
    if n2._neighbour_a==ix1
        n2._neighbour_a = ix2
    elseif n2._neighbour_b==ix1
        n2._neighbour_b = ix2
    else
        n2._neighbour_c = ix2
    end
end

function _flipca!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_a = geta(ot1)
    seta(ot1, geta(ot2))
    old_ot1_neighbour_b = ot1._neighbour_b
    ot1._neighbour_b = ix2
    ot1._neighbour_c = ot2._neighbour_c

    newb = geta(ot2)
    setabc(ot2, old_ot1_geom_a, newb, getc(ot1))
    ot2._neighbour_a = ix1
    ot2._neighbour_c = ot2._neighbour_b
    ot2._neighbour_b = old_ot1_neighbour_b

    _endflipc!(tess,ix1,ix2,ot1,ot2)
end

function _flipcb!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_a = geta(ot1)
    seta(ot1, getb(ot2))
    old_ot1_neighbour_b = ot1._neighbour_b
    ot1._neighbour_b = ix2
    ot1._neighbour_c = ot2._neighbour_a

    setac(ot2, old_ot1_geom_a, getc(ot1))
    ot2._neighbour_a = ix1
    ot2._neighbour_b = old_ot1_neighbour_b

    _endflipc!(tess,ix1,ix2,ot1,ot2)
end

function _flipcc!(tess::DelaunayTessellation2D{T},
                  ix1::Int64, ix2::Int64,
                  ot1::DelaunayTriangle{T}, ot2::DelaunayTriangle{T}) where T<:AbstractPoint2D
    old_ot1_geom_a = geta(ot1)
    seta(ot1, getc(ot2))
    old_ot1_neighbour_b = ot1._neighbour_b
    ot1._neighbour_b = ix2
    ot1._neighbour_c = ot2._neighbour_b

    newb = getc(ot2)
    setabc(ot2, old_ot1_geom_a, newb, getc(ot1))
    ot2._neighbour_c = ot2._neighbour_a
    ot2._neighbour_a = ix1
    ot2._neighbour_b = old_ot1_neighbour_b

    _endflipc!(tess,ix1,ix2,ot1,ot2)
end

function _restoredelaunayhood!(tess::DelaunayTessellation2D{T},
                               ix_trig::Int64) where T<:AbstractPoint2D
    @inbounds center_pt = geta(tess._trigs[ix_trig])

    # `A` - edge
    push!(tess._edges_to_check, ix_trig)
    while length(tess._edges_to_check) > 0
        @inbounds trix = tess._edges_to_check[end]
        @inbounds tr_i = tess._trigs[trix]
        @inbounds nb_a = tr_i._neighbour_a
        if nb_a > 1
            @inbounds tr_f = tess._trigs[nb_a]
            if incircle(tr_f, center_pt) > 0
                _flipa!(tess, trix, nb_a)
                push!(tess._edges_to_check, nb_a)
                continue
            end
        end
        pop!(tess._edges_to_check)
    end

    # `B` - edge
    push!(tess._edges_to_check, tess._last_trig_index-1)
    while length(tess._edges_to_check) > 0
        @inbounds trix = tess._edges_to_check[end]
        @inbounds tr_i = tess._trigs[trix]
        @inbounds nb_b = tr_i._neighbour_b
        if nb_b > 1
            @inbounds tr_f = tess._trigs[nb_b]
            if incircle(tr_f, center_pt) > 0
                _flipb!(tess, trix, nb_b)
                push!(tess._edges_to_check, nb_b)
                continue
            end
        end
        pop!(tess._edges_to_check)
    end

    # `C` - edge
    push!(tess._edges_to_check, tess._last_trig_index)
    while length(tess._edges_to_check) > 0
        @inbounds trix = tess._edges_to_check[end]
        @inbounds tr_i = tess._trigs[trix]
        @inbounds nb_c = tr_i._neighbour_c
        if nb_c > 1
            @inbounds tr_f = tess._trigs[nb_c]
            if incircle(tr_f, center_pt) > 0
                _flipc!(tess, trix, nb_c)
                push!(tess._edges_to_check, nb_c)
                continue
            end
        end
        pop!(tess._edges_to_check)
    end
end

# push a single point. Grows tessellation as needed
function push!(tess::DelaunayTessellation2D{T}, p::T) where T<:AbstractPoint2D
    tess._total_points_added += 1
    sizefit_at_least(tess, tess._total_points_added)
    i = _pushunfixed!(tess, p)
    _restoredelaunayhood!(tess, i)
end

# push an array in given order
function _pushunsorted!(tess::DelaunayTessellation2D{T}, a::Array{T, 1}) where T<:AbstractPoint2D
    sizehint!(tess, length(a))
    for p in a
        push!(tess, p)
    end
end

# push an array but sort it first for better performance
function push!(tess::DelaunayTessellation2D{T}, a::Array{T, 1}) where T<:AbstractPoint2D
    shuffle!(a)
    mssort!(a)
    _pushunsorted!(tess, a)
end

intensity(c::RGB)  = c.b
intensity(c::RGBA) = c.b
intensity(c) 	     = getfield(c, 1) # Workaround. Gray needs to be imported from images, which would take to long.

# Create DelaunayTessellation with npts points from an image
function from_image(img, npts)

    # placing points in places that represent the image
    pts = Point2D[]
    for i in 1:npts
        x = rand()
        y = rand()
        if intensity(img[Int64(floor(x * size(img)[1])) + 1, Int64(floor(y * size(img)[2])) + 1]) > 0.5
            if rand() < 0.100
                push!(pts, Point2D(1.0 + rand(), 1.0 + rand()))
            end
            continue
        end
        x /= 2
        y /= 2
        push!(pts, Point2D(1.0 + x + 0.3, 2.0 - y*2/3 - 0.3))
    end

    tess = DelaunayTessellation(npts)
    push!(tess, pts)

    tess
end

function getplotxy(edges)
    edges = collect(edges)
    x = Float64[]
    y = Float64[]
    for e in edges
        push!(x, getx(geta(e)))
        push!(x, getx(getb(e)))
        push!(x, NaN)
        push!(y, gety(geta(e)))
        push!(y, gety(getb(e)))
        push!(y, NaN)
    end
    (x, y)
end

end # module VoronoiDelaunay

ext: jl


text:
#=
This file is auto-generated. Do not edit.
=#

"""A power-system bus."""
mutable struct Bus <: Topology
    number::Int64  # number associated with the bus
    name::String  # the name of the bus
    bustype::Union{Nothing, BusType}  # bus type
    angle::Union{Nothing, Float64}  # angle of the bus in radians
    voltage::Union{Nothing, Float64}  # voltage as a multiple of basevoltage
    voltagelimits::Union{Nothing, Min_Max}  # limits on the voltage variation as multiples of basevoltage
    basevoltage::Union{Nothing, Float64}  # the base voltage in kV
    internal::InfrastructureSystemsInternal

    function Bus(number, name, bustype, angle, voltage, voltagelimits, basevoltage, internal, )
        (number, name, bustype, angle, voltage, voltagelimits, basevoltage, internal, ) = CheckBusParams(
            number,
            name,
            bustype,
            angle,
            voltage,
            voltagelimits,
            basevoltage,
            internal,
        )
        new(number, name, bustype, angle, voltage, voltagelimits, basevoltage, internal, )
    end
end

function Bus(number, name, bustype, angle, voltage, voltagelimits, basevoltage, )
    Bus(number, name, bustype, angle, voltage, voltagelimits, basevoltage, InfrastructureSystemsInternal())
end

function Bus(; number, name, bustype, angle, voltage, voltagelimits, basevoltage, )
    Bus(number, name, bustype, angle, voltage, voltagelimits, basevoltage, )
end

# Constructor for demo purposes; non-functional.

function Bus(::Nothing)
    Bus(;
        number=0,
        name="init",
        bustype=nothing,
        angle=0.0,
        voltage=0.0,
        voltagelimits=(min=0.0, max=0.0),
        basevoltage=nothing,
    )
end

"""Get Bus number."""
get_number(value::Bus) = value.number
"""Get Bus name."""
get_name(value::Bus) = value.name
"""Get Bus bustype."""
get_bustype(value::Bus) = value.bustype
"""Get Bus angle."""
get_angle(value::Bus) = value.angle
"""Get Bus voltage."""
get_voltage(value::Bus) = value.voltage
"""Get Bus voltagelimits."""
get_voltagelimits(value::Bus) = value.voltagelimits
"""Get Bus basevoltage."""
get_basevoltage(value::Bus) = value.basevoltage
"""Get Bus internal."""
get_internal(value::Bus) = value.internal


text:
# Unit tests for Region type.
# This file is supposed to be included from runtests.jl.

@testset "Region" begin
    @test length(Region().runs) == 0
    @test length(Region(Run[]).runs) == 0
    @test length(Region(Run[], false).runs) == 0

    @test Region([Run(0, 0:0)]).complement == false
    @test Region([Run(0, 0:0)], false).complement == false
    @test Region([Run(0, 0:0)], true).complement == true

    @test length(Region([Run(0, 0:0)]).runs) == 1
    @test Region([Run(0, 0:0)]).runs[1].row == 0
    @test Region([Run(0, 0:0)]).runs[1].columns.start == 0
    @test Region([Run(0, 0:0)]).runs[1].columns.stop == 0

    @test length(Region([Run(1, 2:3)]).runs) == 1
    @test Region([Run(1, 2:3)]).runs[1].row == 1
    @test Region([Run(1, 2:3)]).runs[1].columns.start == 2
    @test Region([Run(1, 2:3)]).runs[1].columns.stop == 3

    @test Region([Run(0, 0:0)], false) == Region([Run(0, 0:0)], false)
    @test Region([Run(0, 0:0)], true) == Region([Run(0, 0:0)], true)
    @test Region([Run(0, 0:0)], false) != Region([Run(0, 0:0)], true)
    @test Region([Run(0, 0:0)], true) != Region([Run(0, 0:0)], false)
    @test Region([Run(0, 0:0)], false) != Region([Run(1, 2:3)], true)

    @test Region([Run(0, 0:0)], false) == copy(Region([Run(0, 0:0)], false))

    @test invert(Region([Run(0, 0:0)])).runs[1].row == 0
    @test invert(Region([Run(0, 0:0)])).runs[1].columns.start == 0
    @test invert(Region([Run(0, 0:0)])).runs[1].columns.stop == 0
    @test invert(Region([Run(1, 2:3)])).runs[1].row == -1
    @test invert(Region([Run(1, 2:3)])).runs[1].columns.start == -3
    @test invert(Region([Run(1, 2:3)])).runs[1].columns.stop == -2
    @test invert(invert(Region([Run(1, 2:3)]))).runs[1].row == 1
    @test invert(invert(Region([Run(1, 2:3)]))).runs[1].columns.start == 2
    @test invert(invert(Region([Run(1, 2:3)]))).runs[1].columns.stop == 3
    @test (-Region([Run(1, 2:3)])).runs[1].row == -1
    @test (-Region([Run(1, 2:3)])).runs[1].columns.start == -3
    @test (-Region([Run(1, 2:3)])).runs[1].columns.stop == -2
    @test (- -Region([Run(1, 2:3)])).runs[1].row == 1
    @test (- -Region([Run(1, 2:3)])).runs[1].columns.start == 2
    @test (- -Region([Run(1, 2:3)])).runs[1].columns.stop == 3

    @test translate(Region([Run(1, 1:2), Run(2, 3:4)]), [-5, -6]) == Region([Run(-5, -4:-3), Run(-4, -2:-1)])
    @test Region([Run(1, 1:2), Run(2, 3:4)]) - [5, 6] == Region([Run(-5, -4:-3), Run(-4, -2:-1)])
    @test Region([Run(1, 1:2), Run(2, 3:4)]) + [5, 6] == Region([Run(7, 6:7), Run(8, 8:9)])

    @test !contains(Region([Run(0, 0:-1)]), 0, -1)
    @test !contains(Region([Run(0, 0:-1)]), 0, 0)
    @test !contains(Region([Run(0, 0:-1)]), 0, 1)
    @test !contains(Region([Run(0, 0:0)]), -1, -1)
    @test !contains(Region([Run(0, 0:0)]), 0, -1)
    @test !contains(Region([Run(0, 0:0)]), -1, 0)
    @test contains(Region([Run(0, 0:0)]), 0, 0)
    @test !contains(Region([Run(0, 0:0)]), 0, 1)
    @test !contains(Region([Run(0, 0:0)]), 1, 0)
    @test !contains(Region([Run(0, 0:1)]), -1, 0)
    @test !contains(Region([Run(0, 0:1)]), 0, -1)
    @test contains(Region([Run(0, 0:1)]), 0, 0)
    @test contains(Region([Run(0, 0:1)]), 1, 0)
    @test !contains(Region([Run(0, 0:1)]), 2, 0)
    @test !contains(Region([Run(0, 0:1)]), 1, 1)
    @test contains(Region([Run(0, 0:1), Run(1, 0:1)]), 0, 0)
    @test contains(Region([Run(0, 0:1), Run(1, 0:1)]), 1, 0)
    @test contains(Region([Run(0, 0:1), Run(1, 0:1)]), 0, 1)
    @test contains(Region([Run(0, 0:1), Run(1, 0:1)]), 1, 1)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), -1, 0)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), 2, 0)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), -1, 1)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), 2, 1)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), -1, 0)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), -1, 1)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), -1, 2)
    @test !contains(Region([Run(0, 0:1), Run(1, 0:1)]), -1, 2)

    @test [0, -1] ∉ Region([Run(0, 0:-1)])
    @test [0, 0] ∉ Region([Run(0, 0:-1)])
    @test [0, 1] ∉ Region([Run(0, 0:-1)])
    @test [-1, -1] ∉ Region([Run(0, 0:0)])
    @test [0, 1] ∉ Region([Run(0, 0:0)])
    @test [-1, 0] ∉ Region([Run(0, 0:0)])
    @test [0, 0] ∈ Region([Run(0, 0:0)])
    @test [0, 1] ∉ Region([Run(0, 0:0)])
    @test [1, 0] ∉ Region([Run(0, 0:0)])
    @test [-1, 0] ∉ Region([Run(0, 0:1)])
    @test [0, -1] ∉ Region([Run(0, 0:1)])
    @test [0, 0] ∈ Region([Run(0, 0:1)])
    @test [1, 0] ∈ Region([Run(0, 0:1)])
    @test [2, 0] ∉ Region([Run(0, 0:1)])
    @test [1, 1] ∉ Region([Run(0, 0:1)])
    @test [0, 0] ∈ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [1, 0] ∈ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [0, 1] ∈ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [1, 1] ∈ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [-1, 0] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [2, 0] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [-1, 1] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [2, 1] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [-1, 0] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [-1, 1] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [-1, 2] ∉ Region([Run(0, 0:1), Run(1, 0:1)])
    @test [-1, 2] ∉ Region([Run(0, 0:1), Run(1, 0:1)])

    @test complement(Region([Run(0, 0:1)])).complement == true;
    @test complement(Region([Run(0, 0:1)])).runs == Region([Run(0, 0:1)]).runs;
    @test complement(Region([Run(0, 0:1)], false)).complement == true;
    @test complement(Region([Run(0, 0:1)], false)).runs == Region([Run(0, 0:1)]).runs;
    @test complement(Region([Run(0, 0:1)], true)).complement == false;
    @test complement(Region([Run(0, 0:1)], true)).runs == Region([Run(0, 0:1)]).runs;

    @test Regions.merge([Run(0, 0:1)], [Run(1, 0:1)]) == [Run(0, 0:1), Run(1, 0:1)]
    @test Regions.merge([Run(1, 0:1)], [Run(0, 0:1)]) == [Run(0, 0:1), Run(1, 0:1)]
    @test Regions.merge([Run(0, 0:1), Run(1, 0:1)], [Run(0, 1:2), Run(1, 1:2)]) == [Run(0, 0:1), Run(0, 1:2), Run(1, 0:1), Run(1, 1:2)]
    @test Regions.merge([Run(0, 1:2), Run(1, 1:2)], [Run(0, 0:1), Run(1, 0:1)]) == [Run(0, 0:1), Run(0, 1:2), Run(1, 0:1), Run(1, 1:2)]

    @test sort([Run(0, 0:1), Run(1, 0:1)]) == [Run(0, 0:1), Run(1, 0:1)]
    @test sort([Run(1, 0:1), Run(0, 0:1)]) == [Run(0, 0:1), Run(1, 0:1)]
    a = [Run(0, 0:1), Run(1, 0:1)]; sort!(a); @test a == [Run(0, 0:1), Run(1, 0:1)]
    a = [Run(1, 0:1), Run(0, 0:1)]; sort!(a); @test a == [Run(0, 0:1), Run(1, 0:1)]

    a = [Run(0, 0:1)]; Regions.pack!(a); @test a == [Run(0, 0:1)]
    a = [Run(0, 0:1), Run(0, 1:2)]; Regions.pack!(a); @test a == [Run(0, 0:2)]
    a = [Run(0, 0:1), Run(0, 2:3)]; Regions.pack!(a); @test a == [Run(0, 0:3)]
    a = [Run(0, 0:3), Run(0, 0:1)]; Regions.pack!(a); @test a == [Run(0, 0:3)]
    a = [Run(0, 0:3), Run(0, 1:2)]; Regions.pack!(a); @test a == [Run(0, 0:3)]
    a = [Run(0, 0:3), Run(0, 2:3)]; Regions.pack!(a); @test a == [Run(0, 0:3)]
    a = [Run(0, 0:3), Run(0, 0:3)]; Regions.pack!(a); @test a == [Run(0, 0:3)]
    a = [Run(0, 0:1), Run(0, 3:4)]; Regions.pack!(a); @test a == [Run(0, 0:1), Run(0, 3:4)]

    @test union([Run(0, 0:1), Run(0, 0:1)]) == [Run(0, 0:1)]
    @test union([Run(0, 0:1), Run(1, 0:1)]) == [Run(0, 0:1), Run(1, 0:1)]
    @test union([Run(0, 0:1), Run(1, 0:1)], [Run(0, 1:2), Run(1, 1:2)]) == [Run(0, 0:2), Run(1, 0:2)]

    a = [Run(0, 0:1)]; Regions.intersect!(a); @test a == Run[]
    a = [Run(0, 0:1), Run(0, 2:3)]; Regions.intersect!(a); @test a == Run[]
    a = [Run(0, 0:1), Run(0, 1:2)]; Regions.intersect!(a); @test a == [Run(0, 1:1)]
    a = [Run(0, 0:3), Run(0, 1:2)]; Regions.intersect!(a); @test a == [Run(0, 1:2)]

    @test intersection([Run(0, 0:1)], [Run(1, 0:1)]) == Run[]
    @test intersection([Run(0, 0:1)], [Run(0, 0:1)]) == [Run(0, 0:1)]

    @test difference(Run[], Run[]) == Run[]
    @test difference([Run(0, 0:1)], Run[]) == [Run(0, 0:1)]
    @test difference(Run[], [Run(0, 0:1)]) == Run[]
    @test difference([Run(0, 0:1)], [Run(0, 0:1)]) == Run[]
    @test difference([Run(0, 0:2)], [Run(0, 0:0)]) == [Run(0, 1:2)]
    @test difference([Run(0, 0:2)], [Run(0, 1:1)]) == [Run(0, 0:0), Run(0, 2:2)]
    @test difference([Run(0, 0:2)], [Run(0, 2:2)]) == [Run(0, 0:1)]

end # "Region"


text:
# Autogenerated wrapper script for AMReX_jll for x86_64-w64-mingw32-cxx11
export libamrex

using CompilerSupportLibraries_jll
using MicrosoftMPI_jll
JLLWrappers.@generate_wrapper_header("AMReX")
JLLWrappers.@declare_library_product(libamrex, "libamrex.dll")
function __init__()
    JLLWrappers.@generate_init_header(CompilerSupportLibraries_jll, MicrosoftMPI_jll)
    JLLWrappers.@init_library_product(
        libamrex,
        "bin\\libamrex.dll",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
## Maximum entropy degree distribution of a food web with S species

# number of species
S = 150

# predicted number of links (using all the posterior distribution of the flexible links model)
links_rdm = predict_L(S)

# median, 67%, 89%, and 97% PI of predicted number of links
links_500 = Int64(median(links_rdm))
links_015 = Int64(quantile(links_rdm, 0.015))
links_055 = Int64(quantile(links_rdm, 0.055))
links_165 = Int64(quantile(links_rdm, 0.165))
links_835 = Int64(quantile(links_rdm, 0.835))
links_945 = Int64(quantile(links_rdm, 0.945))
links_985 = Int64(quantile(links_rdm, 0.985))

# degree distributions of those predicted numbers of links
dd_500 = maxent_dd(S, links_500)
dd_015 = maxent_dd(S, links_015)
dd_055 = maxent_dd(S, links_055)
dd_165 = maxent_dd(S, links_165)
dd_835 = maxent_dd(S, links_835)
dd_945 = maxent_dd(S, links_945)
dd_985 = maxent_dd(S, links_985)

# mean degrees
md = 2 .* links_rdm ./ S
md_500 = 2 * links_500 / S
md_015 = 2 * links_015 / S
md_055 = 2 * links_055 / S
md_165 = 2 * links_165 / S
md_835 = 2 * links_835 / S
md_945 = 2 * links_945 / S
md_985 = 2 * links_985 / S

## plots

# palette
pal_median = RGB(204/255,121/255,167/255)

# plot mean degrees (distribution of constraints)
density(md, color=:black, linewidth=2, label="",  framestyle=:box, dpi=200)
plot!([md_015, md_985], [0.03, 0.03], fill=(0, :grey, 0.12), c=:transparent, lab="")
plot!([md_500], st=:vline, color=pal_median, ls=:solid, lw=1, lab="")
plot!([md_015], st=:vline, color=:grey, ls=:dot, lw=1, lab="")
plot!([md_055], st=:vline, color=:grey, ls=:dash, lw=1, lab="")
plot!([md_165], st=:vline, color=:grey, ls=:solid, lw=1, lab="")
plot!([md_835], st=:vline, color=:grey, ls=:solid, lw=1, lab="")
plot!([md_945], st=:vline, color=:grey, ls=:dash, lw=1, lab="")
plot!([md_985], st=:vline, color=:grey, ls=:dot, lw=1, lab="")
xaxis!(xlabel="mean degree (links / species)", xlims=(0,S))
yaxis!(ylabel="probability density", ylims=(0,0.03))

savefig(joinpath("fig","md_150"))

# plot maximum entropy degree distributions
a = [1,2,5,10,30,100] # for x-axis (needs to be changed depending on the value of S)

plot(dd_015, color=:black, alpha=0.7, linewidth=2, linestyle=:dot, label="97% PI", # 97% PI
     framestyle=:box, foreground_color_legend=nothing, background_color_legend=:white, dpi=200)
plot!(dd_055, color=:black, alpha=0.7, linewidth=2, linestyle=:dash, label="89% PI") # 89% PI
plot!(dd_165, color=:black, alpha=0.7, linewidth=2, linestyle=:solid, label="67% PI") # 67% PI
plot!(dd_835, color=:grey, alpha=0.7, linewidth=1.5, linestyle=:solid, label="") # 67% PI
plot!(dd_945, color=:grey, alpha=0.7, linewidth=1.5, linestyle=:dash, label="") # 89% PI
plot!(dd_985, color=:grey, alpha=0.7, linewidth=1.5, linestyle=:dot, label="") # 97% PI
plot!(dd_500, color=pal_median, linewidth=4, label="median")
xaxis!(:log, xlabel="k", xticks=(a, a), xlims=(1,S))
yaxis!(ylabel="p(k)", ylims=(0,0.25))

savefig(joinpath("fig","dd_150"))

max_stars_repo_name: FrancisBanville/Trophic-METE

max_issues_repo_name: FrancisBanville/Trophic-METE

max_forks_repo_name: FrancisBanville/Trophic-METE


text:
#=
	        MISO	MOSI	SCLK	CE0	CE1	CE2
Main SPI	9	    10	    11	    8	7	-
Aux  SPI	19	    20	    21	    18	17	16
=#
"""
    struct DisplaySPI <: AbstractNumDisplay
        handle::Int
    end
"""
struct DisplaySPI <: AbstractNumDisplay
    handle::Int
end

"""
    function DisplaySPI(;
        spi::Int = 0,
        channel::Int = 0,
        boud::Int = 32*10^3
    )

Creates device that refers to numerical display integrated with SPI chip.

## Arguments

- `spi` : value 0 or 1 to use main or auxilary RaspberryPi's SPI controller. Default is main.

- `channel` : number of CE port: 0, 1 for the main SPI or 0, 1, 2 for the auxilary one. Default is 0.

- `boud` : data transfer rate for SPI, bits per second. Must be from 32kHz to 10MHz.
"""
function DisplaySPI(;
    spi::Int = 0,
    channel::Int = 0,
    boud::Int = 32*10^3 # bits per second
)
    # theoretical spi max boud = 125 MHz, practical max = 30 MHz
    # chip max boud = 10 MHz
    @assert 32*10^3 <= boud <= 10*10^6 "boud must be 32kHz-10Mhz, got $boud"

    @assert 0 <= spi <= 1 "spi number must be 0 or 1, got $spi"

    if PiGPIOC.gpioInitialise() < 0
        throw("pigpio initialisation failed.")
    else
        println("pigpio initialised okay.")
    end

    # b  b  b  b  b  b  R  T  n  n  n  n  W  A u2 u1 u0 p2 p1 p0  m  m
    # default but main or auxiliary
    spiFlags = spi << 8 # set to position 8

    # set spi num here
    handle = PiGPIOC.spiOpen(channel, boud, spiFlags)
    if handle < 0
        throw("spiOpen returns error with status: $spi")
    end

    DisplaySPI(handle)
end

function spiWrite_bytes(d::DisplaySPI, data::AbstractArray{UInt8})
    bytes_count = length(data)
    cs = Cstring(pointer(data))
    ret = PiGPIOC.spiWrite(d.handle, cs, bytes_count)
    if ret < 0
        throw("spiWrite returns error with status: $ret")
    end

    nothing
end

# scan rate = 800 Hz (500-1300) from chip docs
scan_rate(::DisplaySPI) = 800

size(d::DisplaySPI) = 8

### low-level operations

function decode_mode(d::DisplaySPI, decode::UInt8 = 0b1111_1111)
    data = [0b0000_1001, decode]
    spiWrite_bytes(d, data)
end

function set_intensity(d::DisplaySPI, intensity::Int = 16)
    @assert 1 <= intensity <= 16 "intensity must be between 1 and 16, got $intensity"

    data = [0b0000_1010, UInt8(intensity - 1)]
    spiWrite_bytes(d, data)
end

function set_limit(d::DisplaySPI, limit::Int = 8)
    @assert 1 <= limit <= 8 "limit must be between 1 and 8, got $limit"

    data = [0b0000_1011, UInt8(limit - 1)]
    spiWrite_bytes(d, data)
end

function shutdown_mode_on(d::DisplaySPI)
    data = [0b0000_1100, 0b0000_0000]
    spiWrite_bytes(d, data)
end

function shutdown_mode_off(d::DisplaySPI)
    data = [0b0000_1100, 0b0000_0001]
    spiWrite_bytes(d, data)
end

function test_mode_on(d::DisplaySPI)
    data = [0b0000_1111, 0b0000_0001]
    spiWrite_bytes(d, data)
end

function test_mode_off(d::DisplaySPI)
    data = [0b0000_1111, 0b0000_0000]
    spiWrite_bytes(d, data)
end

function write_digit(d::DisplaySPI, value::UInt8, position::Int)
    @assert 1 <= position <= 8 "position must be between 1 and 8, got $position"

    data = [UInt8(position), value]
    spiWrite_bytes(d, data)
end


text:
module SearchLightSerializerJSON

import SearchLight
import JSON

function SearchLight.Serializer.serialize(data) :: String
  JSON.json(data)
end

function SearchLight.Serializer.deserialize(::Type{T}, json_data)::T where {T}
  JSON.parse(json_data)
end

end


text:
ROS.@include actionlib_tutorials: FibonacciAction, FibonacciFeedback, FibonacciResult, FibonacciGoal

action_server = nothing
as_callback_enabled = false

function execute_cb()
    goal = ROS.actionlib.acceptNewGoal(action_server)
    action_feedback = ROS.actionlib_tutorials_FibonacciFeedback()
    f::Vector{Number} = []
    for i in 1:100
        push!(f,i)
        action_feedback.sequence = f
        ROS.actionlib.publishFeedback(action_server, action_feedback)
    end
    action_result = ROS.actionlib_tutorials_FibonacciResult()
    action_result.sequence = f
    ROS.actionlib.setSucceeded(action_server, action_result, ":)")
    ROS.actionlib.setAborted(action_server, ROS.actionlib_tutorials_FibonacciResult(), "result")
    ROS.actionlib.setAborted(action_server)
    ROS.actionlib.setPreempted(action_server, ROS.actionlib_tutorials_FibonacciResult(), "result")
    ROS.actionlib.setPreempted(action_server)
    ROS.actionlib.setSucceeded(action_server)
    global as_callback_enabled = true
    return nothing
end

function testActionServer()
    ROS.init("testActionlibServer")
    nh = ROS.NodeHandle()

    global action_server = ROS.actionlib.SimpleActionServer("test_action_server", ROS.actionlib_tutorials_FibonacciAction)
    ROS.actionlib.start(action_server)
    @test ! ROS.actionlib.isActive(action_server)
    ROS.actionlib.registerGoalCallback(action_server, execute_cb)
    ROS.actionlib.registerPreemptCallback(action_server, execute_cb)
    ROS.actionlib.isNewGoalAvailable(action_server)
    ROS.actionlib.isPreemptRequested(action_server)

    loop = 100
    while loop > 0 && !as_callback_enabled
        loop -= 1
        ROS.sleep(ROS.Rate(10))
        ROS.spinOnce()
    end
    @test as_callback_enabled
    ROS.actionlib.shutdown(action_server)
    ROS.shutdown(nh)
    println("All $(basename(@__FILE__)) tests passed.")
end


text:
abstract type AbstractGP{T<:Real,L<:Likelihood{T},I<:Inference{T},GP<:Abstract_GP,N} end

const AbstractGP1 = AbstractGP{<:Real,<:Likelihood,<:Inference,<:Abstract_GP,1}

function Random.rand!(model::AbstractGP,A::DenseArray{T},X::AbstractArray{T}) where {T<:Real}
    rand!(MvNormal(predict_f(model,X,covf=true,fullcov=true)...),A)
end

function Random.rand(model::AbstractGP,X::AbstractArray{T},n::Int=1) where {T<:Real}
    if model.nLatent == 1
        rand!(model,Array{T}(undef,size(X,1),n),X)
    else
        @error "Sampling not implemented for multiple output GPs"
    end
end

# Statistics.mean(model::AbstractGP) = model.μ

get_K(model::AbstractGP) = getproperty.(model.f,:K)

get_μ(model::AbstractGP) = getproperty.(model.f,:μ)

get_Σ(model::AbstractGP) = getproperty.(model.f,:Σ)

get_σ_k(model::AbstractGP) = getproperty.(model.f,:σ_k)

get_kernel(model::AbstractGP) = getproperty.(model.f,:kernel)


text:
cell_to_vtkcell(::Type{Line}) = VTKCellTypes.VTK_LINE
cell_to_vtkcell(::Type{Line2D}) = VTKCellTypes.VTK_LINE
cell_to_vtkcell(::Type{Line3D}) = VTKCellTypes.VTK_LINE
cell_to_vtkcell(::Type{QuadraticLine}) = VTKCellTypes.VTK_QUADRATIC_EDGE

cell_to_vtkcell(::Type{Quadrilateral}) = VTKCellTypes.VTK_QUAD
cell_to_vtkcell(::Type{Quadrilateral3D}) = VTKCellTypes.VTK_QUAD
cell_to_vtkcell(::Type{QuadraticQuadrilateral}) = VTKCellTypes.VTK_BIQUADRATIC_QUAD
cell_to_vtkcell(::Type{Triangle}) = VTKCellTypes.VTK_TRIANGLE
cell_to_vtkcell(::Type{QuadraticTriangle}) = VTKCellTypes.VTK_QUADRATIC_TRIANGLE
cell_to_vtkcell(::Type{Cell{2,8,4}}) = VTKCellTypes.VTK_QUADRATIC_QUAD

cell_to_vtkcell(::Type{Hexahedron}) = VTKCellTypes.VTK_HEXAHEDRON
cell_to_vtkcell(::Type{Cell{3,20,6}}) = VTKCellTypes.VTK_QUADRATIC_HEXAHEDRON
cell_to_vtkcell(::Type{Tetrahedron}) = VTKCellTypes.VTK_TETRA
cell_to_vtkcell(::Type{QuadraticTetrahedron}) = VTKCellTypes.VTK_QUADRATIC_TETRA

"""
    vtk_grid(filename::AbstractString, grid::Grid)

Create a unstructured VTK grid from a `Grid`. Return a `DatasetFile`
which data can be appended to, see `vtk_point_data` and `vtk_cell_data`.
"""
function WriteVTK.vtk_grid(filename::AbstractString, grid::Grid{dim,C,T}; compress::Bool=true) where {dim,C,T}
    cls = MeshCell[]
    for cell in grid.cells
        celltype = Ferrite.cell_to_vtkcell(typeof(cell))
        push!(cls, MeshCell(celltype, collect(cell.nodes)))
    end
    coords = reshape(reinterpret(T, getnodes(grid)), (dim, getnnodes(grid)))
    return vtk_grid(filename, coords, cls; compress=compress)
end

"""
    vtk_point_data(vtk, data::Vector{<:SymmetricTensor{2}}, name)

Write the second order tensor field `data` to the vtk file. Twodimensional tensors are padded with zeros.
The tensor field is written such that Paraview recognizes the tensor components, where `XX` corresponds to the `[1,1]` component and so on.
"""
function WriteVTK.vtk_point_data(
    vtk::WriteVTK.DatasetFile,
    data::Union{
        Vector{SymmetricTensor{2,dim,T,M}}
        },
    name::AbstractString
    ) where {dim,T,M}

    if dim == 1
        noutputs = 1
        sort_paraview = [1]
    elseif dim == 2
        noutputs = 6
        sort_paraview = [1,4,2]
    elseif dim == 3
        noutputs = 6
        sort_paraview = [1,4,6,2,5,3]
    end

    npoints = length(data)
    out = zeros(T, noutputs, npoints)
    out[sort_paraview, :] .= reshape(reinterpret(T, data), (M, npoints))

    return vtk_point_data(vtk, out, name)
end

"""
    vtk_point_data(vtk, data::Vector{<:Tensor}, name)

Write the tensor field data to the vtk file. Only writes the tensor values available in `data`.
In the vtu-file, ordering of the tensor components is column-wise (just like Julia).
[1 2
 3 4] => 1, 3, 2, 4
"""
function WriteVTK.vtk_point_data(
    vtk::WriteVTK.DatasetFile,
    data::Union{
        Vector{Tensor{order,dim,T,M}},
        Vector{SymmetricTensor{order,dim,T,M}}
        },
    name::AbstractString
    ) where {order,dim,T,M}

    npoints = length(data)
    out = zeros(T, M, npoints)
    out[1:M, :] .= reshape(reinterpret(T, data), (M, npoints))
    return vtk_point_data(vtk, out, name)
end

"""
    vtk_point_data(vtk, data::Vector{<:Vec}, name)

Write the vector field data to the vtk file.
"""
function WriteVTK.vtk_point_data(vtk::WriteVTK.DatasetFile, data::Vector{Vec{dim,T}}, name::AbstractString) where {dim,T}
    npoints = length(data)
    out = zeros(T, (dim == 2 ? 3 : dim), npoints)
    out[1:dim, :] .= reshape(reinterpret(T, data), (dim, npoints))
    return vtk_point_data(vtk, out, name)
end

function vtk_nodeset(vtk::WriteVTK.DatasetFile, grid::Grid{dim}, nodeset::String) where {dim}
    z = zeros(getnnodes(grid))
    z[collect(getnodeset(grid, nodeset))] .= 1.0
    vtk_point_data(vtk, z, nodeset)
end

"""
    vtk_cellset(vtk, grid::Grid)

Export all cell sets in the grid. Each cell set is exported with
`vtk_cell_data` with value 1 if the cell is in the set, and 0 otherwise.
"""
function vtk_cellset(vtk::WriteVTK.DatasetFile, grid::AbstractGrid, cellsets=keys(grid.cellsets))
    z = zeros(getncells(grid))
    for cellset in cellsets
        z .= 0.0
        z[collect(getcellset(grid, cellset))] .= 1.0
        vtk_cell_data(vtk, z, cellset)
    end
    return vtk
end

"""
    vtk_cellset(vtk, grid::Grid, cellset::String)

Export the cell set specified by `cellset` as cell data with value 1 if
the cell is in the set and 0 otherwise.
"""
vtk_cellset(vtk::WriteVTK.DatasetFile, grid::AbstractGrid, cellset::String) =
    vtk_cellset(vtk, grid, [cellset])

function WriteVTK.vtk_point_data(vtkfile, dh::AbstractDofHandler, u::Vector, suffix="")

    fieldnames = Ferrite.getfieldnames(dh)  # all primary fields

    for name in fieldnames
        data = reshape_to_nodes(dh, u, name)
        vtk_point_data(vtkfile, data, string(name, suffix))
    end

    return vtkfile
end


text:
##High-level syntax
#First delete any existing nc-files
for fn in (fn1,fn2,fn3)
  if (isfile(fn))
    rm(fn)
  end
end

nccreate(fn1,"v1","Dim1",[1,2],@Compat.AnyDict("units"=>"deg C"),"Dim2",collect(1:10),"Dim3",20,@Compat.AnyDict("max"=>10),
mode=NC_NETCDF4)
nccreate(fn1,"vstr","Dim2",collect(1:10),t=String)
nccreate(fn2,"v2","Dim1",[1,2,3],@Compat.AnyDict("units"=>"deg C"),"Dim2",collect(1:10),"Dim3",20,@Compat.AnyDict("max"=>10),
atts=@Compat.AnyDict("a1"=>"varatts"),gatts=@Compat.AnyDict("Some global attributes"=>2010),mode=NC_64BIT_OFFSET)
nccreate(fn3,"v3","Dim1",3,mode=NC_CLASSIC_MODEL)
tlist = [Float64, Float32, Int32, Int16, Int8]
for i = 1:length(tlist)
	nccreate(fn3,"vt$i","Dim2",collect(1:10),t=tlist[i])
end

ncputatt(fn1,"v1",@Compat.Dict("Additional String attribute"=>"att"))
ncputatt(fn1,"global",@Compat.Dict("Additional global attribute"=>"gatt"))
ncputatt(fn1,"global",@Compat.Dict("Additional Int8 attribute"=>Int8(20),
							"Additional Int16 attribute"=>Int16(20),
							"Additional Int32 attribute"=>Int32(20),
							"Additional Float32 attribute"=>Float32(20),
							"Additional Float64 attribute"=>Float64(20)))
ncputatt(fn1,"v1",    @Compat.Dict("Additional Int8 array attribute"=>Int8[i for i in 1:20],
							"Additional Int16 array attribute"=>Int16[i for i in 1:20],
							"Additional Int32 array attribute"=>Int32[i for i in 1:20],
							"Additional Float32 array attribute"=>Float32[i for i in 1:20],
							"Additional Float64 array attribute"=>Float64[i for i in 1:20]))

#First generate the data
x1 = rand(2,10,20)
x2 = rand(2,10,20)
xt=Array(Any,length(tlist))
for i=1:length(tlist)
    xt[i]=rand(tlist[i],10)
end
xs=Array(AbstractString,10)
xs[1]="a"
xs[2]="bb"
xs[3]="ccc"
xs[4]="dddd"
xs[5]="eeeee"
xs[6]="ffffff"
xs[7]="ggggggg"
xs[8]="hhhhhhhh"
xs[9]="iiiiiiiii"
xs[10]="jjjjjjjjjj"
#
# And write it
#
ncwrite(x1,fn1,"v1")
#Test sequential writing along one dimension
for i=1:10
  ncwrite(x2[:,i,:],fn2,"v2",start=[1,i,1],count=[-1,1,-1])
end
ncwrite(xs,fn1,"vstr")
#Test automatic type conversion
x4=[1,2,3]
ncwrite(x4,fn3,"v3")

for i=1:length(tlist)
  ncwrite(xt[i],fn3,"vt$i")
end

ncclose()


text:
@doc raw"""
    ItemkNN(k::Int64, shrink::Float64, weighting::Union{Nothing,Symbol}, weighting_at_inference::Bool, normalize::Bool, normalize_similarity::Bool)

Item-based k-nearest neighborhood algorithm with cosine similarity.
The model first computes the item-to-item similarity matrix

```math
s_{ij} = \frac{\bm r_i \cdot \bm r_j}{\|\bm r_i\|\|\bm r_j\| + h}\,,
```
where ``r_{i,u}`` is rating for item ``i`` by user ``u`` and ``h`` is the shrink parameter to suppress the contributions from items with a few ratings.

# Constructor arguments
- `k`: size of the nearest neighbors. Only the k-most similar items to each item are stored, which reduces sparse similarity maxrix size, and also make better predictions.
- `shrink`: shrink paramerer explained above.
- `weighting`: if set to `:ifidf` or `:bm25`, the raw rating matrix is weighted by TF-IDF or BM25, respectively, before computing similarity. If not necessary, just set `nothing`.
- `weighting_at_inference`: to use above weighting at inference time, only relevant for BM25.
- `normalize_similarity`: if set to `true`, normalize each column of similarity matrix. See the reference for detail.

# References
M. Deshpande and G. Karypis (2004), [Item-based top-N recommendation algorithms](https://doi.org/10.1145/963770.963776).
"""
mutable struct ItemkNN <: AbstractRecommender
    k::Int64
    shrink::Float64
    weighting::Union{Nothing,Symbol}
    weighting_at_inference::Bool
    normalize::Bool
    normalize_similarity::Bool

    similarity::Any
    user_histories::Any
    user2uidx::Union{Dict,Nothing}
    item2iidx::Union{Dict,Nothing}
    iidx2item::Union{Dict,Nothing}

    ItemkNN(
        k::Int64,
        shrink::Float64,
        weighting::Union{Nothing,Symbol},
        weighting_at_inference::Bool,
        normalize::Bool,
        normalize_similarity::Bool,
    ) = new(
        k,
        shrink,
        weighting,
        weighting_at_inference,
        normalize,
        normalize_similarity,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
    )
end

"""
    fit!(model::ItemkNN, table; col_user = :userid, col_item = :itemid, col_rating = :rating)

Fit the `ItemkNN` model. `col_rating` specifies rating column in the `table`, which will be all unity if implicit feedback data is given.
"""
function fit!(model::ItemkNN, table; kwargs...)
    col_user = get(kwargs, :col_user, :userid)
    col_item = get(kwargs, :col_item, :itemid)
    col_rating = get(kwargs, :col_rating, :rating)

    table, model.user2uidx, model.item2iidx, model.iidx2item =
        make_idmap(table, col_user = col_user, col_item = col_item)

    X = rows2sparse(
        table,
        col_user = col_user,
        col_item = col_item,
        col_rating = col_rating,
    )

    if !model.weighting_at_inference
        model.user_histories = X
    end

    if model.weighting == :tfidf
        X = tfidf(X)
    elseif model.weighting == :bm25
        X = bm25(X)
    end

    if model.weighting_at_inference
        model.user_histories = X
    end

    model.similarity = compute_similarity(
        X,
        model.k,
        model.shrink,
        model.normalize,
        model.normalize_similarity,
    )
    return model
end

@doc raw"""
    predict_u2i(model::ItemkNN, userid::Union{AbstractString,Int}, n::Int64; drop_history = false)

Recommend top-`n` items for user by `ItemkNN`. The predicted rating of item ``i`` by user ``u`` is computed by

```math

\hat{r}_{i, u} = \sum_{j} s_{ij} r_{j, u}\,,
```
where ``r_{j, u}`` is the actual user rating while ``\hat{r}_{i, u}`` is the model prediction.
"""
function predict_u2i(
    model::ItemkNN,
    userid::Union{AbstractString,Int},
    n::Int64;
    drop_history = false,
    kwargs...,
)
    if userid in keys(model.user2uidx)
        uidx = model.user2uidx[userid]
        pred = predict_u2i(
            model.similarity,
            model.user_histories[uidx, :],
            n,
            drop_history = drop_history,
        )
        return [model.iidx2item[iidx] for iidx in pred]

    else
        return []
    end
end


text:
m1 = collect(1:3.0)
m2 = (collect(1:3.0), collect(4:6.0))
m3 = (x = m1, y = sin, z = collect(4:6.0))

m4 = (x = m1, y = m1, z = collect(4:6.0))  # tied
m5 = (a = (m3, true), b = (m1, false), c = (m4, true))
m6 = (a = m1, b = [4.0 + im], c = m1)

m7 = TwoThirds((sin, collect(1:3.0)), (cos, collect(4:6.0)), (tan, collect(7:9.0)))
m8 = [Foo(m1, m1), (a = true, b = Foo([4.0], false), c = ()), [[5.0]]]

mat = Float32[4 6; 5 7]
m9 = (a = m1, b = mat, c = [mat, m1])

@testset "flatten & rebuild" begin
  @test destructure(m1)[1] isa Vector{Float64}
  @test destructure(m1)[1] == 1:3
  @test destructure(m2)[1] == 1:6
  @test destructure(m3)[1] == 1:6
  @test destructure(m4)[1] == 1:6
  @test destructure(m5)[1] == vcat(1:6, 4:6)
  @test destructure(m6)[1] == vcat(1:3, 4 + im)
  @test destructure(m9)[1] == 1:7

  @test destructure(m1)[2](7:9) == [7,8,9]
  @test destructure(m2)[2](4:9) == ([4,5,6], [7,8,9])
  @test destructure(m3)[2](4:9) == (x = [4,5,6], y = sin, z = [7,8,9])
  m4′ = destructure(m4)[2](4:9)
  @test m4′ == (x = [4,5,6], y = [4,5,6], z = [7,8,9])
  @test m4′.x === m4′.y
  m5′ = destructure(m5)[2](reverse(1:9))
  @test m5′.a[1].x === m5′.b[1]
  @test m5′.b[2] === false
  m6′ = destructure(m6)[2]((4:7) .+ (1:4) .* im)
  @test m6′.a isa Vector{Float64}
  @test m6′.a == 4:6
  @test m6′.a === m6′.c
  @test m6′.b == [7 + 4im]

  # struct, trainable
  @test destructure(m7)[1] == 1:3
  m7′ = destructure(m7)[2]([10,20,30])
  @test m7′.a == (sin, [10,20,30])
  @test m7′.b == (cos, [4,5,6])
  @test m7′.c == (tan, [7,8,9])

  @test destructure(m8)[1] == 1:5
  m8′ = destructure(m8)[2](1:5)
  @test m8′[1].x === m8′[1].y
  @test m8′[2].b.y === false
  @test m8′[3][1] == [5.0]

  m9′ = destructure(m9)[2](10:10:70)
  @test m9′.b === m9′.c[1]
  @test m9′.b isa Matrix{Float32}

  # errors
  @test_throws Exception destructure(m7)[2]([10,20])
  @test_throws Exception destructure(m7)[2]([10,20,30,40])
end

@testset "gradient of flatten" begin
  @test gradient(m -> destructure(m)[1][1], m1)[1] == [1,0,0]
  @test gradient(m -> destructure(m)[1][2], m2)[1] == ([0,1,0], [0,0,0])
  @test gradient(m -> destructure(m)[1][3], (m1, m1))[1] == ([0,0,1], nothing)
  @test gradient(m -> destructure(m)[1][1], m3)[1] == (x = [1,0,0], y = nothing, z = [0,0,0])
  @test gradient(m -> destructure(m)[1][2], m4)[1] == (x = [0,1,0], y = nothing, z = [0,0,0])

  g5 = gradient(m -> destructure(m)[1][3], m5)[1]
  @test g5.a[1].x == [0,0,1]
  @test g5.a[2] === nothing

  g6 = gradient(m -> imag(destructure(m)[1][4]), m6)[1]
  @test g6.a == [0,0,0]
  @test g6.a isa Vector{Float64}
  @test g6.b == [0+im]

  g8 = gradient(m -> sum(abs2, destructure(m)[1]), m8)[1]
  @test g8[1].x == [2,4,6]
  @test g8[2].b.x == [8]
  @test g8[3] == [[10.0]]

  g9 = gradient(m -> sum(sqrt, destructure(m)[1]), m9)[1]
  @test g9.c === nothing

  @testset "second derivative" begin
    @test gradient([1,2,3.0]) do v
      sum(abs2, gradient(m -> sum(abs2, destructure(m)[1]), (v, [4,5,6.0]))[1][1])
    end[1] ≈ [8,16,24]
    # With Diffractor, non-leaf _grad!(x, dx, off, flat::AbstractVector) gets double-wrapped dx:
    # off = (0, 3), dx = Tangent{Tangent{Tuple{Vector{Float64}, Vector{Float64}}, ...
    # until you add explicit double-unwrap: base(dx::Tangent{<:Tangent}) = backing(dx).backing
    # With Zygote, instead:
    # dx = Tangent{Any}(backing = Tangent{Any}([4.0, 8.0, 12.0], ZeroTangent()),)

    @test gradient([1,2,3.0]) do v
      sum(gradient(m -> sum(destructure(m)[1])^3, (v, [4,5,6.0]))[1][1])
    end[1] == [378, 378, 378]

    @test_broken gradient([1,2,3.0]) do v
      sum(abs2, gradient(m -> sum(abs2, destructure(m)[1]), (x = v, y = sin, z = [4,5,6.0]))[1][1])
    end[1] ≈ [8,16,24]
    # Zygote error in (::typeof(∂(canonicalize)))(Δ::NamedTuple{(:backing,), Tuple{NamedTuple{(:x, :y, :z)
    # Diffractor error in perform_optic_transform
  end
end

@testset "gradient of rebuild" begin
  re1 = destructure(m1)[2]
  @test gradient(x -> re1(x)[1], rand(3))[1] == [1,0,0]
  re2 = destructure(m2)[2]
  @test gradient(x -> re2(x)[1][2], rand(6))[1] == [0,1,0,0,0,0]
  re3 = destructure(m3)[2]
  @test gradient(x -> re3(x).x[3], rand(6))[1] == [0,0,1,0,0,0]
  @test gradient(x -> re3(x).z[1], rand(6))[1] == [0,0,0,1,0,0]

  re4 = destructure(m4)[2]
  @test gradient(x -> re4(x).x[1], rand(6))[1] == [1,0,0,0,0,0]
  @test gradient(x -> re4(x).y[2], rand(6))[1] == [0,1,0,0,0,0]
  @test gradient(rand(6)) do x
    m = re4(x)
    m.x[1] + 2*m.y[2] + 3*m.z[3]
  end[1] == [1,2,0, 0,0,3]

  re7 = destructure(m7)[2]
  @test gradient(x -> re7(x).a[2][3], rand(3))[1] == [0,0,1]
  @test gradient(x -> re7(x).b[2][2], rand(3))[1] == [0,0,0]
  @test gradient(x -> re7(x).c[2][1], rand(3))[1] == [0,0,0]

  v8, re8 = destructure(m8)
  @test gradient(x -> sum(abs2, re8(x)[1].y), v8)[1] == [2,4,6,0,0]
  @test gradient(x -> only(sum(re8(x)[3]))^2, v8)[1] == [0,0,0,0,10]

  re9 = destructure(m9)[2]
  @test gradient(x -> sum(abs2, re9(x).c[1]), 1:7)[1] == [0,0,0, 8,10,12,14]

  @testset "second derivative" begin
    @test_broken gradient(collect(1:6.0)) do y
      sum(abs2, gradient(x -> sum(abs2, re2(x)[1]), y)[1])
    end[1] ≈ [8,16,24,0,0,0]
    # ERROR: Need an adjoint for constructor ChainRulesCore.Tangent{Any, Tuple{Vector{Float64}, ChainRulesCore.ZeroTangent}}. Gradient is of type Tuple{Vector{Float64}, Vector{Float64}}
    # with Zygote, which can be fixed by:
    # Zygote.@adjoint Tangent{T,B}(x::Tuple) where {T,B<:Tuple} = Tangent{T,B}(x), dx -> (dx,)

    @test_broken gradient(collect(1:6.0)) do y
      sum(abs2, gradient(x -> sum(abs2, re3(x).z), y)[1])
    end[1] ≈ [0,0,0,32,40,48]
    # Not fixed by this:
    # Zygote.@adjoint Tangent{T,B}(x::NamedTuple) where {T,B<:NamedTuple} = Tangent{T,B}(x), dx -> (dx,)
  end
end

@testset "Flux issue 1826" begin
  v, re = destructure((x=[1,2.0], y=[3,4,5.0]))
  @test gradient(zero(v)) do w
    m = re(w)
    5 * sum(m.x) + 7 * sum(m[2])  # uses both x and y
  end == ([5.0, 5.0, 7.0, 7.0, 7.0],)
  # This, using only x, was broken on Flux:
  @test gradient(w -> sum(re(w).x), zero(v)) == ([1.0, 1.0, 0.0, 0.0, 0.0],)

  sh = [7,7.0];
  v, re = destructure((x=sh, y=[3.0,4.0], z=sh))  # shared array in the model
  @test v == [7, 7, 3, 4]
  @test re([1,10,100,1000]) == (x = [1, 10], y = [100, 1000], z = [1, 10])

  @test gradient(zero(v)) do w
    m = re(w)
    3 * sum(m.x) + 13 * sum(m.z)  # no dependence on y, but two distinct gradient arrays
  end == ([16, 16, 0, 0],)  # Flux gave ([3.0, 3.0, 13.0, 13.0],)

  @test gradient(zero(v)) do w
    m = re(w)
    4(sum(m.x) + sum(m.z))  # now two gradients are ===, so it eliminates one
  end == ([8,8,0,0],)

  @test gradient(zero(v)) do w
    m = re(w)
    4(sum(m.x) + sum(m.y)) + 13*sum(m.z)  # again two gradients are ===, so it eliminates one
  end == ([17,17,4,4],)  # Flux gave ([4.0, 4.0, 13.0, 13.0],)
end


text:
using GitHubActionsUtils
using Test

using Luxor

@testset "GitHubActionsUtils.jl" begin

    # move up from the `test` folder to the main repo
    cd("..")

    @show GitHubActionsUtils.is_github_actions()
    @show GitHubActionsUtils.event_name()
    @show GitHubActionsUtils.is_push()
    @show GitHubActionsUtils.is_pull_request()
    @show GitHubActionsUtils.head_ref()
    @show GitHubActionsUtils.github_ref()
    @show GitHubActionsUtils.is_branch()
    @show GitHubActionsUtils.branch_name()
    @show GitHubActionsUtils.is_tag()
    @show GitHubActionsUtils.tag_name()
    @show GitHubActionsUtils.pull_request_number()
    @show GitHubActionsUtils.pull_request_source()
    @show GitHubActionsUtils.repository()

    if GitHubActionsUtils.is_pull_request()
        pr_number = GitHubActionsUtils.pull_request_number()

        image_branch_name = "pr$(pr_number)-test-images"

        GitHubActionsUtils.set_github_actions_bot_as_git_user()
        GitHubActionsUtils.switch_to_or_create_branch(image_branch_name; orphan = true)

        image_path = "image.png"
        @png juliacircles() 400 400 image_path

        run(`git add -A`)
        run(`git commit -m "create testimages"`)

        GitHubActionsUtils.push_git_branch(image_branch_name)

        commit_hash = chomp(read(`git rev-parse HEAD`, String))

        image_url = string(
            "https://raw.githubusercontent.com/",
            GitHubActionsUtils.repository(),
            "/",
            commit_hash,
            "/",
            image_path
        )

        GitHubActionsUtils.comment_on_pr(
            pr_number,
            """
            This comment is auto-generated from a CI run with version $VERSION.

            Here's an image:
            ![an image]($image_url)
            """
        )
    end
end


text:
# This file is a part of TypeDBClient.  License is MIT: https://github.com/Humans-of-Julia/TypeDBClient.jl/blob/main/LICENSE

#
# package typedb.client.cluster;
#
# import typedb.client.api.database.Database;
# import typedb.client.core.CoreDatabase;
# import typedb.client.core.CoreDatabaseManager;
# import typedb.protocol.ClusterDatabaseProto;
# import org.slf4j.Logger;
# import org.slf4j.LoggerFactory;
#
# import java.util.Comparator;
# import java.util.HashMap;
# import java.util.HashSet;
# import java.util.Map;
# import java.util.Objects;
# import java.util.Optional;
# import java.util.Set;
#
# import static java.util.stream.Collectors.toSet;
#
# class ClusterDatabase implements Database.Cluster {
#
#     private static final Logger LOG = LoggerFactory.getLogger(ClusterDatabase.class);
#     private final String name;
#     private final Map<String, CoreDatabase> databases;
#     private final ClusterDatabaseManager databaseMgr;
#     private final Set<Replica> replicas;
#
#     private ClusterDatabase(String database, ClusterDatabaseManager clusterDatabaseMgr) {
#         this.name = database;
#         this.databaseMgr = clusterDatabaseMgr;
#         this.databases = new HashMap<>();
#         this.replicas = new HashSet<>();
#         for (String address : clusterDatabaseMgr.databaseMgrs().keySet()) {
#             CoreDatabaseManager coreDatabaseMgr = clusterDatabaseMgr.databaseMgrs().get(address);
#             databases.put(address, new CoreDatabase(coreDatabaseMgr, database));
#         }
#     }
#
#     static ClusterDatabase of(ClusterDatabaseProto.ClusterDatabase protoDB, ClusterDatabaseManager clusterDatabaseMgr) {
#         assert protoDB.getReplicasCount() > 0;
#         String database = protoDB.getName();
#         ClusterDatabase databaseClusterRPC = new ClusterDatabase(database, clusterDatabaseMgr);
#         databaseClusterRPC.replicas().addAll(protoDB.getReplicasList().stream().map(
#                 rep -> Replica.of(rep, databaseClusterRPC)
#         ).collect(toSet()));
#         LOG.debug("Discovered database cluster: {}", databaseClusterRPC);
#         return databaseClusterRPC;
#     }
#
#     @Override
#     public String name() {
#         return name;
#     }
#
#     @Override
#     public String schema() {
#         // TODO: select the leader database
#         return databases.values().iterator().next().schema();
#     }
#
#     @Override
#     public void delete() {
#         for (String address : databases.keySet()) {
#             if (databaseMgr.databaseMgrs().get(address).contains(name)) databases.get(address).delete();
#         }
#     }
#
#     @Override
#     public Set<Replica> replicas() {
#         return replicas;
#     }
#
#     @Override
#     public Optional<Replica> primaryReplica() {
#         return replicas.stream().filter(Replica::isPrimary).max(Comparator.comparing(Replica::term));
#     }
#
#     @Override
#     public Replica preferredReplica() {
#         return replicas.stream().filter(Replica::isPreferred).findAny()
#                 .orElse(replicas.iterator().next());
#     }
#
#     @Override
#     public String toString() {
#         return name;
#     }
#
#     static class Replica implements Database.Replica {
#
#         private final ID id;
#         private final ClusterDatabase database;
#         private final boolean isPrimary;
#         private final boolean isPreferred;
#         private final long term;
#         private final int hash;
#
#         private Replica(ClusterDatabase database, String address, boolean isPrimary, boolean isPreferred, long term) {
#             this.database = database;
#             this.isPrimary = isPrimary;
#             this.isPreferred = isPreferred;
#             this.term = term;
#             this.id = new ID(address, database.name());
#             this.hash = Objects.hash(id, isPrimary, isPreferred, term);
#         }
#
#         public static Replica of(ClusterDatabaseProto.ClusterDatabase.Replica replica, ClusterDatabase database) {
#             return new Replica(database, replica.getAddress(), replica.getPrimary(),
#                                replica.getPreferred(), replica.getTerm());
#         }
#
#         public ID id() {
#             return id;
#         }
#
#         @Override
#         public Database.Cluster database() {
#             return database;
#         }
#
#         @Override
#         public String address() {
#             return id.address;
#         }
#
#         @Override
#         public boolean isPrimary() {
#             return isPrimary;
#         }
#
#         @Override
#         public boolean isPreferred() {
#             return isPreferred;
#         }
#
#         @Override
#         public long term() {
#             return term;
#         }
#
#         @Override
#         public String toString() {
#             return id + ":" + (isPrimary ? "P" : "S") + ":" + term;
#         }
#
#         @Override
#         public boolean equals(Object o) {
#             if (this == o) return true;
#             if (o == null || getClass() != o.getClass()) return false;
#             Replica replica = (Replica) o;
#             return (id == replica.id &&
#                     term == replica.term &&
#                     isPrimary == replica.isPrimary &&
#                     isPreferred == replica.isPreferred);
#         }
#
#         @Override
#         public int hashCode() {
#             return hash;
#         }
#
#         static class ID {
#             private final String address;
#             private final String databaseName;
#             private final int hash;
#
#             ID(String address, String databaseName) {
#                 this.address = address;
#                 this.databaseName = databaseName;
#                 this.hash = Objects.hash(address, databaseName);
#                 ;
#             }
#
#             public String address() {
#                 return address;
#             }
#
#             public String databaseName() {
#                 return databaseName;
#             }
#
#             @Override
#             public String toString() {
#                 return address + "/" + databaseName;
#             }
#
#             @Override
#             public boolean equals(Object o) {
#                 if (this == o) return true;
#                 if (o == null || getClass() != o.getClass()) return false;
#                 ID id = (ID) o;
#                 return Objects.equals(address, id.address) &&
#                         Objects.equals(databaseName, id.databaseName());
#             }
#
#             @Override
#             public int hashCode() {
#                 return hash;
#             }
#         }
#     }
# }


text:
module Archives

using FileType

TypeEpub   = FileType.Types("epub", MIME("application/epub+zip"))
TypeZip    = FileType.Types("zip", MIME("application/zip"))
TypeTar    = FileType.Types("tar", MIME("application/x-tar"))
TypeRar    = FileType.Types("rar", MIME("application/vnd.rar"))
TypeGz     = FileType.Types("gz", MIME("application/gzip"))
TypeBz2    = FileType.Types("bz2", MIME("application/x-bzip2"))
Type7z     = FileType.Types("7z", MIME("application/x-7z-compressed"))
TypeXz     = FileType.Types("xz", MIME("application/x-xz"))
TypePdf    = FileType.Types("pdf", MIME("application/pdf"))
TypeExe    = FileType.Types("exe", MIME("application/vnd.microsoft.portable-executable"))
TypeSwf    = FileType.Types("swf", MIME("application/x-shockwave-flash"))
TypeRtf    = FileType.Types("rtf", MIME("application/rtf"))
TypeEot    = FileType.Types("eot", MIME("application/octet-stream"))
TypePs     = FileType.Types("ps", MIME("application/postscript"))
TypeSqlite = FileType.Types("sqlite", MIME("application/vnd.sqlite3"))
TypeNes    = FileType.Types("nes", MIME("application/x-nintendo-nes-rom"))
TypeCrx    = FileType.Types("crx", MIME("application/x-google-chrome-extension"))
TypeCab    = FileType.Types("cab", MIME("application/vnd.ms-cab-compressed"))
TypeDeb    = FileType.Types("deb", MIME("application/vnd.debian.binary-package"))
TypeAr     = FileType.Types("ar", MIME("application/x-unix-archive"))
TypeZ      = FileType.Types("Z", MIME("application/x-compress"))
TypeLz     = FileType.Types("lz", MIME("application/x-lzip"))
TypeRpm    = FileType.Types("rpm", MIME("application/x-rpm"))
TypeElf    = FileType.Types("elf", MIME("application/x-executable"))
TypeDcm    = FileType.Types("dcm", MIME("application/dicom"))
TypeIso    = FileType.Types("iso", MIME("application/x-iso9660-image"))
TypeMachO  = FileType.Types("macho", MIME("application/x-mach-binary")) # Mach-O binaries have no common extension.

function Epub(buf)::Bool
	return length(buf) > 57 &&
		buf[1] == 0x50 && buf[2] == 0x4B && buf[3] == 0x3 && buf[4] == 0x4 &&
		buf[31] == 0x6D && buf[32] == 0x69 && buf[33] == 0x6D && buf[34] == 0x65 &&
		buf[35] == 0x74 && buf[36] == 0x79 && buf[37] == 0x70 && buf[38] == 0x65 &&
		buf[39] == 0x61 && buf[40] == 0x70 && buf[41] == 0x70 && buf[42] == 0x6C &&
		buf[43] == 0x69 && buf[44] == 0x63 && buf[45] == 0x61 && buf[46] == 0x74 &&
		buf[47] == 0x69 && buf[48] == 0x6F && buf[49] == 0x6E && buf[50] == 0x2F &&
		buf[51] == 0x65 && buf[52] == 0x70 && buf[53] == 0x75 && buf[54] == 0x62 &&
		buf[55] == 0x2B && buf[56] == 0x7A && buf[57] == 0x69 && buf[58] == 0x70
end

function Zip(buf)::Bool
	return length(buf) > 3 &&
		buf[1] == 0x50 && buf[2] == 0x4B &&
		(buf[3] == 0x3 || buf[3] == 0x5 || buf[3] == 0x7) &&
		(buf[4] == 0x4 || buf[4] == 0x6 || buf[4] == 0x8)
end

function Tar(buf)::Bool
	return length(buf) > 261 &&
		buf[258] == 0x75 && buf[259] == 0x73 &&
		buf[260] == 0x74 && buf[261] == 0x61 &&
		buf[262] == 0x72
end

function Rar(buf)::Bool
	return length(buf) > 6 &&
		buf[1] == 0x52 && buf[2] == 0x61 && buf[3] == 0x72 &&
		buf[4] == 0x21 && buf[5] == 0x1A && buf[6] == 0x7 &&
		(buf[7] == 0x0 || buf[7] == 0x1)
end

function Gz(buf)::Bool
	return length(buf) > 2 &&
		buf[1] == 0x1F && buf[2] == 0x8B && buf[3] == 0x8
end

function Bz2(buf)::Bool
	return length(buf) > 2 &&
		buf[1] == 0x42 && buf[2] == 0x5A && buf[3] == 0x68
end

function SevenZ(buf)::Bool
	return length(buf) > 5 &&
		buf[1] == 0x37 && buf[2] == 0x7A && buf[3] == 0xBC &&
		buf[4] == 0xAF && buf[5] == 0x27 && buf[6] == 0x1C
end

function Pdf(buf)::Bool
	return length(buf) > 3 &&
		buf[1] == 0x25 && buf[2] == 0x50 &&
		buf[3] == 0x44 && buf[4] == 0x46
end

function Exe(buf)::Bool
	return length(buf) > 1 &&
		buf[1] == 0x4D && buf[2] == 0x5A
end

function Swf(buf)::Bool
	return length(buf) > 2 &&
		(buf[1] == 0x43 || buf[1] == 0x46) &&
		buf[2] == 0x57 && buf[3] == 0x53
end

function Rtf(buf)::Bool
	return length(buf) > 4 &&
		buf[1] == 0x7B && buf[1] == 0x5C &&
		buf[2] == 0x72 && buf[3] == 0x74 &&
		buf[4] == 0x66
end

function Nes(buf)::Bool
	return length(buf) > 3 &&
		buf[1] == 0x4E && buf[2] == 0x45 &&
		buf[3] == 0x53 && buf[4] == 0x1A
end

function Crx(buf)::Bool
	return length(buf) > 3 &&
		buf[1] == 0x43 && buf[2] == 0x72 &&
		buf[3] == 0x32 && buf[4] == 0x34
end

function Cab(buf)::Bool
	return length(buf) > 3 &&
		((buf[1] == 0x4D && buf[2] == 0x53 && buf[3] == 0x43 && buf[4] == 0x46) ||
			(buf[1] == 0x49 && buf[2] == 0x53 && buf[3] == 0x63 && buf[4] == 0x28))
end

function Eot(buf)::Bool
	return length(buf) > 35 &&
		buf[35] == 0x4C && buf[36] == 0x50 &&
		((buf[9] == 0x02 && buf[10] == 0x00 &&
			buf[11] == 0x01) || (buf[9] == 0x01 &&
			buf[10] == 0x00 && buf[11] == 0x00) ||
			(buf[9] == 0x02 && buf[10] == 0x00 &&
				buf[11] == 0x02))
end

function Ps(buf)::Bool
	return length(buf) > 1 &&
		buf[1] == 0x25 && buf[2] == 0x21
end

function Xz(buf)::Bool
	return length(buf) > 5 &&
		buf[1] == 0xFD && buf[2] == 0x37 &&
		buf[3] == 0x7A && buf[4] == 0x58 &&
		buf[5] == 0x5A && buf[6] == 0x00
end

function Sqlite(buf)::Bool
	return length(buf) > 3 &&
		buf[1] == 0x53 && buf[2] == 0x51 &&
		buf[3] == 0x4C && buf[4] == 0x69
end

function Deb(buf)::Bool
	return length(buf) > 20 &&
		buf[1] == 0x21 && buf[2] == 0x3C && buf[3] == 0x61 &&
		buf[4] == 0x72 && buf[5] == 0x63 && buf[6] == 0x68 &&
		buf[7] == 0x3E && buf[8] == 0x0A && buf[9] == 0x64 &&
		buf[10] == 0x65 && buf[11] == 0x62 && buf[12] == 0x69 &&
		buf[13] == 0x61 && buf[14] == 0x6E && buf[15] == 0x2D &&
		buf[16] == 0x62 && buf[17] == 0x69 && buf[18] == 0x6E &&
		buf[19] == 0x61 && buf[20] == 0x72 && buf[21] == 0x79
end

function Ar(buf)::Bool
	return length(buf) > 6 &&
		buf[1] == 0x21 && buf[2] == 0x3C &&
		buf[3] == 0x61 && buf[4] == 0x72 &&
		buf[5] == 0x63 && buf[6] == 0x68 &&
		buf[7] == 0x3E
end

function Z(buf)::Bool
	return length(buf) > 1 &&
		((buf[1] == 0x1F && buf[2] == 0xA0) ||
			(buf[1] == 0x1F && buf[2] == 0x9D))
end

function Lz(buf)::Bool
	return length(buf) > 3 &&
		buf[1] == 0x4C && buf[2] == 0x5A &&
		buf[3] == 0x49 && buf[4] == 0x50
end

function Rpm(buf)::Bool
	return length(buf) > 96 &&
		buf[1] == 0xED && buf[2] == 0xAB &&
		buf[3] == 0xEE && buf[4] == 0xDB
end

function Elf(buf)::Bool
	return length(buf) > 52 &&
		buf[1] == 0x7F && buf[2] == 0x45 &&
		buf[3] == 0x4C && buf[4] == 0x46
end

function Dcm(buf)::Bool
	return length(buf) > 131 &&
		buf[128] == 0x44 && buf[129] == 0x49 &&
		buf[130] == 0x43 && buf[131] == 0x4D
end

function Iso(buf)::Bool
	return length(buf) > 32773 &&
		buf[32770] == 0x43 && buf[32771] == 0x44 &&
		buf[32772] == 0x30 && buf[32773] == 0x30 &&
		buf[32774] == 0x31
end

function MachO(buf)::Bool
	return length(buf) > 3 && ((buf[1] == 0xFE && buf[2] == 0xED && buf[3] == 0xFA && buf[4] == 0xCF) ||
		(buf[1] == 0xFE && buf[2] == 0xED && buf[3] == 0xFA && buf[4] == 0xCE) ||
		(buf[1] == 0xBE && buf[2] == 0xBA && buf[3] == 0xFE && buf[4] == 0xCA) ||
		#Big endian versions below here...
		(buf[1] == 0xCF && buf[2] == 0xFA && buf[3] == 0xED && buf[4] == 0xFE) ||
		(buf[1] == 0xCE && buf[2] == 0xFA && buf[3] == 0xED && buf[4] == 0xFE) ||
		(buf[1] == 0xCA && buf[2] == 0xFE && buf[3] == 0xBA && buf[4] == 0xBE))
end

Archive = Dict(
	TypeEpub=>   Epub,
	TypeZip=>  Zip,
	TypeTar=>    Tar,
	TypeRar=>    Rar,
	TypeGz=>     Gz,
	TypeBz2=>    Bz2,
	Type7z=>     SevenZ,
	TypeXz=>     Xz,
	TypePdf=>    Pdf,
	TypeExe=>    Exe,
	TypeSwf=>    Swf,
	TypeRtf=>    Rtf,
	TypeEot=>    Eot,
	TypePs=>     Ps,
	TypeSqlite=> Sqlite,
	TypeNes=>    Nes,
	TypeCrx=>    Crx,
	TypeCab=>    Cab,
	TypeDeb=>    Deb,
	TypeAr=>     Ar,
	TypeZ=>      Z,
	TypeLz=>     Lz,
	TypeRpm=>    Rpm,
	TypeElf=>    Elf,
	TypeDcm=>    Dcm,
	TypeIso=>    Iso,
	TypeMachO=>  MachO,
)

end


text:
local integer::Int
local boolean::Bool
local string::String

integer = 20
boolean = true
string = "Ah que legal!"

string = string * boolean

if boolean
if string == "Ah que legal!true"
println(integer + 13)
end
end


text:
## Leer datos
using Random

## Leer datos
include("../../utils/preprocesado_datos.jl")
include("../../utils/validation.jl")
include("../../BMB/BMB.jl")

file_path= "src/Instancias_APC/"
struct DataFile
    route::String
    class_atributte::String
end

files = [
    DataFile(file_path*"ionosphere.arff", "class"),
    DataFile(file_path*"parkinsons.arff", "Class"),
    DataFile(file_path*"spectf-heart.arff", "Class"),
]
# ¡Tiene que acabar en /
csv_file_path  = "src/resultados/BMB/"
prefijo = "BMB-"
process_name = [
    DataFile(csv_file_path*prefijo*"ionosphere.result.csv", "Datos Iosfera"),
    DataFile(csv_file_path*prefijo*"parkinsons.result.csv", "Datos parkinson "),
    DataFile(csv_file_path*prefijo*"spectf-heart.result.csv", "Datos ataques corazón")
]
Random.seed!(0)
println("Procedemos a calcular los datos con $(Threads.nthreads()) hebras disponibles")
# Leemos los argumentos para saber el fichero que se quiere ejecutar
# si el argumento no existe, hay ambigüedad o no es un ínice de ficheros se ejecutan todos
if length(ARGS) == 1 && parse(Int,ARGS[1]) in [1,2,3]
    n = parse( Int, ARGS[1] )
    inicio = n
    final = n
else
    inicio = 1
    final = length(files)
end

for i in inicio:final
    # Seleccionamos parkinson
    file = files[i]
    data , labels = DataLabelArff(file.route, file.class_atributte)
    ## Entrenar para obtener el peso
    println("==============================================")
    println("$(process_name[i].class_atributte)")

    ## Evaluar resultado con cross validation
    VerboseCrossValidation(data, labels, 5, BMB_LearnerOneNN, process_name[i].route)
end