"""
    struct CertificateMonomials <: MOI.AbstractConstraintAttribute end

A constraint attribute for the monomials indexing the
[`GramMatrixAttribute`](@ref) and [`MomentMatrixAttribute`](@ref) certificates.
"""
struct CertificateMonomials <: MOI.AbstractConstraintAttribute end

"""
    struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute end

A constraint attribute for the [`GramMatrix`](@ref) of a constraint, that is,
the positive semidefinte matrix `Q` indexed by the monomials in the vector `X`
such that ``X^\\top Q X`` is the sum-of-squares certificate of the constraint.
The
"""
struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute end

"""
    struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute end

A constraint attribute fot the `MomentMatrix` of a constraint.
"""
struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute end

"""
    struct LagrangianMultipliers <: MOI.AbstractConstraintAttribute end

A constraint attribute fot the `LagrangianMultipliers` assiciated to the
inequalities of the domain of a constraint. There is one multiplier per
inequality and no multiplier for equalities as the equalities are handled by
reducing the polynomials over the ideal they generate instead of explicitely
creating multipliers.
"""
struct LagrangianMultipliers <: MOI.AbstractConstraintAttribute end

function MOI.is_set_by_optimize(::Union{CertificateMonomials,
                                        GramMatrixAttribute,
                                        MomentMatrixAttribute,
                                        LagrangianMultipliers})
    return true
end


module Test_Collision

using Modia3D
using Modia3D.StaticArrays
using Modia3D.ModiaMath
using Modia3D.Unitful

# Materials
massProperties = "Aluminium"
vmat1   = Modia3D.Material(color="LightBlue", transparency=0.5, wireframe=false)
vblue   = Modia3D.Material(color="LightBlue" , transparency=0.5)
vred    = Modia3D.Material(color="Red", transparency=0.5)
vgreen  = Modia3D.Material(color="Green", transparency=0.5)
vblack  = Modia3D.Material(color="Black", transparency=0.5)
vyellow = Modia3D.Material(color="Yellow", transparency=0.5)
vpink   = Modia3D.Material(color="Pink", transparency=0.5)
cmat    = "Steel"
vMatAABB = Modia3D.Material(color="Grey", transparency=0.95)

# dimensions
Lx  = 0.7
Dx = 0.5
Ly    = 0.2
Lz   = 1.0

groundWidth  = 1.0
groundHeight = 0.01
cyl  = Modia3D.Cylinder(1.2*Ly, Ly/2)
Tcyl = ModiaMath.rot1(90u"°")
TGround = ModiaMath.rot1(-90u"°")

@assembly CollisionSolids(world) begin
world_f1 = Modia3D.Object3D(world , r=[0.5*Lx, 0.0, groundWidth/2])

blau1     = Modia3D.Part(Modia3D.Solid(Modia3D.SolidBox(Lx,Ly,Lz), "Aluminium", vblue;contactMaterial = cmat),    [ [0.0, 0.0, -Lz/2],[ 0.0, 0.0, Lz/2] ] )
blau2     = Modia3D.Part(Modia3D.Solid(Modia3D.SolidBox(Lx,Ly,Lz), "Aluminium", vblue; contactMaterial = cmat),   [ [0.0, 0.0, -Lz/2], [ 0.0, 0.0, Lz/2] ] )

schwarz1  = Modia3D.Part(Modia3D.Solid(Modia3D.SolidCone(Dx,Lz; relativeTipDiameter=0.0), nothing, vblack; contactMaterial = cmat),  [ [0.0, 0.0, 0.0]] )
rosa1     = Modia3D.Part(Modia3D.Solid(Modia3D.SolidBox(Lx,Ly,Lz), "Aluminium", vpink; contactMaterial = cmat),   [ [0.0, 0.0, -Lz/2],[ 0.0, 0.0, Lz/2] ] )
green1    = Modia3D.Part(Modia3D.Solid(Modia3D.SolidBeam(Lx,Ly,Lz), nothing, vgreen; contactMaterial = cmat),     [ [0.0, 0.0, -Lz/2],[ 0.0, 0.0, Lz/2] ] )
green2    = Modia3D.Part(Modia3D.Solid(Modia3D.SolidBeam(Lx,Ly,Lz), nothing, vgreen; contactMaterial = cmat),     [ [0.0, 0.0, -Lz/2]] )
gelb1     = Modia3D.Part(Modia3D.Solid(Modia3D.SolidBox(Lx,Ly,Lz), "Aluminium", vyellow; contactMaterial = cmat), [ [0.0, 0.0, -Lz/2],[ 0.0, 0.0, Lz/2] ] )
gelb2     = Modia3D.Part(Modia3D.Solid(Modia3D.SolidEllipsoid(Lx,Ly,Lz), "Aluminium", vyellow; contactMaterial = cmat), [ [0.0, 0.0, -Lz/2] ] )
gelb3     = Modia3D.Part(Modia3D.Solid(Modia3D.SolidSphere(Dx), "Aluminium", vyellow; contactMaterial = cmat),    [ [0.0, 0.0, -Lz/2] ] )
rotM1      = Modia3D.Part(Modia3D.Solid(Modia3D.SolidPipe(Dx,Lz), nothing, vred; contactMaterial = cmat),          [[0.0, 0.0, -Lz/2] , [ 0.0, 0.0,Lz/2]] )
rotM2      = Modia3D.Part(Modia3D.Solid(Modia3D.SolidCylinder(Dx, Lz), nothing, vred; contactMaterial = cmat),     [[0.0, 0.0, -Lz/2], [ 0.0, 0.0,Lz/2]])

Modia3D.connect(world_f1, blau1.frames[1]; R=ModiaMath.rot2(160u"°"))
Modia3D.connect(world_f1, blau2.frames[1]; R=ModiaMath.rot2(45u"°"))

rev2 = Modia3D.Revolute(blau2.frames[2], rosa1.frames[1]; phi_start =-pi/2)

rev5 = Modia3D.Revolute(blau1.frames[2], schwarz1.frames[1]; phi_start =-pi/2)

rev3 = Modia3D.Revolute(rosa1.frames[2], green1.frames[1])

Modia3D.connect(green1.frame0, green2.frames[1]; R=ModiaMath.rot2(45u"°"))
rev4 = Modia3D.Revolute(green1.frames[2], gelb1.frames[1])

Modia3D.connect(gelb1.frames[2],gelb2.frames[1]; R=ModiaMath.rot2(130u"°"))
Modia3D.connect(gelb1.frames[2], gelb3.frames[1]; R=ModiaMath.rot2(90u"°"))

Modia3D.connect(world, rotM1.frames[1]; fixed=false, R=ModiaMath.rot3(90u"°")) #
Modia3D.connect(rotM1.frames[2], rotM2.frames[1]; R=ModiaMath.rot3(45u"°"))

end

# Bounding Boxes AABB
@assembly BoundingBoxes(world) begin
  blau1AABB     = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  blau2AABB     = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  rotM1AABB      = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  rotM2AABB      = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  schwarz1AABB  = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  rosa1AABB     = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  green1AABB    = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  green2AABB    = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  gelb1AABB     = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  gelb2AABB     = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
  gelb3AABB     = Modia3D.Object3D(world,  Modia3D.Box(0.9,0.9,0.9      , material=vMatAABB))
end

@assembly CollisionTest() begin
  world = Modia3D.Object3D(Modia3D.CoordinateSystem(1.0))

  collisionSolids = CollisionSolids(world)
  boundingBoxes   = BoundingBoxes(world)
end

collisionTest = CollisionTest(sceneOptions=Modia3D.SceneOptions(visualizeFrames=true, defaultFrameLength=0.3,nz_max = 80))

#collisionTest = CollisionTest()
Modia3D.initAnalysis!(collisionTest)

AABB = collisionTest._internal.scene.options.contactDetection.contactPairs.AABB

tStart=0.0
tEnd  =1.0

for time = range(tStart, stop=tEnd, length=101)
  s = Modia3D.linearMovement(2*Lx, tStart, tEnd, time)
  delta_phi = Modia3D.linearMovement(pi/3, tStart, tEnd, time)

  Modia3D.setAngle!(collisionTest.collisionSolids.rev2,-pi/2 + delta_phi)
  Modia3D.setAngle!(collisionTest.collisionSolids.rev3, delta_phi)
  Modia3D.setAngle!(collisionTest.collisionSolids.rev4,-delta_phi)
  Modia3D.setAngle!(collisionTest.collisionSolids.rev5, delta_phi)

  Modia3D.set_r!(collisionTest.collisionSolids.rotM1.frames[1], [0, 0, -s])

  Modia3D.updatePosition!(collisionTest)
  Modia3D.selectContactPairsWithEvent!(collisionTest)

  collisionTest.boundingBoxes.blau1AABB.data.Lx = abs(AABB[1][1].x_max - AABB[1][1].x_min)
  collisionTest.boundingBoxes.blau1AABB.data.Ly = abs(AABB[1][1].y_max - AABB[1][1].y_min)
  collisionTest.boundingBoxes.blau1AABB.data.Lz = abs(AABB[1][1].z_max - AABB[1][1].z_min)
  collisionTest.boundingBoxes.blau1AABB.r_abs = collisionTest.collisionSolids.blau1.frame0.r_abs

  collisionTest.boundingBoxes.blau2AABB.data.Lx = abs(AABB[1][2].x_max - AABB[1][2].x_min)
  collisionTest.boundingBoxes.blau2AABB.data.Ly = abs(AABB[1][2].y_max - AABB[1][2].y_min)
  collisionTest.boundingBoxes.blau2AABB.data.Lz = abs(AABB[1][2].z_max - AABB[1][2].z_min)
  collisionTest.boundingBoxes.blau2AABB.r_abs = collisionTest.collisionSolids.blau2.frame0.r_abs

  collisionTest.boundingBoxes.rotM1AABB.data.Lx = abs(AABB[2][1].x_max - AABB[2][1].x_min)
  collisionTest.boundingBoxes.rotM1AABB.data.Ly = abs(AABB[2][1].y_max - AABB[2][1].y_min)
  collisionTest.boundingBoxes.rotM1AABB.data.Lz = abs(AABB[2][1].z_max - AABB[2][1].z_min)
  collisionTest.boundingBoxes.rotM1AABB.r_abs = collisionTest.collisionSolids.rotM1.frame0.r_abs

  collisionTest.boundingBoxes.rotM2AABB.data.Lx = abs(AABB[2][2].x_max - AABB[2][2].x_min)
  collisionTest.boundingBoxes.rotM2AABB.data.Ly = abs(AABB[2][2].y_max - AABB[2][2].y_min)
  collisionTest.boundingBoxes.rotM2AABB.data.Lz = abs(AABB[2][2].z_max - AABB[2][2].z_min)
  collisionTest.boundingBoxes.rotM2AABB.r_abs = collisionTest.collisionSolids.rotM2.frame0.r_abs

  collisionTest.boundingBoxes.schwarz1AABB.data.Lx = abs(AABB[3][1].x_max - AABB[3][1].x_min)
  collisionTest.boundingBoxes.schwarz1AABB.data.Ly = abs(AABB[3][1].y_max - AABB[3][1].y_min)
  collisionTest.boundingBoxes.schwarz1AABB.data.Lz = abs(AABB[3][1].z_max - AABB[3][1].z_min)
  collisionTest.boundingBoxes.schwarz1AABB.r_abs =  [(AABB[3][1].x_max+AABB[3][1].x_min)/2,(AABB[3][1].y_max+AABB[3][1].y_min)/2,(AABB[3][1].z_max+AABB[3][1].z_min)/2 ]

  collisionTest.boundingBoxes.rosa1AABB.data.Lx = abs(AABB[4][1].x_max - AABB[4][1].x_min)
  collisionTest.boundingBoxes.rosa1AABB.data.Ly = abs(AABB[4][1].y_max - AABB[4][1].y_min)
  collisionTest.boundingBoxes.rosa1AABB.data.Lz = abs(AABB[4][1].z_max - AABB[4][1].z_min)
  collisionTest.boundingBoxes.rosa1AABB.r_abs = collisionTest.collisionSolids.rosa1.frame0.r_abs

  collisionTest.boundingBoxes.green1AABB.data.Lx = abs(AABB[5][1].x_max - AABB[5][1].x_min)
  collisionTest.boundingBoxes.green1AABB.data.Ly = abs(AABB[5][1].y_max - AABB[5][1].y_min)
  collisionTest.boundingBoxes.green1AABB.data.Lz = abs(AABB[5][1].z_max - AABB[5][1].z_min)
  collisionTest.boundingBoxes.green1AABB.r_abs = collisionTest.collisionSolids.green1.frame0.r_abs

  collisionTest.boundingBoxes.green2AABB.data.Lx = abs(AABB[5][2].x_max - AABB[5][2].x_min)
  collisionTest.boundingBoxes.green2AABB.data.Ly = abs(AABB[5][2].y_max - AABB[5][2].y_min)
  collisionTest.boundingBoxes.green2AABB.data.Lz = abs(AABB[5][2].z_max - AABB[5][2].z_min)
  collisionTest.boundingBoxes.green2AABB.r_abs = collisionTest.collisionSolids.green2.frame0.r_abs

  collisionTest.boundingBoxes.gelb1AABB.data.Lx = abs(AABB[6][1].x_max - AABB[6][1].x_min)
  collisionTest.boundingBoxes.gelb1AABB.data.Ly = abs(AABB[6][1].y_max - AABB[6][1].y_min)
  collisionTest.boundingBoxes.gelb1AABB.data.Lz = abs(AABB[6][1].z_max - AABB[6][1].z_min)
  collisionTest.boundingBoxes.gelb1AABB.r_abs = collisionTest.collisionSolids.gelb1.frame0.r_abs

  collisionTest.boundingBoxes.gelb2AABB.data.Lx = abs(AABB[6][2].x_max - AABB[6][2].x_min)
  collisionTest.boundingBoxes.gelb2AABB.data.Ly = abs(AABB[6][2].y_max - AABB[6][2].y_min)
  collisionTest.boundingBoxes.gelb2AABB.data.Lz = abs(AABB[6][2].z_max - AABB[6][2].z_min)
  collisionTest.boundingBoxes.gelb2AABB.r_abs = collisionTest.collisionSolids.gelb3.frame0.r_abs

  collisionTest.boundingBoxes.gelb3AABB.data.Lx = abs(AABB[6][3].x_max - AABB[6][3].x_min)
  collisionTest.boundingBoxes.gelb3AABB.data.Ly = abs(AABB[6][3].y_max - AABB[6][3].y_min)
  collisionTest.boundingBoxes.gelb3AABB.data.Lz = abs(AABB[6][3].z_max - AABB[6][3].z_min)
  collisionTest.boundingBoxes.gelb3AABB.r_abs = collisionTest.collisionSolids.gelb2.frame0.r_abs

  Modia3D.setComputationFlag(collisionTest)
  Modia3D.visualize!(collisionTest,time)

#=
  time += 0.01
  s = Modia3D.linearMovement(2*Lx, tStart, tEnd, time)
  Modia3D.set_r!(rotM1, [0, 0, s])
  Modia3D.updatePosition!(world)
  Modia3D.selectContactPairsWithEvent!(world)
  Modia3D.setComputationFlag(world)
  Modia3D.visualize!(world,time)
=#
  time += 0.01
end

Modia3D.closeAnalysis!(collisionTest)
println("... success of Test_Collision.jl!")
end


using Colors

abstract type ColoredObject <: Object end
abstract type Pigment <: JR end

export FT
struct FT <: Pigment
    filter::Float64
    transmit::Float64

    function FT(filter,transmit)
        if filter<0 || transmit<0 || filter+transmit>1
            error("だめです")
        else
            new(filter,transmit)
        end
    end
end

export rgbColor
struct rgbColor <: ColoredObject
    object :: Object
    color :: Color

    function rgbColor(object,color)
        if object == Empty()
            Empty()
        else
            new(object,color)
        end
    end
end

export rgbftColor
struct rgbftColor <:ColoredObject
    object :: Object
    color :: Color
    transparence :: FT

    function rgbftColor(object,color,ft)
        if object == Empty()
            Empty()
        else
            new(object,color,ft)
        end
    end
end

export rgbftColor2
struct rgbftColor2 <:ColoredObject
    object :: Object
    color :: Color
    transparence :: FT

    function rgbftColor2(object,color,ft)
        if object == Empty()
            Empty()
        else
            new(object,color,ft)
        end
    end
end

# Color
function povray_script(color :: Color)
    r = string(Float64(color.r))
    g = string(Float64(color.g))
    b = string(Float64(color.b))
    return "rgb<"*r*","*g*","*b*">"
end

function povray_script(color :: Color, ft:: FT)
    r = string(Float64(color.r))
    g = string(Float64(color.g))
    b = string(Float64(color.b))
    f = string(Float64(ft.filter))
    t = string(Float64(ft.transmit))
    return "rgbft<"*r*","*g*","*b*","*f*","*t*">"
end

function povray_script(rgbcolor :: rgbColor; preindent = 0)
    script = "object{\n" * "  "^(preindent+1)
    script *= povray_script(rgbcolor.object, preindent=preindent+1) * "\n" * "  "^(preindent+1)
    script *= "pigment{" * povray_script(rgbcolor.color) * "}" * "\n" * "  "^preindent
    script *= "}"
    return script
end

function povray_script(rgbftcolor :: rgbftColor; preindent = 0)
    r = string(Float64(rgbftcolor.color.r))
    g = string(Float64(rgbftcolor.color.g))
    b = string(Float64(rgbftcolor.color.b))
    f = string(Float64(rgbftcolor.transparence.filter))
    t = string(Float64(rgbftcolor.transparence.transmit))

    script = "object{\n" * "  "^(preindent+1)
    script *= povray_script(rgbftcolor.object, preindent=preindent+1) * "\n" * "  "^(preindent+1)
    script *= "pigment{rgbft<"*r*","*g*","*b*","*f*","*t*">}" * "\n" * "  "^preindent
    script *= "}"
    return script
end

function povray_script(rgbftcolor :: rgbftColor2; preindent = 0)
    r = string(Float64(rgbftcolor.color.r))
    g = string(Float64(rgbftcolor.color.g))
    b = string(Float64(rgbftcolor.color.b))
    f = string(Float64(rgbftcolor.transparence.filter))
    t = string(Float64(rgbftcolor.transparence.transmit))

    script = "object{\n" * "  "^(preindent+1)
    script *= povray_script(rgbftcolor.object, preindent=preindent+1) * "\n" * "  "^(preindent+1)
    script *= "texture{pigment{rgbft<"*r*","*g*","*b*","*f*","*t*">}}" * "\n" * "  "^preindent
    script *= "texture{pigment{rgbft<"*r*","*g*","*b*","*f*","*t*">}}" * "\n" * "  "^preindent
    script *= "}"
    return script
end

export Transparent
function Transparent(object::PrimitiveObject,ft::FT)
    return object
end
function Transparent(csg::csgObject,ft::FT)
    csgtype = typeof(csg)
    return csgtype(map(obj->Transparent(obj,ft),csg.objects))
end

function Transparent(rgbcolor::rgbColor,ft::FT)
    return rgbftColor(rgbcolor.object,rgbcolor.color,ft)
end
function Transparent(rgbftcolor::rgbftColor,ft::FT)
    f0 = rgbftcolor.transparence.filter
    t0 = rgbftcolor.transparence.transmit
    f1 = ft.filter
    t1 = ft.transmit
    f2 = 1-(1-f0)*(1-f1)
    t2 = 1-(1-t0)*(1-t1)
    return rgbftColor(rgbftcolor.object,rgbftcolor.color,FT(f2,t2))
end
function Transparent(affinetransform::AffineTransform,ft::FT)
    return AffineTransform(Transparent(affinetransform.object,ft),affinetransform.matrix, affinetransform.vector)
end
function Transparent(paralleltranslation::ParallelTranslation,ft::FT)
    return ParallelTranslation(Transparent(paralleltranslation.object,ft),paralleltranslation.vector)
end
function Transparent(scaling::Scaling,ft::FT)
    return Scaling(Transparent(scaling.object,ft),scaling.scalar)
end
#TODO: Transparent for rgbftColors


@testset "generate.jl" begin
    manager = ExperimentManager(db="choleskytest")
    init_manager(manager)
    @testset "gendecomposition" begin
        instance_name, oid = gendecomposition(manager, "./case9.m",
                                              Dict(:degree_max => Dict(:degree => 10)),
                                              Dict(:degree_max => Dict(:degree => 10)),
                                              3)
        @test isdecomposition(manager.decompositions, instance_name, oid)
        decomposition = getdecomposition(manager.decompositions, instance_name, oid)
        @test haskey(decomposition["options_src"], "degree_max")
        @test haskey(decomposition["options_src"]["degree_max"], "degree")
        @test decomposition["options_src"]["degree_max"]["degree"] == 10
        @test haskey(decomposition["options_dst"], "degree_max")
        @test haskey(decomposition["options_dst"]["degree_max"], "degree")
        @test decomposition["options_dst"]["degree_max"]["degree"] == 10
        @test decomposition["nb_added_edges"] == 3
    end
    @testset "gendecomposition_all" begin
        instances_names, oids = gendecomposition_all(manager,
                                                     Dict(:degree_min => Dict(:degree => 0)),
                                                     Dict(:degree_min => Dict(:degree => 0)),
                                                     2)
        for (instance_name, oid) in zip(instances_names, oids)
            @test isdecomposition(manager.decompositions, instance_name, oid)
            decomposition = getdecomposition(manager.decompositions, instance_name, oid)
            @test haskey(decomposition["options_src"], "degree_min")
            @test haskey(decomposition["options_src"]["degree_min"], "degree")
            @test decomposition["options_src"]["degree_min"]["degree"] == 0
            @test haskey(decomposition["options_dst"], "degree_min")
            @test haskey(decomposition["options_dst"]["degree_min"], "degree")
            @test decomposition["options_dst"]["degree_min"]["degree"] == 0
            @test decomposition["nb_added_edges"] == 2
        end
    end
    @testset "gendecomposition_all_json" begin
        instances_names, oids = gendecomposition_all_json(manager, "config.json")
        for (instance_name, oid) in zip(instances_names, oids)
            @test isdecomposition(manager.decompositions, instance_name, oid)
            decomposition = getdecomposition(manager.decompositions, instance_name, oid)
            @test haskey(decomposition["options_src"], "size_biggest_clique_max")
            @test haskey(decomposition["options_src"]["size_biggest_clique_max"], "size")
            @test decomposition["options_src"]["size_biggest_clique_max"]["size"] == 9
            @test haskey(decomposition["options_dst"], "size_biggest_clique_max")
            @test haskey(decomposition["options_dst"]["size_biggest_clique_max"], "size")
            @test decomposition["options_dst"]["size_biggest_clique_max"]["size"] == 9
            @test decomposition["nb_added_edges"] == 4
        end
    end
    @testset "gendecomposition_all_jsons" begin
        ids = gendecomposition_all_jsons(manager, "config_mult.json")
        for (instance_name, oid) in zip(ids["gen1"]...)
            @test isdecomposition(manager.decompositions, instance_name, oid)
            decomposition = getdecomposition(manager.decompositions, instance_name, oid)
            @test haskey(decomposition["options_src"], "degree")
            @test haskey(decomposition["options_src"]["degree"], "degrees")
            @test decomposition["options_src"]["degree"]["degrees"] == [1, 2, 3, 4, 5, 6, 7, 8, 9]
            @test haskey(decomposition["options_dst"], "degree")
            @test haskey(decomposition["options_dst"]["degree"], "degrees")
            @test decomposition["options_dst"]["degree"]["degrees"] == [1, 2, 3, 4, 5, 6, 7, 8, 9]
        end
        for (instance_name, oid) in zip(ids["gen2"]...)
            @test isdecomposition(manager.decompositions, instance_name, oid)
            decomposition = getdecomposition(manager.decompositions, instance_name, oid)
            @test haskey(decomposition["options_src"], "size_smallest_clique_max")
            @test haskey(decomposition["options_src"]["size_smallest_clique_max"], "size")
            @test decomposition["options_src"]["size_smallest_clique_max"]["size"] == 9
            @test haskey(decomposition["options_dst"], "size_smallest_clique_max")
            @test haskey(decomposition["options_dst"]["size_smallest_clique_max"], "size")
            @test decomposition["options_dst"]["size_smallest_clique_max"]["size"] == 9
            @test decomposition["nb_added_edges"] == 4
        end
    end
    @testset "genoriginal" begin
        instance_name, oid = genoriginal(manager, "case9.m")
        @test isdecomposition(manager.decompositions, instance_name, oid)
        decomposition = getdecomposition(manager.decompositions, instance_name, oid)
        @test isempty(decomposition["options_src"])
        @test isempty(decomposition["options_dst"])
        @test decomposition["nb_added_edges"] == 0
    end
end


module FixtureLearners

importall Orchestra.Types
importall Orchestra.Util

export MLProblem,
       Classification,
       FeatureClassification,
       NumericFeatureClassification,
       PerfectScoreLearner,
       AlwaysSameLabelLearner,
       fit_and_transform!,
       fit!,
       transform!

abstract MLProblem
abstract Classification <: MLProblem

# NOTE(svs14): Currently hardcoded example.
#              Consider turning into rule-based generator.
train_dataset = [
  1.0        1 "b"  2 "c" "a";
  2.0        2 "b"  3 "c" "a";
  nan(3.0)   3 "b"  4 "c" "a";
  -1.0      -1 "d" -2 "c" "b";
  -2.0      -2 "d" -3 "c" "b";
  nan(-3.0) -3 "d" -4 "c" "b";
  1.0        1 "a"  1 "a" "c";
  2.0        2 "b"  2 "b" "c";
  nan(3.0)   3 "c"  3 "c" "c";
  0.0        0 "e"  1 "a" "d";
  0.0        0 "e"  2 "b" "d";
  nan(0.0)   0 "e"  3 "c" "d";
]
test_dataset = [
  4.0        4 "b"  5 "c" "a";
  nan(5.0)   5 "b"  6 "c" "a";
  -4.0      -4 "d" -5 "c" "b";
  nan(-5.0) -5 "d" -6 "c" "b";
  4.0        4 "d"  4 "d" "c";
  nan(5.0)   5 "e"  5 "e" "c";
  0.0        0 "e"  4 "d" "d";
  nan(0.0)   0 "e"  5 "e" "d";
]

type FeatureClassification <: Classification
  train_instances::Matrix
  test_instances::Matrix
  train_labels::Vector
  test_labels::Vector

  function FeatureClassification()
    train_instances = train_dataset[:, 1:end-1]
    test_instances = test_dataset[:, 1:end-1]
    train_labels = train_dataset[:, end]
    test_labels = test_dataset[:, end]
    new(
      train_instances,
      test_instances,
      train_labels,
      test_labels
    )
  end
end

type NumericFeatureClassification <: Classification
  train_instances::Matrix
  test_instances::Matrix
  train_labels::Vector
  test_labels::Vector

  function NumericFeatureClassification()
    train_instances = convert(Array{Real, 2}, train_dataset[:, [2,4]])
    test_instances = convert(Array{Real, 2}, test_dataset[:, [2,4]])
    train_labels = convert(Array{String, 1}, train_dataset[:, end])
    test_labels = convert(Array{String, 1}, test_dataset[:, end])
    new(
      train_instances,
      test_instances,
      train_labels,
      test_labels
    )
  end
end

function fit_and_transform!(transformer::Transformer, problem::MLProblem, seed=1)
    srand(seed)
    fit!(transformer, problem.train_instances, problem.train_labels)
    return transform!(transformer, problem.test_instances)
end

type PerfectScoreLearner <: TestLearner
  model
  options

  function PerfectScoreLearner(options=Dict())
    default_options = {
      :output => :class,
      :problem => NumericFeatureClassification()
    }
    new(nothing, nested_dict_merge(default_options, options))
  end
end

function fit!(
  psl::PerfectScoreLearner, instances::Matrix, labels::Vector)

  problem = psl.options[:problem]

  dataset = [
    problem.train_instances problem.train_labels;
    problem.test_instances problem.test_labels
  ]
  instance_label_map = [
    dataset[i,1:2] => dataset[i,3] for i=1:size(dataset, 1)
  ]

  psl.model = {
    :map => instance_label_map
  }
end

function transform!(
  psl::PerfectScoreLearner, instances::Matrix)

  num_instances = size(instances, 1)
  predictions = Array(String, num_instances)
  for i in 1:num_instances
    predictions[i] = psl.model[:map][instances[i,:]]
  end
  return predictions
end

type AlwaysSameLabelLearner <: TestLearner
  model
  options

  function AlwaysSameLabelLearner(options=Dict())
    default_options = {
      :output => :class,
      :label => nothing
    }
    new(nothing, nested_dict_merge(default_options, options))
  end
end

function fit!(awsl::AlwaysSameLabelLearner, instances::Matrix, labels::Vector)
  if awsl.options[:label] == nothing
    awsl.model = {
      :label => first(labels)
    }
  else
    awsl.model = {
      :label => awsl.options[:label]
    }
  end
end

function transform!(awsl::AlwaysSameLabelLearner, instances::Matrix)
  return fill(awsl.model[:label], size(instances, 1))
end

end # module


#Structures

density,fourier,intensity = createSphericalHarmonicsStructure("$(ENV["DETERMINATION_DATA"])/structures/crambin.pdb", LMAX, KMAX, float(KMAX))
densityCube,fourierCube,intensityCube = createCubicStructure("$(ENV["DETERMINATION_DATA"])/structures/crambin.pdb", 2*KMAX+1, float(KMAX))
# @test_approx_eq_eps similarity(getCube(density), densityCube) 1.0 1e-1
# @test_approx_eq_eps similarity(getCube(intensity), intensityCube) 1.0 1e-2


@testset "bishop.jl" begin
    @printf("> bishop\n")

    @test Bb.BISHOP_MASKS[p2u["a1"]] == Bb.BISHOP_MASKS[p2u["h8"]]
    @test Bb.BISHOP_MASKS[p2u["a8"]] == Bb.BISHOP_MASKS[p2u["h1"]]
    @test Bb.diagoSlide(p2u["e3"]) == Bb.BISHOP_MASKS[p2u["e3"]]
    @test Bb.bishopMasksGen()[p2u["d6"]] == reduce(|, [p2u[s] for s = ["b4", "c5", "c7", "e7", "e5", "f4", "g3"]])
end


module Runtests

import Test
using  ModiaLang

@time Test.@testset verbose=true "Modia3D" begin
    usePlotPackage("SilentNoPlot")

    Test.@testset "Basic" begin
        include(joinpath("Basic", "AllShapes.jl"))
        include(joinpath("Basic", "PendulumWithBar1.jl"))
        include(joinpath("Basic", "PendulumWithBar2.jl"))
        include(joinpath("Basic", "PendulumWithDamper.jl"))
        include(joinpath("Basic", "PendulumWithFix.jl"))
        include(joinpath("Basic", "PendulumWithParameterizedDamper.jl"))
        include(joinpath("Basic", "PendulumWithSpring.jl"))
        include(joinpath("Basic", "DoublePendulumWithDampers.jl"))
        include(joinpath("Basic", "Mobile.jl"))
        include(joinpath("Basic", "ShaftFreeMotion.jl"))
        Test.@test_throws LoadError include(joinpath("Basic", "Object3DWithoutParentError.jl"))
    end

    Test.@testset "Robot" begin
        include(joinpath("Robot", "ServoWithRamp.jl"))
        include(joinpath("Robot", "ServoWithRampAndPrismatic.jl"))
        include(joinpath("Robot", "ServoWithRampAndRevolute.jl"))
        include(joinpath("Robot", "ServoWithPathAndRevolute.jl"))
        include(joinpath("Robot", "YouBotWithSphere.jl"))
        Test.@test_skip include(joinpath("Robot", "YouBotPingPong.jl")) # too long computation time
        include(joinpath("Robot", "YouBotGripping.jl"))
        Test.@test_skip include(joinpath("Robot", "YouBotsGripping.jl")) # too long computation time
    end

    Test.@testset "Collision" begin
        include(joinpath("Collision", "BouncingSphere.jl"))
        include(joinpath("Collision", "BouncingSphereFreeMotion.jl"))
        include(joinpath("Collision", "BouncingEllipsoid.jl"))
        include(joinpath("Collision", "BouncingCones.jl"))
        include(joinpath("Collision", "BouncingCapsules.jl"))
        include(joinpath("Collision", "BouncingBeams.jl"))
        include(joinpath("Collision", "TwoCollidingBalls.jl"))
        include(joinpath("Collision", "TwoCollidingBoxes.jl"))
        include(joinpath("Collision", "CollidingCylinders.jl"))
        include(joinpath("Collision", "CollidingSphereWithBunnies.jl"))
        include(joinpath("Collision", "NewtonsCradle.jl"))
        include(joinpath("Collision", "Billard4Balls.jl"))
        Test.@test_skip include(joinpath("Collision", "Billard16Balls.jl"))  # too long computation time
    end

    Test.@testset "Tutorial" begin
        include(joinpath("Tutorial", "Pendulum1.jl"))
        include(joinpath("Tutorial", "Pendulum2.jl"))
        include(joinpath("Tutorial", "Pendulum3.jl"))
        include(joinpath("Tutorial", "BouncingSphere.jl"))
    end

    Test.@testset "old" begin
        include(joinpath("old", "Move_Pendulum.jl"))
        include(joinpath("old", "Plot_cor.jl"))
        include(joinpath("old", "Plot_SlidingFriction.jl"))
        include(joinpath("old", "test_Shapes.jl"))
        include(joinpath("old", "Test_PathPlanning.jl"))
        include(joinpath("old", "test_Solids.jl"))
        include(joinpath("old", "Visualize_Beam.jl"))
    end

    usePreviousPlotPackage()
end

end


module VectorizationBase

import ArrayInterface, LinearAlgebra, Libdl, Hwloc, IfElse, LayoutPointers
using ArrayInterface:
    StaticInt, Zero, One, StaticBool, True, False,
    contiguous_axis, contiguous_axis_indicator, contiguous_batch_size, stride_rank,
    device, CPUPointer, CPUIndex, eq, ne, lt, le, gt, ge,
    known_length, known_first, known_last, strides, offsets,
    static_first, static_last, static_length
import IfElse: ifelse

using CPUSummary:
  cache_type, num_cache, num_threads, num_cache_levels, num_cores, num_l1cache,
  num_l2cache, cache_associativity, num_l3cache, sys_threads, cache_inclusive,
  num_l4cache, cache_linesize, num_machines, cache_size, num_sockets
using HostCPUFeatures:
  register_size, static_sizeof, fast_int64_to_double, pick_vector_width,
  pick_vector_width_shift, prevpow2, simd_integer_register_size, fma_fast,
  smax, smin, has_feature, has_opmask_registers, register_count, static_sizeof,
  cpu_name, register_size, unwrap, intlog2, nextpow2, fast_half

using SIMDTypes:
  Bit, FloatingTypes, SignedHW, UnsignedHW, IntegerTypesHW, NativeTypesExceptBitandFloat16,
  NativeTypesExceptBit, NativeTypesExceptFloat16, NativeTypes, _Vec
using LayoutPointers:
  AbstractStridedPointer, StridedPointer, StridedBitPointer, memory_reference,
  stridedpointer, zstridedpointer, similar_no_offset, similar_with_offset,
  grouped_strided_pointer, stridedpointers, bytestrides, DensePointerWrapper,
  zero_offsets

asbool(::Type{True}) = true
asbool(::Type{False}) = false
# TODO: see if `@inline` is good enough.
# @inline asvalbool(r) = Val(map(Bool, r))
# @inline asvalint(r) = Val(map(Int, r))
@generated function asvalint(r::T) where {T<:Tuple{Vararg{StaticInt}}}
    t = Expr(:tuple)
    for s ∈ T.parameters
        push!(t.args, s.parameters[1])
    end
    Expr(:call, Expr(:curly, :Val, t))
end
@generated function asvalbool(r::T) where {T<:Tuple{Vararg{StaticBool}}}
    t = Expr(:tuple)
    for b ∈ T.parameters
        push!(t.args, b === True)
    end
    Expr(:call, Expr(:curly, :Val, t))
end
@inline val_stride_rank(A) = asvalint(stride_rank(A))
@inline val_dense_dims(A) = asvalbool(ArrayInterface.dense_dims(A))

# doesn't export `Zero` and `One` by default, as these names could conflict with an AD library
export Vec, Mask, EVLMask, MM, stridedpointer, vload, vstore!, StaticInt, True, False, Bit,
    vbroadcast, mask, vfmadd, vfmsub, vfnmadd, vfnmsub,
    VecUnroll, Unroll, pick_vector_width

using Base: llvmcall, VecElement, HWReal, tail
const LLVMCALL = GlobalRef(Base, :llvmcall)

const Boolean = Union{Bit,Bool}

abstract type AbstractSIMD{W,T <: Union{<:StaticInt,NativeTypes}} <: Real end
abstract type AbstractSIMDVector{W,T} <: AbstractSIMD{W,T} end
"""
    VecUnroll{N,W,T,V<:Union{NativeTypes,AbstractSIMD{W,T}}} <: AbstractSIMD{W,T}

`VecUnroll` supports optimizations when interleaving instructions across different memory storage schemes.
`VecUnroll{N,W,T} is typically a tuple of `N+1` `AbstractSIMDVector{W,T}`s. For example, a `VecUnroll{3,8,Float32}`
is a collection of 4× `Vec{8,Float32}`.

# Examples

```jldoctest; setup=:(using VectorizationBase)
julia> rgbs = [(R = Float32(i)/255, G = Float32(i+100)/255, B = Float32(i+200)/255) for i in 0:7:49]
8-element Vector{NamedTuple{(:R, :G, :B), Tuple{Float32, Float32, Float32}}}:
 (R = 0.0, G = 0.39215687, B = 0.78431374)
 (R = 0.02745098, G = 0.41960785, B = 0.8117647)
 (R = 0.05490196, G = 0.44705883, B = 0.8392157)
 (R = 0.08235294, G = 0.4745098, B = 0.8666667)
 (R = 0.10980392, G = 0.5019608, B = 0.89411765)
 (R = 0.13725491, G = 0.5294118, B = 0.92156863)
 (R = 0.16470589, G = 0.5568628, B = 0.9490196)
 (R = 0.19215687, G = 0.58431375, B = 0.9764706)

julia> ret = vload(stridedpointer(reinterpret(reshape, Float32, rgbs)), Unroll{1,1,3,2,8,zero(UInt),1}((1,1)))
3 x Vec{8, Float32}
Vec{8, Float32}<0.0f0, 0.02745098f0, 0.05490196f0, 0.08235294f0, 0.10980392f0, 0.13725491f0, 0.16470589f0, 0.19215687f0>
Vec{8, Float32}<0.39215687f0, 0.41960785f0, 0.44705883f0, 0.4745098f0, 0.5019608f0, 0.5294118f0, 0.5568628f0, 0.58431375f0>
Vec{8, Float32}<0.78431374f0, 0.8117647f0, 0.8392157f0, 0.8666667f0, 0.89411765f0, 0.92156863f0, 0.9490196f0, 0.9764706f0>

julia> typeof(ret)
VecUnroll{2, 8, Float32, Vec{8, Float32}}
```
While the `R`, `G`, and `B` are interleaved in `rgb`s, they have effectively been split out in `ret`
(the first contains all 8 `R` values, with `G` and `B` in the second and third, respectively).

To optimize for the user's CPU, in real code it would typically be better to use `Int(pick_vector_width(Float32))`
in place of `8` (`W`) in the `Unroll` construction.
"""
struct VecUnroll{N,W,T,V<:Union{NativeTypes,AbstractSIMD{W,T}}} <: AbstractSIMD{W,T}
    data::Tuple{V,Vararg{V,N}}
    @inline (VecUnroll(data::Tuple{V,Vararg{V,N}})::VecUnroll{N,W,T,V}) where {N,W,T,V<:AbstractSIMD{W,T}} = new{N,W,T,V}(data)
    @inline (VecUnroll(data::Tuple{T,Vararg{T,N}})::VecUnroll{N,T,T}) where {N,T<:NativeTypes} = new{N,1,T,T}(data)
    # # following two definitions are for checking that you aren't accidentally creating `VecUnroll{0}`s.
    # @inline (VecUnroll(data::Tuple{V,Vararg{V,N}})::VecUnroll{N,W,T,V}) where {N,W,T,V<:AbstractSIMD{W,T}} = (@assert(N > 0); new{N,W,T,V}(data))
    # @inline (VecUnroll(data::Tuple{T,Vararg{T,N}})::VecUnroll{N,T,T}) where {N,T<:NativeTypes} = (@assert(N > 0); new{N,1,T,T}(data))

    # @inline VecUnroll{N,W,T,V}(data::Tuple{V,Vararg{V,N}}) where {N,W,T,V<:AbstractSIMDVector{W,T}} = new{N,W,T,V}(data)
    # @inline (VecUnroll(data::Tuple{V,Vararg{V,N}})::VecUnroll{N,W,T,Vec{W,T}}) where {N,W,T,V<:AbstractSIMDVector{W,T}} = new{N,W,T,V}(data)
    # @inline (VecUnroll(data::Tuple{V,Vararg{V,N}})::VecUnroll{N,W,T,V}) where {N,W,T,V<:AbstractSIMDVector{W,T}} = new{N,W,T,V}(data)
end
# @inline VecUnroll(data::Tuple) = VecUnroll(promote(data))
# const AbstractSIMD{W,T} = Union{AbstractSIMDVector{W,T},VecUnroll{<:Any,W,T}}
const IntegerTypes = Union{StaticInt,IntegerTypesHW}
const VecOrScalar = Union{AbstractSIMDVector,NativeTypes}
const NativeTypesV = Union{AbstractSIMD,NativeTypes,StaticInt}
# const NativeTypesV = Union{AbstractSIMD,NativeTypes,StaticInt}
const IntegerTypesV = Union{AbstractSIMD{<:Any,<:IntegerTypes},IntegerTypesHW}
struct Vec{W,T} <: AbstractSIMDVector{W,T}
    data::NTuple{W,Core.VecElement{T}}
    @inline Vec{W,T}(x::NTuple{W,Core.VecElement{T}}) where {W,T<:NativeTypes} = new{W,T}(x)
    @generated function Vec(x::Tuple{Core.VecElement{T},Vararg{Core.VecElement{T},_W}}) where {_W,T<:NativeTypes}
        W = _W + 1
        # @assert W === pick_vector_width(W, T)# || W === 8
        vtyp = Expr(:curly, :Vec, W, T)
        Expr(:block, Expr(:meta,:inline), Expr(:(::), Expr(:call, vtyp, :x), vtyp))
    end
    # @inline function Vec(x::NTuple{W,<:Core.VecElement}) where {W}
    #     T = eltype(x)
    #     @assert W === pick_vector_width(W, T)
    #     # @assert ispow2(W) && (W ≤ max(pick_vector_width(W, T), 8))
    #     new{W,T}(x)
    # end
end

@inline Base.copy(v::AbstractSIMDVector) = v
@inline asvec(x::_Vec) = Vec(x)
@inline asvec(x) = x
@inline data(vu::VecUnroll) = getfield(vu, :data)

@inline unrolleddata(x) = x
@inline unrolleddata(x::VecUnroll) = getfield(x, :data)

@inline _demoteint(::Type{T}) where {T} = T
@inline _demoteint(::Type{Int64}) = Int32
@inline _demoteint(::Type{UInt64}) = UInt32

# abstract type AbstractMask{W,U<:Union{UnsignedHW,UInt128,UInt256,UInt512,UInt1024}} <: AbstractSIMDVector{W,Bit} end
abstract type AbstractMask{W,U<:Union{UnsignedHW,UInt128}} <: AbstractSIMDVector{W,Bit} end
struct Mask{W,U} <: AbstractMask{W,U}
    u::U
    @inline function Mask{W,U}(u::Unsigned) where {W,U} # ignores U...
        U2 = mask_type(StaticInt{W}())
        new{W,U2}(u % U2)
    end
end
struct EVLMask{W,U} <: AbstractMask{W,U}
    u::U
    evl::UInt32
    @inline function EVLMask{W,U}(u::Unsigned, evl) where {W,U} # ignores U...
        U2 = mask_type(StaticInt{W}())
        new{W,U2}(u % U2, evl % UInt32)
    end
end
const AnyMask{W} = Union{AbstractMask{W},VecUnroll{<:Any,W,Bit,<:AbstractMask{W}}}
@inline Mask{W}(u::U) where {W,U<:Unsigned} = Mask{W,U}(u)
@inline EVLMask{W}(u::U, i) where {W,U<:Unsigned} = EVLMask{W,U}(u, i)
@inline Mask{1}(b::Bool) = b
@inline EVLMask{1}(b::Bool, i) = b
@inline Mask(m::EVLMask{W,U}) where {W,U} = Mask{W,U}(getfield(m,:u))
# Const prop is good enough; added an @inferred test to make sure.
# Removed because confusion can cause more harm than good.

@inline Base.broadcastable(v::AbstractSIMDVector) = Ref(v)

Vec{W,T}(x::Vararg{NativeTypes,W}) where {W,T<:NativeTypes} = Vec(ntuple(w -> Core.VecElement{T}(x[w]), Val{W}()))
Vec{1,T}(x::Union{Float32,Float64}) where {T<:NativeTypes} = T(x)
Vec{1,T}(x::Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Bool}) where {T<:NativeTypes} = T(x)

@inline Base.length(::AbstractSIMDVector{W}) where W = W
@inline Base.size(::AbstractSIMDVector{W}) where W = (W,)
@inline Base.eltype(::AbstractSIMD{W,T}) where {W,T} = T
@inline Base.conj(v::AbstractSIMDVector) = v # so that things like dot products work.
@inline Base.adjoint(v::AbstractSIMDVector) = v # so that things like dot products work.
@inline Base.transpose(v::AbstractSIMDVector) = v # so that things like dot products work.

# Not using getindex/setindex as names to emphasize that these are generally treated as single objects, not collections.
@generated function extractelement(v::Vec{W,T}, i::I) where {W,I <: IntegerTypesHW,T}
    typ = LLVM_TYPES[T]
    instrs = """
        %res = extractelement <$W x $typ> %0, i$(8sizeof(I)) %1
        ret $typ %res
    """
    call = :($LLVMCALL($instrs, $T, Tuple{_Vec{$W,$T},$I}, data(v), i))
    Expr(:block, Expr(:meta, :inline), call)
end
@generated function insertelement(v::Vec{W,T}, x::T, i::I) where {W,I <: IntegerTypesHW,T}
    typ = LLVM_TYPES[T]
    instrs = """
        %res = insertelement <$W x $typ> %0, $typ %1, i$(8sizeof(I)) %2
        ret <$W x $typ> %res
    """
    call = :(Vec($LLVMCALL($instrs, _Vec{$W,$T}, Tuple{_Vec{$W,$T},$T,$I}, data(v), x, i)))
    Expr(:block, Expr(:meta, :inline), call)
end
@inline (v::AbstractSIMDVector)(i::IntegerTypesHW) = extractelement(v, i - one(i))
@inline (v::AbstractSIMDVector)(i::Integer) = extractelement(v, Int(i) - 1)
Base.@propagate_inbounds (vu::VecUnroll)(i::Integer, j::Integer) = getfield(vu, :data)[j](i)

@inline Base.Tuple(v::Vec{W}) where {W} = ntuple(v, Val{W}())

# Use with care in function signatures; try to avoid the `T` to stay clean on Test.detect_unbound_args

@inline data(v) = v
@inline data(v::Vec) = getfield(v, :data)

function Base.show(io::IO, v::AbstractSIMDVector{W,T}) where {W,T}
    name = typeof(v)
    print(io, "$(name)<")
    for w ∈ 1:W
        print(io, repr(extractelement(v, w-1)))
        w < W && print(io, ", ")
    end
    print(io, ">")
end
Base.bitstring(m::AbstractMask{W}) where {W} = bitstring(data(m))[end-W+1:end]
function Base.show(io::IO, m::AbstractMask{W}) where {W}
  bits = data(m)
  if m isa EVLMask
    print(io, "EVLMask{$W,Bit}<")
  else
    print(io, "Mask{$W,Bit}<")
  end
  for w ∈ 0:W-1
    print(io, (bits & 0x01) % Int)
    bits >>= 0x01
    w < W-1 && print(io, ", ")
  end
  print(io, ">")
end
function Base.show(io::IO, vu::VecUnroll{N,W,T,V}) where {N,W,T,V}
    println(io, "$(N+1) x $V")
    d = data(vu)
    for n in 1:N+1
        show(io, d[n]);
        n > N || println(io)
    end
end

"""
The name `MM` type refers to _MM registers such as `XMM`, `YMM`, and `ZMM`.
`MMX` from the original MMX SIMD instruction set is a [meaningless initialism](https://en.wikipedia.org/wiki/MMX_(instruction_set)#Naming).

The `MM{W,X}` type is used to represent SIMD indexes of width `W` with stride `X`.
"""
struct MM{W,X,I<:Union{HWReal,StaticInt}} <: AbstractSIMDVector{W,I}
    i::I
    @inline MM{W,X}(i::T) where {W,X,T<:Union{HWReal,StaticInt}} = new{W,X::Int,T}(i)
end
@inline MM(i::MM{W,X}) where {W,X} = MM{W,X}(getfield(i, :i))
@inline MM{W}(i::Union{HWReal,StaticInt}) where {W} = MM{W,1}(i)
@inline MM{W}(i::Union{HWReal,StaticInt}, ::StaticInt{X}) where {W,X} = MM{W,X}(i)
@inline data(i::MM) = getfield(i, :i)

@inline extractelement(i::MM{W,X,I}, j) where {W,X,I} = getfield(i, :i) + (X % I) * (j % I)

function Base.getproperty(::AbstractSIMD, ::Symbol)
    throw(ErrorException("""
`Base.getproperty` not defined on AbstractSIMD.
If you wish to work with the data as a tuple, it is recommended to use `Tuple(v)`. Once you have an ordinary tuple, you can access
individual elements normally. Alternatively, you can index using parenthesis, e.g. `v(1)` indexes the first element.
Parenthesis are used instead of `getindex`/square brackets because `AbstractSIMD` objects represent a single number, and
for `x::Number`, `x[1] === x`.

If you wish to perform a reduction on the collection, the naming convention is prepending the base function with a `v`. These functions
are not overloaded, because for `x::Number`, `sum(x) === x`. Functions include `vsum`, `vprod`, `vmaximum`, `vminimum`, `vany`, and `vall`.

If you wish to define a new operation applied to the entire vector, do not define it in terms of operations on the individual eleemnts.
This will often lead to bad code generation -- bad in terms of both performance, and often silently producing incorrect results!
Instead, implement them in terms of existing functions defined on `::AbstractSIMD`. Please feel free to file an issue if you would like
clarification, and especially if you think the function may be useful for others and should be included in `VectorizationBase.jl`.
"""))
end

"""
  pause()

For use in spin-and-wait loops, like spinlocks.
"""
@inline pause() = ccall(:jl_cpu_pause, Cvoid, ())

# notinthreadedregion() = iszero(ccall(:jl_in_threaded_region, Cint, ()))
# function assert_init_has_finished()
#     _init_has_finished[] || throw(ErrorException("bad stuff happened"))
#     return nothing
# end

include("static.jl")
include("cartesianvindex.jl")
include("early_definitions.jl")
include("promotion.jl")
include("llvm_types.jl")
include("lazymul.jl")
include("strided_pointers/stridedpointers.jl")
# include("strided_pointers/bitpointers.jl")
include("strided_pointers/cartesian_indexing.jl")
include("strided_pointers/cse_stridemultiples.jl")
include("llvm_intrin/binary_ops.jl")
include("vector_width.jl")
include("ranges.jl")
include("llvm_intrin/conversion.jl")
include("llvm_intrin/masks.jl")
include("llvm_intrin/intrin_funcs.jl")
include("llvm_intrin/memory_addr.jl")
include("llvm_intrin/unary_ops.jl")
include("llvm_intrin/vbroadcast.jl")
include("llvm_intrin/vector_ops.jl")
include("llvm_intrin/nonbroadcastingops.jl")
include("llvm_intrin/integer_fma.jl")
include("llvm_intrin/vfmaddsub.jl")
include("vecunroll/memory.jl")
include("vecunroll/fmap.jl")
include("base_defs.jl")
include("alignment.jl")
include("special/misc.jl")
include("special/double.jl")
include("special/exp.jl")
include("special/verf.jl")
# include("special/log.jl")

demoteint(::Type{T}, W) where {T} = False()
demoteint(::Type{UInt64}, W::StaticInt) = gt(W, pick_vector_width(UInt64))
demoteint(::Type{Int64}, W::StaticInt) = gt(W, pick_vector_width(Int64))

@generated function simd_vec(::DemoteInt, y::_T, x::Vararg{_T,_W}) where {DemoteInt<:StaticBool,_T,_W}
    W = 1 + _W
    T = DemoteInt === True ? _demoteint(_T) : _T
    trunc = T !== _T
    Wfull = nextpow2(W)
    ty = LLVM_TYPES[T]
    init = W == Wfull ? "undef" : "zeroinitializer"
    instrs = ["%v0 = insertelement <$Wfull x $ty> $init, $ty %0, i32 0"]
    Tup = Expr(:curly, :Tuple, T)
    for w ∈ 1:_W
        push!(instrs, "%v$w = insertelement <$Wfull x $ty> %v$(w-1), $ty %$w, i32 $w")
        push!(Tup.args, T)
    end
    push!(instrs, "ret <$Wfull x $ty> %v$_W")
    llvmc = :($LLVMCALL($(join(instrs,"\n")), _Vec{$Wfull,$T}, $Tup))
    trunc ? push!(llvmc.args, :(y % $T)) : push!(llvmc.args, :y)
    for w ∈ 1:_W
        ref = Expr(:ref, :x, w)
        trunc && (ref = Expr(:call, :%, ref, T))
        push!(llvmc.args, ref)
    end
    quote
        $(Expr(:meta,:inline))
        Vec($llvmc)
    end
end
function vec_quote(demote, W, Wpow2, offset::Int = 0)
  call = Expr(:call, :simd_vec, Expr(:call, demote ? :True : :False)); Wpow2 += offset
  iszero(offset) && push!(call.args, :y)
  foreach(w -> push!(call.args, Expr(:call, getfield, :x, w, false)), max(1,offset):min(W,Wpow2)-1)
  foreach(w -> push!(call.args, Expr(:call, :zero, :T)), W+1:Wpow2)
  call
end
@generated function _vec(::StaticInt{_Wpow2}, ::DemoteInt, y::T, x::Vararg{T,_W}) where {DemoteInt<:StaticBool, _Wpow2, _W, T <: NativeTypes}
    1+3
  W = _W + 1
  demote = DemoteInt === True
  Wpow2 = demote ? 2_Wpow2 : _Wpow2
  if W ≤ Wpow2
    vec_quote(demote, W, Wpow2)
  else
    tup = Expr(:tuple)
    offset = 0
    while offset < W
      push!(tup.args, vec_quote(demote, W, Wpow2, offset)); offset += Wpow2
    end
    Expr(:call, :VecUnroll, tup)
  end
end
@inline function Vec(y::T, x::Vararg{T,_W}) where {_W, T <: NativeTypes}
  W = StaticInt{_W}() + One()
  _vec(pick_vector_width(W, T), demoteint(T, W), y, x...)
end

@inline reduce_to_onevec(f::F, vu::VecUnroll) where {F} = ArrayInterface.reduce_tup(f, data(vu))

include("precompile.jl")
_precompile_()

end # module


using PSCOPF

using Test
using Dates

@testset verbose=true "test_ech_generator" begin

    @testset "mode_1" begin
        network = PSCOPF.Networks.Network()
        ts1 = DateTime("2015-01-01T11:00:00")
        TS = PSCOPF.create_target_timepoints(ts1)
        mode = PSCOPF.PSCOPF_MODE_1

        list_ech = PSCOPF.generate_ech(network, TS, mode)

        EXPECTED_ECH = [ts1 - Dates.Hour(4),
                        ts1 - Dates.Hour(1),
                        ts1 - Dates.Minute(30),
                        ts1 - Dates.Minute(15),
                        ts1]
        @test length(list_ech) == length(EXPECTED_ECH)
        @test list_ech == EXPECTED_ECH
    end

    #=
        generate_ech sorts the received TS, and looks at the first target timepoint
    =#
    @testset "mode_1_unsorted_TS" begin
        network = PSCOPF.Networks.Network()
        #unsorted TS
        TS = [DateTime("2015-01-01T11:30:00"), DateTime("2015-01-01T11:45:00"), DateTime("2015-01-01T11:00:00"), DateTime("2015-01-01T11:15:00")]
        mode = PSCOPF.PSCOPF_MODE_1

        list_ech = PSCOPF.generate_ech(network, TS, mode)

        ts1 = DateTime("2015-01-01T11:00:00")
        EXPECTED_ECH = [ts1 - Dates.Hour(4),
                        ts1 - Dates.Hour(1),
                        ts1 - Dates.Minute(30),
                        ts1 - Dates.Minute(15),
                        ts1]
        @test length(list_ech) == length(EXPECTED_ECH)
        @test list_ech == EXPECTED_ECH
    end

    @testset "mode_2" begin
        network = PSCOPF.Networks.Network()
        ts1 = DateTime("2015-01-01T11:00:00")
        TS = PSCOPF.create_target_timepoints(ts1)
        mode = PSCOPF.PSCOPF_MODE_2

        list_ech = PSCOPF.generate_ech(network, TS, mode)

        EXPECTED_ECH = [ts1 - Dates.Hour(4),
                        ts1 - Dates.Hour(1),
                        ts1 - Dates.Minute(30),
                        ts1 - Dates.Minute(15),
                        ts1]
        @test length(list_ech) == length(EXPECTED_ECH)
        @test list_ech == EXPECTED_ECH
    end

    @testset "mode_3" begin
        network = PSCOPF.Networks.Network()
        ts1 = DateTime("2015-01-01T11:00:00")
        TS = PSCOPF.create_target_timepoints(ts1)
        mode = PSCOPF.PSCOPF_MODE_3

        list_ech = PSCOPF.generate_ech(network, TS, mode)

        EXPECTED_ECH = [ts1 - Dates.Hour(4),
                        ts1 - Dates.Hour(1),
                        ts1 - Dates.Minute(30),
                        ts1 - Dates.Minute(15),
                        ts1]
        @test length(list_ech) == length(EXPECTED_ECH)
        @test list_ech == EXPECTED_ECH
    end

end

#=

#TEst
    planningTSO = planningRefTSO
    planningMarket = planningRefMarket

    data = (Grid, ECH, TS, ...)
    planning = (planningTSO, planningMarket)
    OptimResult = run(MarketMode1, planning, data)

    tester les résutlats

#TEst update (démarrage des unités)

    planningTSO = planningRefTSO
    planningMarket = planningRefMarket

    data = (Grid, ECH, TS, ...)
    planning = (planningTSO, planningMarket)

    update_imposition(planning, OptimResult)
    update_limitation(planning, OptimResult)
    update_units(planning, OptimResult)
    update_reserve(planning, OptimResult)

    test sur planning
    #Decision ferme/
    niveau["unit1"][] = 15
    niveau["unit1"] = 15

=#


# Types

export
    Interaction,
    GeneralInteraction,
    SpecificInteraction,
    AbstractCutoff,
    Simulator,
    Thermostat,
    NeighborFinder,
    Logger,
    Atom,
    AtomData,
    Simulation

const DefaultFloat = Float64

"An interaction between atoms that contributes to forces on the atoms."
abstract type Interaction end

"""
A general interaction that will apply to all or most atom pairs.
Custom general interactions should sub-type this type.
"""
abstract type GeneralInteraction <: Interaction end

"""
A specific interaction between sets of specific atoms, e.g. a bond angle.
Custom specific interactions should sub-type this type.
"""
abstract type SpecificInteraction <: Interaction end

"""
A general type of cutoff encoding the approximation used for a potential.
Interactions can be parameterized by the cutoff behavior.
"""
abstract type AbstractCutoff end

"""
A type of simulation to run, e.g. leap-frog integration or energy minimisation.
Custom simulators should sub-type this type.
"""
abstract type Simulator end

"""
A way to keep the temperature of a simulation constant.
Custom thermostats should sub-type this type.
"""
abstract type Thermostat end

"""
A way to find near atoms to save on simulation time.
Custom neighbor finders should sub-type this type.
"""
abstract type NeighborFinder end

"""
A way to record a property, e.g. the temperature, throughout a simulation.
Custom loggers should sub-type this type.
"""
abstract type Logger end

"""
    Atom(; <keyword arguments>)

An atom and its associated information.
Properties unused in the simulation or in analysis can be left with their
default values.
The types used should be bits types if the GPU is going to be used.

# Arguments
- `index::Int`: the index of the atom in the system.
- `charge::C=0.0u"q"`: the charge of the atom, used for electrostatic interactions.
- `mass::M=0.0u"u"`: the mass of the atom.
- `σ::S=0.0u"nm"`: the Lennard-Jones finite distance at which the inter-particle
    potential is zero.
- `ϵ::E=0.0u"kJ * mol^-1"`: the Lennard-Jones depth of the potential well.
"""
struct Atom{C, M, S, E}
    index::Int
    charge::C
    mass::M
    σ::S
    ϵ::E
end

function Atom(;
                index=1,
                charge=0.0u"q",
                mass=0.0u"u",
                σ=0.0u"nm",
                ϵ=0.0u"kJ * mol^-1")
    return Atom(index, charge, mass, σ, ϵ)
end

function Base.show(io::IO, a::Atom)
    print(io, "Atom with index ", a.index, ", charge=", a.charge,
            ", mass=", a.mass, ", σ=", a.σ, ", ϵ=", a.ϵ)
end

"""
    AtomData(atom_type, atom_name, res_number, res_name)

Data associated with an atom.
Storing this separately allows the atom types to be bits types and hence
work on the GPU.
"""
struct AtomData
    atom_type::String
    atom_name::String
    res_number::Int
    res_name::String
    element::String
end

function AtomData(;
                    atom_type="?",
                    atom_name="?",
                    res_number=1,
                    res_name="???",
                    element="?")
    return AtomData(atom_type, atom_name, res_number, res_name, element)
end

"""
    Simulation(; <keyword arguments>)

The data needed to define and run a molecular simulation.
Properties unused in the simulation or in analysis can be left with their
default values.

# Arguments
- `simulator::Simulator`: the type of simulation to run.
- `atoms::A`: the atoms, or atom equivalents, in the simulation. Can be
    of any type.
- `atoms_data::AD`: data associated with the atoms, allowing the atoms to be
    bits types and hence work on the GPU.
- `specific_inter_lists::SI=()`: the specific interactions in the simulation,
    i.e. interactions between specific atoms such as bonds or angles. Typically
    a `Tuple`.
- `general_inters::GI=()`: the general interactions in the simulation, i.e.
    interactions between all or most atoms such as electrostatics. Typically a
    `Tuple`.
- `coords::C`: the coordinates of the atoms in the simulation. Typically a
    `Vector` of `SVector`s of any dimension and type `T`, where `T` is an
    `AbstractFloat` type.
- `velocities::V=zero(coords)`: the velocities of the atoms in the simulation,
    which should be the same type as the coordinates. The meaning of the
    velocities depends on the simulator used, e.g. for the `VelocityFreeVerlet`
    simulator they represent the previous step coordinates for the first step.
- `temperature::T=0.0`: the temperature of the simulation.
- `box_size::B`: the size of the box in which the simulation takes place.
- `neighbor_finder::NeighborFinder=NoNeighborFinder()`: the neighbor finder
    used to find close atoms and save on computation.
- `thermostat::Thermostat=NoThermostat()`: the thermostat which applies during
    the simulation.
- `loggers::Dict{String, <:Logger}=Dict()`: the loggers that record properties
    of interest during the simulation.
- `timestep::S=0.0`: the timestep of the simulation.
- `n_steps::Integer=0`: the number of steps in the simulation.
- `force_unit::F=u"kJ * mol^-1 * nm^-1"`: the unit of force used in the
    simulation.
- `energy_unit::E=u"kJ * mol^-1"`: the unit of energy used in the simulation.
- `gpu_diff_safe::Bool`: whether to use the GPU implementation. Defaults to
    `isa(coords, CuArray)`.
"""
struct Simulation{D, T, A, AD, C, V, GI, SI, B, S, F, E}
    simulator::Simulator
    atoms::A
    atoms_data::AD
    specific_inter_lists::SI
    general_inters::GI
    coords::C
    velocities::V
    temperature::T
    box_size::B
    neighbors::Vector{Tuple{Int, Int, Bool}}
    neighbor_finder::NeighborFinder
    thermostat::Thermostat
    loggers::Dict{String, <:Logger}
    timestep::S
    n_steps::Int
    n_steps_made::Vector{Int}
    force_unit::F
    energy_unit::E
end

Simulation{D}(args...) where {D, T, A, AD, C, V, GI, SI, B, S, F, E} = Simulation{D, T, A, AD, C, V, GI, SI, B, S, F, E}(args...)

function Simulation(;
                    simulator=VelocityVerlet(),
                    atoms,
                    atoms_data=[],
                    specific_inter_lists=(),
                    general_inters=(),
                    coords,
                    velocities=zero(coords),
                    temperature=0.0u"K",
                    box_size,
                    neighbors=Tuple{Int, Int, Bool}[],
                    neighbor_finder=NoNeighborFinder(),
                    thermostat=NoThermostat(),
                    loggers=Dict{String, Logger}(),
                    timestep=0.0u"ps",
                    n_steps=0,
                    n_steps_made=[0],
                    force_unit=u"kJ * mol^-1 * nm^-1",
                    energy_unit=u"kJ * mol^-1",
                    gpu_diff_safe=isa(coords, CuArray))
    T = typeof(temperature)
    A = typeof(atoms)
    AD = typeof(atoms_data)
    C = typeof(coords)
    V = typeof(velocities)
    GI = typeof(general_inters)
    SI = typeof(specific_inter_lists)
    B = typeof(box_size)
    S = typeof(timestep)
    F = typeof(force_unit)
    E = typeof(energy_unit)
    return Simulation{gpu_diff_safe, T, A, AD, C, V, GI, SI, B, S, F, E}(
                simulator, atoms, atoms_data, specific_inter_lists, general_inters,
                coords, velocities, temperature, box_size, neighbors, neighbor_finder,
                thermostat, loggers, timestep, n_steps, n_steps_made, force_unit,
                energy_unit)
end

function Base.show(io::IO, s::Simulation)
    print(io, "Simulation with ", length(s.coords), " atoms, ",
                typeof(s.simulator), " simulator, ", s.timestep, " timestep, ",
                s.n_steps, " steps, ", first(s.n_steps_made), " steps made")
end


using BigArrays
using BigArrays.BinDicts
using Test
using RealNeuralNetworks
using RealNeuralNetworks.Manifests
using RealNeuralNetworks.NodeNets
using RealNeuralNetworks.SWCs
using JSON

const cellId = 77497
const VOXEL_SIZE = (80,80,45)

@testset "test basic data structure" begin
    h = BinDict("/neuroglancer/zfish_v1/consensus-20180123/mesh_mip_4")
    manifest = String(h["$(cellId):0"])
    manifest = JSON.parse(manifest; dicttype=Dict{Symbol, Any})

    str = split(manifest[:fragments][1], ":")[end]
    @show str
    range = BigArrays.Indexes.string2unit_range( str )
    @test range == [2865:3376, 2017:2528, 16401:16912]
end

@testset "test manifest iteration" begin
    manifest = Manifest("/neuroglancer/zfish_v1/consensus-20180123/mesh_mip_4",
                        "$(cellId):0", "/neuroglancer/zfish_v1/consensus-20180123/40_40_45")
    println("trace a cell...")
    @time nodeNet = Manifests.trace(manifest, cellId)
    swc = SWC( nodeNet )
    SWCs.stretch_coordinates!(swc, VOXEL_SIZE)
    SWCs.save(swc, "/tmp/$(cellId).swc")
end


using Test, StorageColorTypes
using Colors, FixedPointNumbers

@testset "reinterpret" begin
    @testset "reinterpret RGB/ARGB" begin
        @test reinterpret(UInt16, RGB565LE(1, 0, 1)) === ltoh(0b11111000_00011111)
        @test reinterpret(UInt16, RGB565BE(1, 0, 1)) === ntoh(0b11111000_00011111)
        @test reinterpret(UInt16, RGB555LE(1, 0, 1)) === ltoh(0b01111100_00011111)
        @test reinterpret(UInt16, RGB555BE(1, 0, 1)) === ntoh(0b01111100_00011111)
        @test reinterpret(UInt16, XRGB444LE(1, 1, 0)) === ltoh(0b00001111_11110000)
        @test reinterpret(UInt16, XRGB444BE(1, 1, 0)) === ntoh(0b00001111_11110000)
        @test reinterpret(UInt16, RGBX444LE(0, 1, 1)) === ltoh(0b00001111_11110000)
        @test reinterpret(UInt16, RGBX444BE(0, 1, 1)) === ntoh(0b00001111_11110000)

        @test reinterpret(RGB565LE, ltoh(0b11111000_00011111)) === RGB565LE(1, 0, 1)
        @test reinterpret(RGB565BE, ntoh(0b11111000_00011111)) === RGB565BE(1, 0, 1)
        @test reinterpret(RGB555LE, ltoh(0b01111100_00011111)) === RGB555LE(1, 0, 1)
        @test reinterpret(RGB555BE, ntoh(0b01111100_00011111)) === RGB555BE(1, 0, 1)
        @test reinterpret(XRGB444LE, ltoh(0b00001111_11110000)) === XRGB444LE(1, 1, 0)
        @test reinterpret(XRGB444BE, ntoh(0b00001111_11110000)) === XRGB444BE(1, 1, 0)
        @test reinterpret(RGBX444LE, ltoh(0b00001111_11110000)) === RGBX444LE(0, 1, 1)
        @test reinterpret(RGBX444BE, ntoh(0b00001111_11110000)) === RGBX444BE(0, 1, 1)
    end

    @testset "reinterpret HSV/HSL" begin
        @test reinterpret(UInt8, [HSV24(100, 1.0, 0.5)]) == UInt8[ 50, 0xFF, 0x80]
        @test reinterpret(UInt8, [HSL24(200, 0.0, 0.6)]) == UInt8[100, 0x00, 0x99]
        @test reinterpret(UInt8, [HLS24(300, 0.0, 0.4)]) == UInt8[150, 0x66, 0x00]

        @test reinterpret(HSV24, UInt8[ 50, 0xFF, 0x80]) == [HSV24(100, 1.0, 0.5)]
        @test reinterpret(HSL24, UInt8[100, 0x00, 0x99]) == [HSL24(200, 0.0, 0.6)]
        @test reinterpret(HLS24, UInt8[150, 0x66, 0x00]) == [HLS24(300, 0.0, 0.4)]

        @test reinterpret(HLS{Float32}, [HSL{Float32}(200, 0.0, 0.6)]) ==
            [HLS{Float32}(200, 0.6, 0.0)]
    end

    @testset "reinterpret Lab/Luv" begin
        @test reinterpret(UInt8, [Lab24(60, 100, -100)]) == UInt8[0x99, 228, 28]
        @test reinterpret(UInt8, [Luv24(60, 100, -100)]) == UInt8[0x99, 169, 39]

        @test reinterpret(Lab24, UInt8[0x99, 228, 28]) == [Lab24(60, 100, -100)]
        @test reinterpret(Luv24, UInt8[0x99, 169, 39]) == [Luv24(60, 100, -100)]
    end
end


@testset "hit_chance" begin
    @test typeof(Augmentor.hit_chance(0.2)) <: Bool
    @test typeof(Augmentor.hit_chance(Float32(0.2))) <: Bool
    @test typeof(Augmentor.hit_chance(Float32(1))) <: Bool
    @test typeof(Augmentor.hit_chance(rand(10,2))) <: BitArray{2}
    @test size(Augmentor.hit_chance(rand(10,2))) == (10,2)
    @test typeof(Augmentor.hit_chance(rand(5))) <: BitArray{1}
    @test size(Augmentor.hit_chance(rand(5))) == (5,)
end


module MANA
# Packge Main for MANA GWAS package
using Distributed, DelimitedFiles, Statistics, LinearAlgebra, Random, SparseArrays, BEDFiles, Distributions, JuliaDB, IndexedTables, OnlineStats, JWAS, DataFrames
#
include("types.jl")
include("read/read.jl")
include("model/model.jl")
include("mutate/mutate.jl")
include("run/run.jl")
include("summarise/summarise.jl")
include("ycreator/ycreator.jl")
include("pedigree/pedigree.jl")

# Functions for basic model
export build_gwas
export add_snps
export setup_phenotypes
export initiate_model
export run_model
export concatenate_summaries
# Functions for adjusting marker effects
export make_alphas
export filter_alphas
export print_alphas
# Functions for adjusting phenotypes
export build_ycreator
export add_phenotypes
export add_alphas
export add_markers_gensel
export sort_ycreator
export initiate_ycreator
export create_yk_loso
# Functions for pedigree models
export run_pedigree_model
export run_spliceQTL
export run_pedigree_model_multirecord
export make_genotype_table
export generate_genotypeclass_columns
export center_phenotype

end


module TestExamples2DEuler

using Test
using Trixi

include("test_trixi.jl")

# pathof(Trixi) returns /path/to/Trixi/src/Trixi.jl, dirname gives the parent directory
EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", "tree_2d_dgsem")

@testset "Compressible Euler" begin
  @trixi_testset "elixir_euler_source_terms.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms.jl"),
      l2   = [8.517808508019351e-7, 1.2350203856098537e-6, 1.2350203856728076e-6, 4.277886946638239e-6],
      linf = [8.357848139128876e-6, 1.0326302096741458e-5, 1.0326302101404394e-5, 4.496194024383726e-5])
  end

  @trixi_testset "elixir_euler_convergence_pure_fv.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_convergence_pure_fv.jl"),
      l2   = [0.026440292358506527, 0.013245905852168414, 0.013245905852168479, 0.03912520302609374],
      linf = [0.042130817806361964, 0.022685499230187034, 0.022685499230187922, 0.06999771202145322])
  end

  @trixi_testset "elixir_euler_density_wave.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_density_wave.jl"),
      l2   = [0.0010600778457964775, 0.00010600778457634275, 0.00021201556915872665, 2.650194614399671e-5],
      linf = [0.006614198043413566, 0.0006614198043973507, 0.001322839608837334, 0.000165354951256802],
      tspan = (0.0, 0.5))
  end

  @trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonperiodic.jl"),
      l2   = [2.3653424742011065e-6, 2.1388875092652046e-6, 2.1388875093792834e-6, 6.010896863407163e-6],
      linf = [1.4080465934984687e-5, 1.7579850582816192e-5, 1.757985059525069e-5, 5.95689353266593e-5])
  end

  @trixi_testset "elixir_euler_ec.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
      l2   = [0.061751715597716854, 0.05018223615408711, 0.05018989446443463, 0.225871559730513],
      linf = [0.29347582879608825, 0.31081249232844693, 0.3107380389947736, 1.0540358049885143])
  end

  @trixi_testset "elixir_euler_shockcapturing.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing.jl"),
      l2   = [0.053797946432602085, 0.04696120828935379, 0.04696384063506395, 0.19685320969570913],
      linf = [0.18540158860112732, 0.24029373364236004, 0.23267525584314722, 0.6874555954921888])
  end

  @trixi_testset "elixir_euler_blast_wave.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave.jl"),
      l2   = [0.13938437665766198, 0.11552277347912725, 0.11552281282617058, 0.3391365873985103],
      linf = [1.4554615795909327, 1.3256380647458283, 1.3256380647457329, 1.8161954000082436],
      maxiters = 30)
  end

  @trixi_testset "elixir_euler_blast_wave_pure_fv.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_pure_fv.jl"),
      l2   = [0.39957047631960346, 0.21006912294983154, 0.21006903549932, 0.6280328163981136],
      linf = [2.20417889887697, 1.5487238480003327, 1.5486788679247812, 2.4656795949035857],
      tspan = (0.0, 0.5))
  end

  @trixi_testset "elixir_euler_blast_wave_amr.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_amr.jl"),
      l2   = [0.6777595378750056, 0.2813201698283611, 0.2813199969265205, 0.720079968985097],
      linf = [2.889020548335417, 1.8035429384705923, 1.8034830438685023, 3.033907232631001],
      tspan = (0.0, 1.0))
  end

  @trixi_testset "elixir_euler_sedov_blast_wave.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave.jl"),
      l2   = [0.4819231474652106, 0.1655453843075888, 0.16554538430758944, 0.6182451347074112],
      linf = [2.4849541392059202, 1.2816207838889486, 1.2816207838888944, 6.4743208586529875],
      tspan = (0.0, 1.0))
  end

  @trixi_testset "elixir_euler_positivity.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_positivity.jl"),
      l2   = [0.48408821703857413, 0.16610607713651496, 0.16610607713651482, 0.6181627089877778],
      linf = [2.4926053782999267, 1.2903378547503723, 1.2903378547503739, 6.472338570555728],
      tspan = (0.0, 1.0))
  end

  @trixi_testset "elixir_euler_blob_mortar.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blob_mortar.jl"),
      l2   = [0.22112279110928998, 0.6274680581327996, 0.24324346433572555, 2.925884837102588],
      linf = [10.209709958634505, 28.427728124038527, 9.45255862469211, 97.24756873689934],
      tspan = (0.0, 0.5))
  end

  @trixi_testset "elixir_euler_blob_amr.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blob_amr.jl"),
      l2   = [0.20227473253519843, 1.1852146938118358, 0.10163211952508579, 5.24084854294007],
      linf = [14.241296885123061, 71.64475965226391, 7.3265332346563605, 293.17007294795917],
      tspan = (0.0, 0.12))
  end

  @trixi_testset "elixir_euler_kelvin_helmholtz_instability.jl" begin
    if Threads.nthreads() == 1
      # This example uses random numbers to generate the initial condition.
      # Hence, we can only check "errors" if everything is made reproducible.
      # However, that's not enough to ensure reproducibility since the stream
      # of random numbers is not guaranteed to be the same across different
      # minor versions of Julia.
      # See https://github.com/trixi-framework/Trixi.jl/issues/232#issuecomment-709738400
      @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_kelvin_helmholtz_instability.jl"),
        l2   = [0.002046113073936985, 0.002862623943300569, 0.001971116879236713, 0.004816623657677065],
        linf = [0.024375050653856478, 0.01803061241763637, 0.009938942915093363, 0.02097211774984231],
        tspan = (0.0, 0.2))
    else
      @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_kelvin_helmholtz_instability.jl"),
        tspan = (0.0, 0.2))
    end
  end

  @trixi_testset "elixir_euler_kelvin_helmholtz_instability_amr.jl" begin
    if Threads.nthreads() == 1
      # This example uses random numbers to generate the initial condition.
      # Hence, we can only check "errors" if everything is made reproducible.
      # However, that's not enough to ensure reproducibility since the stream
      # of random numbers is not guaranteed to be the same across different
      # minor versions of Julia.
      # See https://github.com/trixi-framework/Trixi.jl/issues/232#issuecomment-709738400
      @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_kelvin_helmholtz_instability_amr.jl"),
        l2   = [0.0015773943210539198, 0.0023012411276933705, 0.0013793453564426015, 0.003239622733845669],
        linf = [0.025791632274568155, 0.015170620391733003, 0.00958159135384009, 0.013265593557157729],
        tspan = (0.0, 0.2))
    else
      @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_kelvin_helmholtz_instability_amr.jl"),
        tspan = (0.0, 0.2))
    end
  end

  @trixi_testset "elixir_euler_vortex.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex.jl"),
      l2   = [3.6343141788303172e-6, 0.0032111379945554378, 0.0032111482803927763, 0.004545715899533244],
      linf = [7.901851921288117e-5, 0.030561510550305093, 0.03050266851613559, 0.042876690674344076])
  end

  @trixi_testset "elixir_euler_vortex_mortar.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_mortar.jl"),
      l2   = [2.1203067597905396e-6, 2.792922844344066e-5, 3.7593422075434476e-5, 8.813644239472934e-5],
      linf = [5.932046017209647e-5, 0.0007491265252463908, 0.0008165690091972433, 0.0022122638482677814])
  end

  @trixi_testset "elixir_euler_vortex_mortar_split.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_mortar_split.jl"),
      l2   = [2.1203686464224497e-6, 2.8053511778944724e-5, 3.7617944213324846e-5, 8.840785095671702e-5],
      linf = [5.900566824046383e-5, 0.0007554116580206216, 0.0008166047918783947, 0.0022090163480150693])
  end

  @trixi_testset "elixir_euler_vortex_mortar_split.jl with flux_central" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_mortar_split.jl"),
      l2   = [2.1203067597988456e-6, 2.7929228443420677e-5, 3.7593422075440256e-5, 8.813644239501579e-5],
      linf = [5.932046017187442e-5, 0.0007491265252450585, 0.0008165690091986866, 0.0022122638482677814],
      volume_flux = flux_central)
  end

  @trixi_testset "elixir_euler_vortex_mortar_split.jl with flux_shima_etal" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_mortar_split.jl"),
      l2   = [2.120102590290442e-6, 2.8056524989269332e-5, 3.7595003938956103e-5, 8.84137216636431e-5],
      linf = [5.9341032821902395e-5, 0.000755231667019185, 0.0008152449093754566, 0.0022069874183223703],
      volume_flux = flux_shima_etal)
  end

  @trixi_testset "elixir_euler_vortex_mortar_split.jl with flux_ranocha" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_mortar_split.jl"),
      l2   = [2.1201025794676757e-6, 2.805652743675726e-5, 3.759500922486255e-5, 8.841377002453846e-5],
      linf = [5.93402785843411e-5, 0.0007552314167595942, 0.0008152450162266511, 0.002206997654994325],
      volume_flux = flux_ranocha)
  end

  @trixi_testset "elixir_euler_vortex_shockcapturing.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_shockcapturing.jl"),
      l2   = [3.803468104603101e-6, 5.561125881446469e-5, 5.564051742708959e-5, 0.00015706487208149976],
      linf = [8.49343523420254e-5, 0.0009602338002179245, 0.0009669941278601657, 0.0030758222779923017])
  end

  @trixi_testset "elixir_euler_vortex_mortar_shockcapturing.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_mortar_shockcapturing.jl"),
      l2   = [2.120587333728832e-6, 2.8053708180265217e-5, 3.7618048362912055e-5, 8.841529146530808e-5],
      linf = [5.900528113544912e-5, 0.0007554176274675584, 0.000816603085540657, 0.002208999078852969])
  end

  @trixi_testset "elixir_euler_vortex_amr.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex_amr.jl"),
      l2   = [2.1325938318497577e-6, 0.003281545066125044, 0.0032806321457482615, 0.00464605975938406],
      linf = [4.667437348149228e-5, 0.03175420871507906, 0.0318039789241531, 0.04561735256198318])
  end
end

end # module


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

"""
    SMPSScenario

Conveniece type that adheres to the [`AbstractScenario`](@ref) abstraction. Obtained when reading scenarios specified in SMPS format.

See also: [`SMPSSampler`](@ref)
"""
struct SMPSScenario{T <: AbstractFloat, M <: AbstractMatrix} <: AbstractScenario
    probability::Probability
    Δq::Vector{T}
    ΔT::M
    ΔW::M
    Δh::Vector{T}
    ΔC::M
    Δd₁::Vector{T}
    Δd₂::Vector{T}
end

function Base.zero(::Type{SMPSScenario{T}}) where T <: AbstractFloat
    return SMPSScenario(Probability(1.0),
                        AdditiveZeroArray{T,1}(),
                        AdditiveZeroArray{T,2}(),
                        AdditiveZeroArray{T,2}(),
                        AdditiveZeroArray{T,1}(),
                        AdditiveZeroArray{T,2}(),
                        AdditiveZeroArray{T,1}(),
                        AdditiveZeroArray{T,1}())
end

function StochasticPrograms.expected(scenarios::Vector{<:SMPSScenario{T}}) where T <: AbstractFloat
    isempty(scenarios) && return zero(SMPSScenario{T})
    expected = reduce(scenarios) do ξ₁, ξ₂
        SMPSScenario(Probability(1.0),
                     probability(ξ₁) * ξ₁.Δq + probability(ξ₂) * ξ₂.Δq,
                     probability(ξ₁) * ξ₁.ΔT + probability(ξ₂) * ξ₂.ΔT,
                     probability(ξ₁) * ξ₁.ΔW + probability(ξ₂) * ξ₂.ΔW,
                     probability(ξ₁) * ξ₁.Δh + probability(ξ₂) * ξ₂.Δh,
                     probability(ξ₁) * ξ₁.ΔC + probability(ξ₂) * ξ₂.ΔC,
                     probability(ξ₁) * ξ₁.Δd₁ + probability(ξ₂) * ξ₂.Δd₁,
                     probability(ξ₁) * ξ₁.Δd₂ + probability(ξ₂) * ξ₂.Δd₂)
    end
    return StochasticPrograms.ExpectedScenario(expected)
end

function StochasticPrograms.scenariotext(io::IO, scenario::SMPSScenario)
    print(io, " and underlying data:\n\n")
    println(io, "Δq = $(scenario.Δq)")
    println(io, "ΔT = $(scenario.ΔT)")
    println(io, "ΔW = $(scenario.ΔW)")
    println(io, "Δh = $(scenario.Δh)")
    println(io, "ΔC = $(scenario.ΔC)")
    println(io, "Δd₁ = $(scenario.Δd₁)")
    print(io, "Δd₂ = $(scenario.Δd₂)")
    return io
end


struct Prop2DAcoVTIDenQ_DEO2_FDTD
    p::Ptr{Cvoid}
end

function Prop2DAcoVTIDenQ_DEO2_FDTD(;
        nz=0,
        nx=0,
        nsponge=60,
        nbz=512,
        nbx=8,
        dz=5.0,
        dx=5.0,
        dt=0.001,
        nthreads=Sys.CPU_THREADS,
        freesurface=true,
        freqQ=5.0,
        qMin=0.1,
        qInterior=100.0)
    nz,nx,nsponge,nbz,nbx,nthreads = map(x->round(Int,x), (nz,nx,nsponge,nbz,nbx,nthreads))
    dz,dx,dt = map(x->Float32(x), (dz,dx,dt))

    @assert nx > 0
    @assert nz > 0
    @assert nsponge > 0
    @assert nthreads > 0
    @assert nbx > 0
    @assert nbz > 0

    fs = freesurface ? 1 : 0

    p = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_alloc, libprop2DAcoVTIDenQ_DEO2_FDTD),
        Ptr{Cvoid},
        (Clong, Clong,    Clong, Clong, Clong,   Cfloat, Cfloat, Cfloat, Clong, Clong),
         fs,    nthreads, nx,    nz,    nsponge, dx,     dz,     dt,     nbx,   nbz)

    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_SetupDtOmegaInvQ, libprop2DAcoVTIDenQ_DEO2_FDTD),
        Cvoid,
        (Ptr{Cvoid}, Cfloat, Cfloat, Cfloat),
         p,          freqQ,  qMin,   qInterior)

    Prop2DAcoVTIDenQ_DEO2_FDTD(p)
end

free(prop::Prop2DAcoVTIDenQ_DEO2_FDTD) = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_free, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid, (Ptr{Cvoid},), prop.p)

function size(prop::Prop2DAcoVTIDenQ_DEO2_FDTD)
    nz = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_getNz, libprop2DAcoVTIDenQ_DEO2_FDTD), (Clong), (Ptr{Cvoid},), prop.p)
    nx = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_getNx, libprop2DAcoVTIDenQ_DEO2_FDTD), (Clong), (Ptr{Cvoid},), prop.p)
    (nz,nx)
end

for _f in (:V, :Eps, :Eta, :B, :F, :PSpace, :MSpace, :PCur, :POld, :MCur, :MOld, :DtOmegaInvQ)
    symf = "Prop2DAcoVTIDenQ_DEO2_FDTD_get" * string(_f)
    @eval $(_f)(prop::Prop2DAcoVTIDenQ_DEO2_FDTD) = unsafe_wrap(Array, ccall(($symf, libprop2DAcoVTIDenQ_DEO2_FDTD), Ptr{Float32}, (Ptr{Cvoid},), prop.p), size(prop), own=false)
end

propagateforward!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD) = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_TimeStep, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid, (Ptr{Cvoid},), prop.p)
propagateforward_linear!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD) = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_TimeStepLinear, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid, (Ptr{Cvoid},), prop.p)
propagateadjoint!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD) = propagateforward_linear!(prop) # self-adjoint

scale_spatial_derivatives!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD) =
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_ScaleSpatialDerivatives, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid, (Ptr{Cvoid},), prop.p)

abstract type Prop2DAcoVTIDenQ_DEO2_FDTD_Model end
struct Prop2DAcoVTIDenQ_DEO2_FDTD_Model_VEA <: Prop2DAcoVTIDenQ_DEO2_FDTD_Model end
struct Prop2DAcoVTIDenQ_DEO2_FDTD_Model_V <: Prop2DAcoVTIDenQ_DEO2_FDTD_Model end

# v,ϵ and η in model-space
function forwardBornInjection!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD, modeltype::Prop2DAcoVTIDenQ_DEO2_FDTD_Model_VEA, dmodel, wavefields)
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_ForwardBornInjection_VEA, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid,
        (Ptr{Cvoid}, Ptr{Cfloat}, Ptr{Cfloat}, Ptr{Cfloat}, Ptr{Cfloat},        Ptr{Cfloat},        Ptr{Cfloat},          Ptr{Cfloat}),
         prop.p,     dmodel["v"], dmodel["ϵ"], dmodel["η"], wavefields["pold"], wavefields["mold"], wavefields["pspace"], wavefields["mspace"])
end

function adjointBornAccumulation!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD, modeltype::Prop2DAcoVTIDenQ_DEO2_FDTD_Model_VEA,
        imagingcondition::ImagingConditionStandard, dmodel, wavefields)
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_AdjointBornAccumulation_VEA, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid,
        (Ptr{Cvoid}, Ptr{Cfloat}, Ptr{Cfloat}, Ptr{Cfloat}, Ptr{Cfloat},        Ptr{Cfloat},        Ptr{Cfloat},          Ptr{Cfloat}),
         prop.p,     dmodel["v"], dmodel["ϵ"], dmodel["η"], wavefields["pold"], wavefields["mold"], wavefields["pspace"], wavefields["mspace"])
end

# v in model-space
function forwardBornInjection!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD, modeltype::Prop2DAcoVTIDenQ_DEO2_FDTD_Model_V, dmodel, wavefields)
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_ForwardBornInjection_V, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid,
        (Ptr{Cvoid}, Ptr{Cfloat}, Ptr{Cfloat},          Ptr{Cfloat}),
         prop.p,     dmodel["v"], wavefields["pspace"], wavefields["mspace"])
end

function adjointBornAccumulation!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD, modeltype::Prop2DAcoVTIDenQ_DEO2_FDTD_Model_V,
        imagingcondition::ImagingConditionStandard, dmodel, wavefields)
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_AdjointBornAccumulation_V, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid,
        (Ptr{Cvoid}, Ptr{Cfloat}, Ptr{Cfloat},          Ptr{Cfloat}),
         prop.p,     dmodel["v"], wavefields["pspace"], wavefields["mspace"])
end

function adjointBornAccumulation!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD, modeltype::Prop2DAcoVTIDenQ_DEO2_FDTD_Model_V,
        imagingcondition::ImagingConditionWaveFieldSeparationFWI, dmodel, wavefields)
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_AdjointBornAccumulation_wavefieldsep_V, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid,
        (Ptr{Cvoid}, Ptr{Cfloat}, Ptr{Cfloat},          Ptr{Cfloat},          Clong),
         prop.p,     dmodel["v"], wavefields["pspace"], wavefields["mspace"], 1)
end

function adjointBornAccumulation!(prop::Prop2DAcoVTIDenQ_DEO2_FDTD, modeltype::Prop2DAcoVTIDenQ_DEO2_FDTD_Model_V,
        imagingcondition::ImagingConditionWaveFieldSeparationRTM, dmodel, wavefields)
    ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_AdjointBornAccumulation_wavefieldsep_V, libprop2DAcoVTIDenQ_DEO2_FDTD), Cvoid,
        (Ptr{Cvoid}, Ptr{Cfloat}, Ptr{Cfloat},          Ptr{Cfloat},          Clong),
         prop.p,     dmodel["v"], wavefields["pspace"], wavefields["mspace"], 0)
end

function show(io::IO, prop::Prop2DAcoVTIDenQ_DEO2_FDTD)
    nx = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_getNx, libprop2DAcoVTIDenQ_DEO2_FDTD), (Clong), (Ptr{Cvoid},), prop.p)
    nz = ccall((:Prop2DAcoVTIDenQ_DEO2_FDTD_getNz, libprop2DAcoVTIDenQ_DEO2_FDTD), (Clong), (Ptr{Cvoid},), prop.p)
    write(io, "Prop2DAcoVTIDenQ_DEO2_FDTD -- nx,nz; $nx,$nz")
end


"""
    compute(eph,jd0,time,target,center)

Compute position and velocity of target with respect to center at epoch
jd0+time. This method does not support the NAIF numbering scheme.
To get the best precision for the interpolation, the time is splitted in two
floating-point numbers. The argument jd0 should be an integer and time should
be a fraction of the day. But you may call this function with time=0 and jd0,
the desired time, if you don't care about precision.

This method does not support the NAIF body identification scheme.

Output units are:
* AU and AU/day for position and velocity
* rad and rad/day for librations
* second and unitless for time ephemeris and time ephemeris rate

# Arguments
- `eph`: ephemeris
- `jd0::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `time::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `target::Integer`: The body or reference point whose coordinates are required.
- `center::Integer`: The origin of the coordinate system.

The possible values for target and center are :
* 1 : Mercury Barycenter
* 2 : Venus Barycenter
* 3 : Earth
* 4 : Mars Barycenter
* 5 : Jupiter Barycenter
* 6 : Saturn Barycenter
* 7 : Uranus Barycenter
* 8 : Neptune Barycenter
* 9 : Pluto Barycenter
* 10    : Moon
* 11    : Sun
* 12    : Solar Sytem barycenter
* 13    : Earth-moon barycenter
* 14    : Nutation angles
* 15    : Librations
* 16    : TT-TDB
* 17    : TCG-TCB
* asteroid number + 2000000    : asteroid

"""
function compute(eph::Ephem,jd0::Float64,time::Float64,
   target::Integer,center::Integer)
    @_checkPointer eph.data "Ephemeris is not properly initialized!"
    result = Array{Float64,1}(undef,6)
    stat = unsafe_compute!(result,eph,jd0,time,target,center)
    @_checkStatus stat "Unable to compute ephemeris"
    return result
end

"""
    unsafe_compute!(eph,jd0,time,target,center)

In place version of the compute function. Does not perform any checks!
Compute position and velocity of target with respect to center at epoch
jd0+time. This method does not support the NAIF numbering scheme.
To get the best precision for the interpolation, the time is splitted in two
floating-point numbers. The argument jd0 should be an integer and time should
be a fraction of the day. But you may call this function with time=0 and jd0,
the desired time, if you don't care about precision.

This method does not support the NAIF body identification scheme.

Output units are:
* AU and AU/day for position and velocity
* rad and rad/day for librations
* second and unitless for time ephemeris and time ephemeris rate

# Arguments
- `result`: container for result. It is not checked if it is sufficiently large enough!
- `eph`: ephemeris
- `jd0::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `time::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `target::Integer`: The body or reference point whose coordinates are required.
- `center::Integer`: The origin of the coordinate system.

# Return:
- status integer from CALCEPH: 0 if an error occured

The possible values for target and center are :
* 1 : Mercury Barycenter
* 2 : Venus Barycenter
* 3 : Earth
* 4 : Mars Barycenter
* 5 : Jupiter Barycenter
* 6 : Saturn Barycenter
* 7 : Uranus Barycenter
* 8 : Neptune Barycenter
* 9 : Pluto Barycenter
* 10    : Moon
* 11    : Sun
* 12    : Solar Sytem barycenter
* 13    : Earth-moon barycenter
* 14    : Nutation angles
* 15    : Librations
* 16    : TT-TDB
* 17    : TCG-TCB
* asteroid number + 2000000    : asteroid

"""
function unsafe_compute!(result,eph::Ephem,jd0::Float64,time::Float64,
   target::Integer,center::Integer)
    stat = ccall((:calceph_compute, libcalceph), Cint,
    (Ptr{Cvoid},Cdouble,Cdouble,Cint,Cint,Ref{Cdouble}),
    eph.data,jd0,time,target,center,result)
    return stat
end

"""
    compute(eph,jd0,time,target,center,unit)

Compute position and velocity of target with respect to center
at epoch jd0+time.
To get the best precision for the interpolation, the time is splitted in two
floating-point numbers. The argument jd0 should be an integer and time should
be a fraction of the day. But you may call this function with time=0 and jd0,
the desired time, if you don't care about precision.

# Arguments
- `eph`: ephemeris
- `jd0::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `time::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `target::Integer`: The body or reference point whose coordinates are required. The numbering system depends on the parameter unit.
- `center::Integer`: The origin of the coordinate system. The numbering system depends on the parameter unit.
- `unit::Integer` : The units of the result. This integer is a sum of some unit constants (unit*) and/or the constant useNaifId. If the unit contains useNaifId, the NAIF identification numbering system is used for the target and the center. If the unit does not contain useNaifId, the old number system is used for the target and the center.

"""
function compute(eph::Ephem,jd0::Float64,time::Float64,
   target::Integer,center::Integer,unit::Integer)
    @_checkPointer eph.data "Ephemeris is not properly initialized!"
    result = Array{Float64,1}(undef,6)
    stat = unsafe_compute!(result,eph,jd0,time,target,center,unit)
    @_checkStatus stat "Unable to compute ephemeris"
    return result
end

"""
    unsafe_compute!(result,eph,jd0,time,target,center,unit)

In place version of the compute function. Does not perform any checks!
Compute position and velocity of target with respect to center
at epoch jd0+time.
To get the best precision for the interpolation, the time is splitted in two
floating-point numbers. The argument jd0 should be an integer and time should
be a fraction of the day. But you may call this function with time=0 and jd0,
the desired time, if you don't care about precision.

# Arguments
- `result`: container for result. It is not checked if it is sufficiently large enough!
- `eph`: ephemeris
- `jd0::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `time::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `target::Integer`: The body or reference point whose coordinates are required. The numbering system depends on the parameter unit.
- `center::Integer`: The origin of the coordinate system. The numbering system depends on the parameter unit.
- `unit::Integer` : The units of the result. This integer is a sum of some unit constants (unit*) and/or the constant useNaifId. If the unit contains useNaifId, the NAIF identification numbering system is used for the target and the center. If the unit does not contain useNaifId, the old number system is used for the target and the center.

# Return:
- status integer from CALCEPH: 0 if an error occured

"""
function unsafe_compute!(result,eph::Ephem,jd0::Float64,time::Float64,
   target::Integer,center::Integer,unit::Integer)
    stat = ccall((:calceph_compute_unit, libcalceph), Cint,
    (Ptr{Cvoid},Cdouble,Cdouble,Cint,Cint,Cint,Ref{Cdouble}),
    eph.data,jd0,time,target,center,unit,result)
    return stat
end

"""
    compute(eph,jd0,time,target,center,unit,order)

Compute position and derivatives up to order of target with respect to center
at epoch jd0+time.
To get the best precision for the interpolation, the time is splitted in two
floating-point numbers. The argument jd0 should be an integer and time should
be a fraction of the day. But you may call this function with time=0 and jd0,
the desired time, if you don't care about precision.

# Arguments
- `eph`: ephemeris
- `jd0::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `time::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `target::Integer`: The body or reference point whose coordinates are required. The numbering system depends on the parameter unit.
- `center::Integer`: The origin of the coordinate system. The numbering system depends on the parameter unit.
- `unit::Integer` : The units of the result. This integer is a sum of some unit constants (unit*) and/or the constant useNaifId. If the unit contains useNaifId, the NAIF identification numbering system is used for the target and the center. If the unit does not contain useNaifId, the old number system is used for the target and the center.
- `order::Integer` : The order of derivatives
    * 0: only the position is computed.
    * 1: only the position and velocity are computed.
    * 2: only the position, velocity and acceleration are computed.
    * 3: the position, velocity and acceleration and jerk are computed.

"""
function compute(eph::Ephem,jd0::Float64,time::Float64,
   target::Integer,center::Integer,unit::Integer,order::Integer)
    @_checkPointer eph.data "Ephemeris is not properly initialized!"
    @_checkOrder order
    result = Array{Float64,1}(undef,3+3order)
    stat = unsafe_compute!(result,eph,jd0,time,target,center,unit,order)
    @_checkStatus stat "Unable to compute ephemeris"
    return result
end

"""
    unsafe_compute!(result,eph,jd0,time,target,center,unit,order)

In place version of the compute function. Does not perform any checks!
Compute position and derivatives up to order of target with respect to center
at epoch jd0+time.
To get the best precision for the interpolation, the time is splitted in two
floating-point numbers. The argument jd0 should be an integer and time should
be a fraction of the day. But you may call this function with time=0 and jd0,
the desired time, if you don't care about precision.

# Arguments
- `result`: container for result. It is not checked if it is sufficiently large enough!
- `eph`: ephemeris
- `jd0::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `time::Float64`: jd0+time must be equal to the Julian Day for the time coordinate corresponding to the ephemeris (usually TDB or TCB)
- `target::Integer`: The body or reference point whose coordinates are required. The numbering system depends on the parameter unit.
- `center::Integer`: The origin of the coordinate system. The numbering system depends on the parameter unit.
- `unit::Integer` : The units of the result. This integer is a sum of some unit constants (unit*) and/or the constant useNaifId. If the unit contains useNaifId, the NAIF identification numbering system is used for the target and the center. If the unit does not contain useNaifId, the old number system is used for the target and the center.
- `order::Integer` : The order of derivatives
    * 0: only the position is computed.
    * 1: only the position and velocity are computed.
    * 2: only the position, velocity and acceleration are computed.
    * 3: the position, velocity and acceleration and jerk are computed.

# Return:
- status integer from CALCEPH: 0 if an error occured

"""
function unsafe_compute!(result,eph::Ephem,jd0::Float64,time::Float64,
   target::Integer,center::Integer,unit::Integer,order::Integer)
    stat = ccall((:calceph_compute_order, libcalceph), Cint,
    (Ptr{Cvoid},Cdouble,Cdouble,Cint,Cint,Cint,Cint,Ref{Cdouble}),
    eph.data,jd0,time,target,center,unit,order,result)
    return stat
end


#!/usr/bin/env julia
println("Initialising libraries...")
using NeRCA
using Plots
using PlotThemes
using Dates
using Measures
GR.inline("png")

if length(ARGS) < 3
    println("Usage: ./live_royfit.jl DETX LIGIER_PORT TIME_RES")
    exit(1)
end

const calib = Calibration("latest.detx")
const LIGIER_PORT = parse(Int, ARGS[2])
const TIME_RES = ARGS[3]
const DOWNSAMPLE = 0.1  # fraction to keep

function main()
    println("Starting live ROyFit")

    sparams = NeRCA.SingleDURecoParams()

    for message in CHClient(ip"127.0.0.1", LIGIER_PORT, ["IO_EVT"])
        if rand() > DOWNSAMPLE
            print("x")
            continue
        else
            print(".")
        end
        event = read(IOBuffer(message.data), NeRCA.DAQEvent)

        hits = calibrate(calib, event.hits)
        triggered_hits = triggered(hits)
        dus = sort(unique(map(h->h.du, hits)))
        triggered_dus = sort(unique(map(h->h.du, triggered_hits)))
        n_dus = length(dus)
        n_triggered_dus = length(triggered_dus)
        n_doms = length(unique(h->h.dom_id, triggered_hits))

        if n_doms < 4
            continue
        end

        colours = palette(:default)
        plot()
        Q = []
        for (idx, du) in enumerate(dus)
            du_hits = filter(h->h.du == du, hits)
            if length(triggered(du_hits))== 0
                continue
            end
            fit = NeRCA.single_du_fit(du_hits, sparams)
            push!(Q, fit.Q)
            plot!(du_hits, fit, markercolor=colours[idx], label="DU $(du)", max_z=calib.max_z)
            write_time_residuals(TIME_RES, event, fit.selected_hits, fit)
        end
        if sum(Q) < 200 && n_doms > 12 && n_dus > 1
            println("\nPlotting...")
            fit_params = "ROy live reconstruction (combined single line): Q=$([round(_Q,digits=2) for _Q in Q])"
            event_params = "Det ID $(event.det_id), Run $(event.run_id), FrameIndex $(event.timeslice_id), TriggerCounter $(event.trigger_counter), Overlays $(event.overlays)"
            time_params = "$(unix2datetime(event.timestamp)) UTC"
            trigger_params = "Trigger: $(is_mxshower(event) ? "MX " : "")$(is_3dmuon(event) ? "3DM " : "")$(is_3dshower(event) ? "3DS " : "")"
            time_params = "$(unix2datetime(event.timestamp)) UTC"

            plot!(title="$(fit_params)\n$(event_params), $(trigger_params)\n$(time_params)", titlefontsize=8, margin=5mm)

            savefig("plots/ztplot_roy.png")
        end

        #= if n_triggered_dus > 2 && n_doms > 14 =#
        #=     selected_hits = unique(h->h.dom_id, triggered_hits) =#
        #=     println("\nStarting multiline fit with $(n_dus) DUs and $(length(selected_hits)) selected hits") =#
        #=     prefit_track = NeRCA.prefit(selected_hits) =#
        #=     println(prefit_track) =#
        #=     plot(selected_hits, prefit_track) =#
        #=     savefig("plots/ztplot_roy_prefit.png") =#
        #= end =#
    end
end

function write_time_residuals(filename, event, hits, fit)
    if !isfile(filename)
        fobj = open(filename, "w")
        write(fobj, "run,timestamp,du,floor,dom_id,t_res,Q\n")
    else
        fobj = open(filename, "a")
    end
    dγ, ccalc = NeRCA.make_cherenkov_calculator(fit.sdp)
    for hit in hits
        Δt = hit.t - ccalc(hit.pos.z)
        write(fobj, "$(event.run_id),$(event.timestamp),$(hit.du),$(hit.floor),$(hit.dom_id),$(Δt),$(fit.Q)\n")
    end
    close(fobj)
end

main()


export initialize

function initialize(model;groups=[],expr="",parallel=false)
  var=match(r"^.*(?==)",expr).match
  value=match(r"(?<==).*",expr).match
  if !contains(model.initialization,var)
    N=model.groups[:N]
    model.initialization*="$var=SharedArray(Float64,N)\n"
  end
  if groups==[] || groups=="all"
    model.initialization*="for i=1:N $var[i]=$value end\n"
  else
    for name in [groups;]
    i=model.groups[name][:localindex]
    ii="(i-$(i[1]-1))"
    value=replace(value,"i",ii)
    model.initialization*="for i=$i $var[i]=$value end\n"
    end
  end
end