text:
function diagnose_velocities_from_streamfunction(ψ, grid)
    ψᶠᶠᶜ = Field(Face, Face,   Center, CPU(), grid)
    uᶠᶜᶜ = Field(Face, Center, Center, CPU(), grid)
    vᶜᶠᶜ = Field(Center, Face, Center, CPU(), grid)

    for (f, grid_face) in enumerate(grid.faces)
        Nx, Ny, Nz = size(grid_face)

        ψᶠᶠᶜ_face = ψᶠᶠᶜ.data.faces[f]
        uᶠᶜᶜ_face = uᶠᶜᶜ.data.faces[f]
        vᶜᶠᶜ_face = vᶜᶠᶜ.data.faces[f]

        for i in 1:Nx+1, j in 1:Ny+1
            ψᶠᶠᶜ_face[i, j, 1] = ψ(grid_face.λᶠᶠᵃ[i, j], grid_face.φᶠᶠᵃ[i, j])
        end

        for i in 1:Nx+1, j in 1:Ny
            uᶠᶜᶜ_face[i, j, 1] = (ψᶠᶠᶜ_face[i, j, 1] - ψᶠᶠᶜ_face[i, j+1, 1]) / grid.faces[f].Δyᶠᶜᵃ[i, j]
        end

        for i in 1:Nx, j in 1:Ny+1
            vᶜᶠᶜ_face[i, j, 1] = (ψᶠᶠᶜ_face[i+1, j, 1] - ψᶠᶠᶜ_face[i, j, 1]) / grid.faces[f].Δxᶜᶠᵃ[i, j]
        end
    end

    return uᶠᶜᶜ, vᶜᶠᶜ, ψᶠᶠᶜ
end

function set_velocities_from_streamfunction!(u, v, ψ, grid)
    uψ, vψ, ψ₀ = diagnose_velocities_from_streamfunction(ψ, grid)
    set!(u, uψ)
    set!(v, vψ)
    return nothing
end

@testset "Cubed sphere circulation" begin

    arch = CPU()
    grid = ConformalCubedSphereGrid(cs32_filepath, Nz=1, z=(-1, 0))

    FT = eltype(grid)
    Nx, Ny, Nz, Nf = size(grid)
    R = grid.faces[1].radius

    u_field = XFaceField(arch, grid)
    v_field = YFaceField(arch, grid)

    ψ(λ, φ) = R * sind(φ)
    set_velocities_from_streamfunction!(u_field, v_field, ψ, grid)

    fill_horizontal_velocity_halos!(u_field, v_field, arch)

    grid_faces = [grid.faces[f] for f in 1:Nf]
    u_faces = [get_face(u_field, f) for f in 1:Nf]
    v_faces = [get_face(v_field, f) for f in 1:Nf]

    circulation(i, j, f) = Γᶠᶠᵃ(i, j, 1, grid_faces[f], u_faces[f], v_faces[f])

    @testset "Face 1-2 boundary" begin
        # Quick test at a single point.
        Γ1 = circulation(33, 16, 1)
        Γ2 = circulation(1,  16, 2)
        @test Γ1 == Γ2

        # Check the entire boundary.
        Γ1 = [circulation(33, j, 1) for j in 1:Ny+1]
        Γ2 = [circulation(1,  j, 2) for j in 1:Ny+1]

        # There should be no truncation errors here.
        @test Γ1 == Γ2
    end

    @testset "Face 1-3 boundary" begin
        Γ1 = circulation(16, 33, 1)
        Γ3 = circulation(1,  16, 3)

        # We expect a truncation error of around 3ϵ here.
        ϵ = eps(maximum(abs, [Γ1, Γ3]))
        @test isapprox(Γ1, Γ3, atol=3ϵ)

        # ffc locations are shifted by one along this boundary (and go in reverse).
        Γ1 = [circulation(i, 33, 1) for i in 1:Nx]
        Γ3 = [circulation(1,  j, 3) for j in Ny+1:-1:2]

        # We expect truncation errors at this boundary.
        ϵ = eps(maximum(abs, [Γ1; Γ3]))
        @test all(isapprox.(Γ1, Γ3, atol=4ϵ))
    end

    @testset "Face 1-5 boundary" begin
        Γ1 = [circulation(1,  j, 1) for j in 1:Ny]
        Γ5 = [circulation(i, 33, 5) for i in Nx+1:-1:2]

        # Hmmm there's more truncation error here.
        ϵ = eps(maximum(abs, [Γ1; Γ5]))
        @test all(isapprox.(Γ1, Γ5, atol=32ϵ))
    end

    @testset "Face 1-6 boundary" begin
        Γ1 = [circulation(i,  1, 1) for i in 1:Nx+1]
        Γ6 = [circulation(i, 33, 6) for i in 1:Nx+1]
        @test Γ1 == Γ6
    end

    @testset "Face 1-2-3 corner" begin
        Γ1 = circulation(33, 33, 1)
        Γ2 = circulation(1,  33, 2)
        Γ3 = circulation(1,  1,  3)
        @test Γ1 == Γ2 == Γ3
    end

    @testset "Face 1-3-5 corner" begin
        Γ1, Γ3, Γ5 = Γ = [circulation(1, 33, f) for f in (1, 3, 5)]
        ϵ = eps(maximum(abs, Γ))
        @test isapprox(Γ1, Γ3, atol=32ϵ)
        @test isapprox(Γ1, Γ5, atol=32ϵ)
        @test isapprox(Γ3, Γ5, atol=32ϵ)
    end

    @testset "Face 1-5-6 corner" begin
        Γ1 = circulation(1,   1, 1)
        Γ5 = circulation(33, 33, 5)
        Γ6 = circulation(1,  33, 6)
        @test Γ1 == Γ5 == Γ6
    end

    @testset "Face 1-2-6 corner" begin
        Γ1 = circulation(33,  1, 1)
        Γ2 = circulation(1,   1, 2)
        Γ6 = circulation(33, 33, 6)
        @test Γ1 == Γ2 == Γ6
    end

    @testset "Face 2-3-4 corner" begin
        Γ2 = circulation(33, 33, 2)
        Γ3 = circulation(33, 1,  3)
        Γ4 = circulation(1,  1,  4)
        Γ = [Γ2, Γ3, Γ4]

        ϵ = eps(maximum(abs, Γ))
        @test isapprox(Γ2, Γ3, atol=32ϵ)
        @test isapprox(Γ2, Γ4, atol=32ϵ)
        @test isapprox(Γ3, Γ4, atol=32ϵ)
    end

    @testset "Face 2-4-6 corner" begin
        Γ2, Γ4, Γ6 = Γ = [circulation(33, 1, f) for f in (2, 4, 6)]
        @show ϵ = eps(maximum(abs, Γ))
        @test isapprox(Γ2, Γ4, atol=32ϵ)
        @test isapprox(Γ2, Γ6, atol=32ϵ)
        @test isapprox(Γ4, Γ6, atol=32ϵ)
    end

end


text:
using GEMPIC

@testset " Mesh " begin
    mesh1d = OneDGrid(-π, π, 11)
    mesh2d = TwoDGrid(0.0, 2π, 11, -6.0, 6.0, 21)
    #mesh3d = Mesh( 0.0, 2π, 11, -π, π, 21, 0.0, 1.0, 32 )

    @test get_x(mesh1d, 1) ≈ -π
    @test get_x(mesh1d, 6) ≈ 0.0
    #@test get_x(mesh2d, [1,1])       ≈ [0.0, -6.0]
    #@test get_x(mesh2d, [6,11])      ≈ [π, 0.0]
    #@test get_x(mesh3d, [1,1,1])     ≈ [0.0, -π, 0.0]
    #@test get_x(mesh3d, [6, 11, 32]) ≈ [π, 0.0, 1.0]

end

ext: jl


text:
using Performance
using Test

@testset "globals" begin
    ##
    ## https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-global-variables
    ##
    @test getchar(5) == 'e'                # this should pass from the beginning
    # The next test will fail. Use `@code_warntype getchar(5)` from the REPL to see the problem
    # and then fix it.
    # Tip: sometimes you have to determine types via the process of elimination (TODO: fix that in Julia)
    # In this case, make sure you see that:
    # - `idx` has known, concrete type
    # - that Julia knows that `getindex` refers to `Base.getindex`
    # - that Julia knows that `charset` refers to `Performance.charset`
    # but notice it doesn't show you any type info for `charset`. By the process of elimination,
    # that must be the source of the poor type-inference.
    # (If you pass an argument of unknown type to a function, generally you also can't predict the type it
    # will return.)
    @test @inferred(getchar(5)) == 'e'   # see `?@inferred`
    # Check `@code_warntype` again at the end to see that you've fixed it!

    # The next two tests should pass even at the outset
    @test get_homedir() == "wrong"
    set_homedir(ENV["HOME"])
    @test get_homedir() == ENV["HOME"]
    # This one will fail. But you can't make the value constant and still update it.
    # Find a way to fix it! Hint: `?Ref` or use an array (Refs are like single-element arrays)
    # Explanation: the *container* is constant, but the *contents* can be modified.
    # (Think of the container like a box, which is distinct from the things inside it.)
    # If the container type specifies the type of the contents, Julia's type-inference will be happy.
    @test @inferred(get_homedir()) == ENV["HOME"]
end


text:
function MarkAllCells(r)
    side = Ost
    moves_Sud = movementsandcountmoves(r, Sud)
    moves_West = movementsandcountmoves(r, West)
    markrow(r, side)
    putmarker!(r)
    while isborder(r, Nord) == false
        move!(r, Nord)
        side = inverse(side)
        markrow(r, side)
        putmarker!(r)
    end
    movements(r, Sud)
    movements(r, West)
    movementsbyfixmoves(r, Ost, moves_West)
    movementsbyfixmoves(r, Nord, moves_Sud)
end

function movements(r, side)
    while isborder(r, side) == false
        move!(r, side)
    end
end

function inverse(side)
    return HorizonSide((Int(side) + 2) % 4)
end

function markrow(r, side)
    while isborder(r, side) == false
        putmarker!(r)
        move!(r, side)
    end
end

function movementsandcountmoves(r, side)
    moves = 0
    while isborder(r, side) == false
        move!(r, side)
        moves += 1
    end
    return moves
end

function movementsbyfixmoves(r, side, moves)
    while moves != 0
        move!(r, side)
        moves -= 1
    end
end


text:
function generate_boards(::Type{Game}, n) where Game
  player = RandomPlayer{Game}()
  rec = AlphaZero.Recorder{Game}()
  for i in 1:n
    self_play!(player, rec)
  end
  return Set(rec.boards)
end

function test_symmetries(::Type{Game}) where Game
  boards = generate_boards(Game, 100)
  for b in boards
    syms = GI.symmetries(Game, b)
    for sym in syms
      @test GI.test_symmetry(Game, b, sym)
    end
  end
end


text:
using Test
using LinearAlgebraicRepresentation
Lar = LinearAlgebraicRepresentation
using DataStructures,IntervalTrees

@testset "2D containment tests" begin

(V, EV) = ([0.43145 0.596771 0.758062 1.0 0.778226 0.919353 0.879033 0.806447 0.778226 0.709677 0.596771 0.262094 0.322578 0.0 0.2379 0.161291 0.467739 0.429435 0.627999 0.627999 0.383062 0.694833 0.653221 0.544027 0.778226 0.848789 0.750707 0.627999 0.694833 0.806447; -0.0163938 0.22521 0.104412 0.325182 0.629266 0.683418 0.820882 0.725074 0.845873 0.75215 1.0 0.820882 0.629266 0.385151 0.43765 0.246033 0.466811 0.629266 0.704244 0.507207 0.275195 0.683418 0.43765 0.291323 0.199264 0.43765 0.497413 0.341841 0.259902 0.364484], Array{Int64,1}[[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], [15, 16], [16, 17], [17, 18], [18, 19], [19, 20], [20, 21], [21, 1], [22, 23], [24, 23], [24, 25], [25, 26], [26, 22], [27, 28], [28, 29], [29, 30], [30, 27]])

classify = Lar.pointInPolygonClassification(V,EV)
queryPoint = [0.5,0.5]

	@testset "crossingTest Tests" begin
		@test Lar.crossingTest(0, 0, 0., 0)::Number == 0.5
		@test Lar.crossingTest(0, 0, 0.5, 0)::Number == 1.0
		@test Lar.crossingTest(0, 0, 0.5, 0)::Number == 1.0
		@test Lar.crossingTest(1, 0, 0.5, 0) == 1.0
		@test Lar.crossingTest(1, 1, 0.5, 0) == 1.0
		@test Lar.crossingTest(1, 1, 0.5, 1) == 0
	end

	@testset "setTile Tests" begin
		x,y = 0.5,0.75
		xmin,xmax,ymin,ymax = x,x,y,y
		box = [ymax,ymin,xmax,xmin]
		tilecode = Lar.setTile(box)

		@test Lar.setTile isa Function
		@test typeof(box)==Array{Float64,1}
		@test Lar.setTile(box) isa Function
		@test tilecode([.5,.5])==2
		@test tilecode([-.5,.5])==10
		@test tilecode([.5,-.5])==2
		@test tilecode([-.5,-.5])==10
		@test tilecode([.5,.95])==1
	end

	@testset "pointInPolygonClassification Tests" begin
		@test Lar.pointInPolygonClassification(V,EV) isa Function
		@test pnt = [0.5,0.5] isa Array{Float64,1}
		@test classify(queryPoint)=="p_out"
		@test classify([0.5,0.75])=="p_in"
		@test classify([1.5,0.75])=="p_out"
		@test typeof(classify(queryPoint))==String
	end
end

@testset "Biconnected components" begin

(V, (VV, EV, FV)) = Lar.cuboidGrid([3, 3], true)

	@testset "verts2verts data Tests" begin
		@test Lar.cuboidGrid([3,3],true) == ([0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 2.0 2.0 2.0 2.0 3.0 3.0 3.0 3.0; 0.0 1.0 2.0 3.0 0.0 1.0 2.0 3.0 0.0 1.0 2.0 3.0 0.0 1.0 2.0 3.0], ( [[[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16]], [[1, 2],[2,3],[3,4],[5,6],[6,7],[7,8],[9,10],[10,11],[11,12],[13,14],[14, 15],[15,16],[1,5],[2,6],[3,7],[4,8],[5,9],[6,10],[7,11],[8,12],[9,13], [10,14],[11,15],[12,16]], [[1,2,5,6],[2,3,6,7],[3,4,7,8],[5,6,9,10], [6,7,10,11],[7,8,11,12],[9,10,13,14],[10,11,14,15],[11,12,15,16]]] ) )
		@test size(V,2)==length(VV)
		@test length(VV)==16
		@test length(EV)==24
		@test length(FV)==9
		@test length(VV)-length(EV)+length(FV)==1
		@test Lar.verts2verts(EV::Lar.Cells)==[[2,5],[1,3,6],[2,4,7],[3,8],[1,
		6,9],[2,5,7,10],[3,6,8,11],[4,7,12],[5,10,13],[6,9,11,14],[7,10,12,
		15],[8,11,16],[9,14],[10,13,15],[11,14,16],[12,15]]
		@test Lar.verts2verts(FV::Lar.Cells)==[[2,5,6],[1,3,5,6,7],[2,4,6,7,
		8],[3,7,8],[1,2,6,9,10],[1,2,3,5,7,9,10,11],[2,3,4,6,8,10,11,12],[3,
		4,7,11,12],[5,6,10,13,14],[5,6,7,9,11,13,14,15],[6,7,8,10,12,14,15,
		16],[7,8,11,15,16],[9,10,14],[9,10,11,13,15],[10,11,12,14,16],[11,12,15]]
	end

	@testset "DFV_visit Tests" begin
		@test true
	end

	@testset "outputComp Tests" begin
		@test Lar.outputComp isa Function
		@test true
	end

	@testset "biconnectedComponent Tests" begin
		#(V, EV) = ([0.0 0.97721 0.97721 0.724048 0.724048 0.258225 0.258225 0.660757 0.660757 0.0; 1.0 1.0 0.0 0.0 0.934178 0.934178 0.346836 0.346836 0.0 0.0], [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 1]])
		#V,EVs = Lar.biconnectedComponent((V,EV))
		#@test sort(map(sort,EVs[1]))==sort(map(sort,EV))
		#@test Lar.biconnectedComponent((V,EV)) == ([0.0 0.97721 0.97721 0.724048 0.724048 0.258225 0.258225 0.660757 0.660757 0.0; 1.0 1.0 0.0 0.0 0.934178 0.934178 0.346836 0.346836 0.0 0.0], Any[Array{Int64,1}[[1, 10], [9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]])
		#@test length(EVs)==1
		#@test typeof(EVs[1])==Array{Array{Int64,1},1}
		#@test typeof(EVs)==Array{Any,1}
	end
end

@testset "Refactoring spaceindex tests" begin

	# 2x2x2 cuboidal grid for 1-, 2-, and 3-dim tests
	V,(VV,EV,FV,CV) = Lar.cuboidGrid([2,2,2],true)
	W,_ = Lar.apply(Lar.r(1,1,pi/6),(V,[VV,EV,FV,CV]))

	function test_bboxes(bboxes)
		# initialize accumulator
		accumulator = BitArray{1}()
		for k=1:size(bboxes[1],1)
			push!(accumulator, true)
		end
		# testing data
		for h=1:length(bboxes)
			accumulator = (bboxes[h][:,1] .< bboxes[h][:,2]) .& accumulator
		end
		return (&)(accumulator...)
	end

	@testset "boundingbox Tests" begin

		@testset "Edge tests" begin #
			cellpoints = [ W[:,EV[k]]::Lar.Points for k=1:length(EV) ]
			bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
			@test true == test_bboxes(bboxes)
		end
		@testset "Face tests" begin #
			cellpoints = [ W[:,FV[k]]::Lar.Points for k=1:length(FV) ]
			bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
			@test true == test_bboxes(bboxes)
		end
		@testset "Cell tests" begin #
			cellpoints = [ W[:,CV[k]]::Lar.Points for k=1:length(CV) ]
			bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
			@test true == test_bboxes(bboxes)
		end
	end

	@testset "coordintervals Tests" begin
	# 2x2x2 cuboidal grid for 1-, 2-, and 3-dim tests
	V,(VV,EV,FV,CV) = Lar.cuboidGrid([2,2,2],true)
	W,_ = Lar.apply(Lar.r(1,1,pi/6),(V,[VV,EV,FV,CV]))

		cellpoints = [ W[:,EV[k]]::Lar.Points for k=1:length(EV) ]
		bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
		@testset "Edge tests" begin #
			@test typeof(Lar.coordintervals(1,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
			@test typeof(Lar.coordintervals(2,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
			@test typeof(Lar.coordintervals(3,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
		end
		cellpoints = [ W[:,FV[k]]::Lar.Points for k=1:length(FV) ]
		bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
		@testset "Face tests" begin #
			@test typeof(Lar.coordintervals(1,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
			@test typeof(Lar.coordintervals(2,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
			@test typeof(Lar.coordintervals(3,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
		end
		cellpoints = [ W[:,CV[k]]::Lar.Points for k=1:length(CV) ]
		bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
		@testset "Cell tests" begin #
			@test typeof(Lar.coordintervals(1,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
			@test typeof(Lar.coordintervals(2,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
			@test typeof(Lar.coordintervals(3,bboxes)) ==
				OrderedDict{Array{Float64,1}, Array{Int64,1}}
		end
	end

	@testset "boxcovering Tests" begin
		V,(VV,EV,FV,CV) = Lar.cuboidGrid([2,2,2],true)
		W,_ = Lar.apply(Lar.r(1,1,pi/6),(V,[VV,EV,FV,CV]))
		cellpoints = [ W[:,EV[k]]::Lar.Points for k=1:length(EV) ]
		bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
		dict = Lar.coordintervals(1,bboxes)
		@test typeof(dict) == OrderedDict{Array{Float64,1},Array{Int64,1}}
		@test length(Lar.coordintervals(1,bboxes)) == 54
		@test length(Lar.coordintervals(2,bboxes)) == 54
		@test length(Lar.coordintervals(3,bboxes)) == 54

		V,(VV,EV,FV) = Lar.cuboidGrid([2,1],true)
		cellpoints = [ V[:,EV[k]]::Lar.Points for k=1:length(EV) ]
		bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
		@test bboxes == [[0.0 0.0; 0.0 1.0],
        [1.0 1.0; 0.0 1.0],
        [2.0 2.0; 0.0 1.0],
        [0.0 1.0; 0.0 0.0],
        [0.0 1.0; 1.0 1.0],
        [1.0 2.0; 0.0 0.0],
        [1.0 2.0; 1.0 1.0]]
        xboxdict = Dict(
         [0.0, 0.0] => [1],
         [1.0, 1.0] => [2],
         [2.0, 2.0] => [3],
         [0.0, 1.0] => [4, 5],
         [1.0, 2.0] => [6, 7])
        @test xboxdict == Lar.coordintervals(1,bboxes)
		xs = IntervalTrees.IntervalMap{Float64, Array}()
		for (key,boxset) in xboxdict
			xs[tuple(key...)] = boxset
		end
       @test typeof(xs) ==
		IntervalTrees.IntervalBTree{Float64,
		IntervalValue{Float64,Array},64}
	end

	@testset "Refactoring spaceindex tests" begin
		V,(VV,EV,FV) = Lar.cuboidGrid([2,1],true)
		EV = [[1, 2], [3, 4], [5, 6], [1, 3], [2, 4], [3, 5], [4, 6]]
		cellpoints = [ V[:,EV[k]]::Lar.Points for k=1:length(EV) ]
		bboxes = [hcat(Lar.boundingbox(cell)...) for cell in cellpoints]
		xboxdict = Lar.coordintervals(1,bboxes)
		yboxdict = Lar.coordintervals(2,bboxes)
		xs = IntervalTrees.IntervalMap{Float64, Array}()
		for (key,boxset) in xboxdict
			xs[tuple(key...)] = boxset
		end
		ys = IntervalTrees.IntervalMap{Float64, Array}()
		for (key,boxset) in yboxdict
			ys[tuple(key...)] = boxset
		end
		xcovers = Lar.boxcovering(bboxes, 1, xs)
		ycovers = Lar.boxcovering(bboxes, 2, ys)
		covers = [intersect(pair...) for pair in zip(xcovers,ycovers)]

		@test covers == Array{Int64,1}[[1, 4, 5], [4, 5, 2, 6, 7], [6, 7, 3],
			[1, 4, 2, 6], [1, 5, 2, 7], [4, 2, 6, 3], [5, 2, 7, 3]]
	end
end

@testset "Refactoring fragmentlines" begin

	@testset "linefragments Tests" begin
		V = hcat([[0.,0],[1,0],[1,1],[0,1],[2,1]]...);
		EV = [[1,2],[2,3],[3,4],[4,1],[1,5]];
		@test Lar.spaceindex((V,EV)) ==
		[[4, 5, 2], [1, 3, 5], [4, 5, 2], [1, 3, 5], [4, 1, 3, 2]]
		Sigma = [[4, 5, 2], [1, 3, 5], [4, 5, 2], [1, 3, 5], [4, 1, 3, 2]]
		@test Lar.linefragments(V,EV,Sigma) ==
		[[0.0, 1.0], [0.0, 0.5, 1.0], [0.0, 1.0], [0.0, 1.0], [0.0, 0.5, 1.0]]
	end

	@testset "intersection Tests" begin
		line1 = [[0.0, 0.0], [1.0, 2.0]]
		line2 = [[2.0, 0.0], [0.0, 3.0]]
 		@test Lar.intersection(line1,line2)==(0.8571428571428571, 0.5714285714285714)
		line1 = [[0.0, 0.0], [-2.0, 0.0]]
		line2 = [[0.0, 0.0], [0.0, 3.0]]
		@test Lar.intersection(line1,line2)==(0.0, 0.0)
		line2 = [[0.0, 3.0],[0.0, 0.0]]
		@test Lar.intersection(line1,line2)==(0.0, 1.0)
		line1 = [[-2.0, 0.0],[0.0, 0.0]]
		@test Lar.intersection(line1,line2)==(1.0, 1.0)
		@test Lar.intersection(line1,line1)==nothing
	end

#	@testset "fragmentlines Tests" begin
#		V = hcat([[0.,0],[1,0],[1,1],[0,1],[2,1]]...);
#		EV = [[1,2],[2,3],[3,4],[4,1],[1,5]];
#		W,EW = Lar.fragmentlines((V,EV))
#		@test W == [0.0  1.0  1.0  1.0  0.0  2.0; 0.0  0.0  0.5  1.0  1.0  1.0]
#		@test EW == [[1, 2],[2, 3],[3, 4],[4, 5],[5, 1],[1, 3],[3, 6]]
#	end
end

@testset "Refactoring pipeline 2" begin

	@testset "bbbbbbb Tests" begin
		@test true
	end

	@testset "bbbbbbb Tests" begin
		@test true
	end

	@testset "bbbbbbb Tests" begin
		@test true
	end
end


text:
# struct Header
#     version           ::UInt32
#     previous_hash     ::UInt256 #Vector{UInt8} # length 32
#     merkle_root       ::UInt256 #Vector{UInt8} # length 32
#     timestamp         ::UInt32
#     difficulty_target ::UInt32
#     nounce            ::UInt32
# end
#
# The following implementation hopefully is
# more efficient, because no conversions at reading time have to be made:
"""
    Header

Data Structure representing the Header of a Block in the Bitcoin blockchain.

The elements of the `Header` can be accessed by `header.element`.

```julia
header.version
header.previous_hash
header.merkle_root
header.timestamp
header.difficulty_target
header.nounce
```

The hash of the `Header` can be retrieved with
```julia
double_sha256(header)
```
"""
struct Header
    data :: NTuple{80, UInt8}
end

Header(x::IO) = Header(ntuple((i) -> read(x, UInt8), 80))

function Header(bcio::BCIterator)

    check_magic_bytes(bcio)

    block_size = read(bcio, UInt32)

    block_header = Header(bcio.io)

    # @assert block_size - 80 > 0

    skip(bcio, block_size - 80)

    if eof(bcio)
        open_next_file(bcio)
    end

    block_header
end

@inline Base.getindex(x::Header, r) = x.data[r]

@inline function gidx(x, ::Val{from}, ::Val{to}) where from where to
    ntuple(i -> x[i + from - 1], to - from + 1)
end

@inline function Base.getproperty(x::Header, d::Symbol)
    if     d == :version           to_unsigned(gidx(x, Val( 1), Val( 4)))
    elseif d == :previous_hash     to_unsigned(gidx(x, Val( 5), Val(36)))
    elseif d == :merkle_root       to_unsigned(gidx(x, Val(37), Val(68)))
    elseif d == :timestamp         to_unsigned(gidx(x, Val(69), Val(72)))
    elseif d == :difficulty_target to_unsigned(gidx(x, Val(73), Val(76)))
    elseif d == :nounce            to_unsigned(gidx(x, Val(77), Val(80)))
    else getfield(x, d)
    end
end

function Base.propertynames(::Type{Header}, private = false)
    (:version, :previous_hash, :merkle_root,
     :timestamp, :difficulty_target, :nounce,
     fieldnames(Header)...)
end

function showcompact(io::IO, header::Header)
    println(io, "Header, " * string(header.timestamp, base = 10) * ":")
end

function Base.show(io::IO, header::Header)
    showcompact(io, header)
    if !get(io, :compact, false)
        # TODO: add leading zeroes where necessary
        println(io, "  Version:    " * string(header.version,           base = 16))
        println(io, "  Prev Hash:  " * string(header.previous_hash,     base = 16))
        println(io, "  Root:       " * string(header.merkle_root,       base = 16))
        println(io, "  Time:       " * string(header.timestamp,         base = 10))
        println(io, "  Difficulty: " * string(header.difficulty_target, base = 16))
        println(io, "  Nounce:     " * string(header.nounce,            base = 10))
    end
end
# Base.showall(io::IO, header::Header) = show(io, header)

function double_sha256(x::Header)::UInt256
    x.data |> sha256 |> sha256 |>
        x -> reinterpret(UInt256, x)[1]
end

function Header(fp::FilePointer)
    fp.file_number |>
        BTCParser.get_block_chain_file_path |>
        x -> open(x) do fh
            seek(fh, fp.file_position)
            Header(fh)
        end
end


text:
using MPI
using Oceananigans
using Oceananigans.Distributed

MPI.Init()

comm = MPI.COMM_WORLD
rank = MPI.Comm_rank(comm)
Nranks = MPI.Comm_size(MPI.COMM_WORLD)

# Setup model
topology = (Periodic, Periodic, Flat)
arch = MultiArch(CPU(); topology, ranks=(1, Nranks, 1))
grid = RectilinearGrid(arch; topology, size=(16, 16), extent=(2π, 2π))
c = CenterField(grid)

f(x, y, z) = rand()
set!(c, f)
cmax = maximum(c)
@info "(function) rank $rank has max|c|: $cmax"

a = rand(size(c)...)
set!(c, a)
cmax = maximum(c)
@info "(array) rank $rank has max|c|: $cmax"


text:
module DOIHS

using ValidatedNumerics
using FastGaussQuadrature
using Distributions
using RecipesBase
using Parameters
using Distributions
using Lazy
using NLsolve
using Plots

import Base: show, size, linspace, \, *, zeros, ones

include("misc.jl")
include("basis.jl")
include("quadrature.jl")
include("iteration.jl")
include("discrete_dynprog.jl")
include("general_dynprog.jl")

end # module


text:
include("kn_tools.jl")
######################
######################
######################
######################
######################
######################
######################
######################
######################
######################
######################
######################
@doc """
    KNSingleFactorEstimator(data::Array{Float64,2}, dfm::DFMStruct)

Description:
Estimate a single-factor DFM using the Kim-Nelson approach.

Inputs:
- data = Matrix with each column being a data series.
- dfm = Model structure specification.

Outputs:
- results = HDMF Bayesian estimator-generated MCMC posterior distribution samples and their means for latent factors and hyperparameters.
"""
function KN1LevelEstimator(data::Array{Float64,2}, dfm::DFMStruct)

    # Unpack simulation parameters
    @unpack factorlags, errorlags, ndraws, burnin = dfm

    # Save total number of Monte Carlo draws
    totdraws = ndraws + burnin

    # Store data as separate object
    y = data

    # nvar = number of variables including the variable with missing date
    # nobs = length of data of complete dataset
    nobs, nvar = size(y)

    # Number of regressors in each observable equation
    # (constant + global factor)
    nreg = 2

    # De-mean data series
    y = y - repeat(mean(y, dims = 1), nobs, 1)

    # Set up some matricies for storage (optional)
    Xtsave = zeros(nobs, totdraws)                  # just keep draw of factor, not all states (others are trivial)
    bsave = zeros(totdraws, nreg * nvar)           # observable equation regression coefficients
    ssave = zeros(totdraws, nvar)                  # innovation variances
    psave = zeros(totdraws, 1 + factorlags)            # factor autoregressive polynomials
    psave2 = zeros(totdraws, nvar * errorlags)      # factor autoregressive polynomials

    # Initialize global factor
    factor = mean(y, dims = 2)

    # Begin Monte Carlo Loop
    for dr = 1:totdraws

        println(dr)

        # Create HDFM parameter containers
        varcoefs = zeros(nvar, 2)[:, :]
        varlagcoefs = zeros(nvar, errorlags)[:, :]
        fcoefs = Any[]
        push!(fcoefs, zeros(1 + factorlags))
        fvars = Any[]
        push!(fvars, ones(1))
        varvars = zeros(nvar)

        ##################################
        ##################################
        # Draw β, σ2, ϕ

        ## Gather all regressors into `X`
        X = [ones(nobs) factor]

        ## Initialize β, σ2, ϕ
        β = ones(2)
        σ2 = 0
        ϕ = zeros(errorlags)

        ## Iterate over all data series
        ## to draw obs. eq. hyperparameters
        for i = 1:nvar

            ## Save i-th series
            Y = y[:, i]

            if i == 1
                ind = 0
                β, σ2, ϕ = autocorrErrorLinearRegressionSampler(Y, X, errorlags)
                while β[2] < 0
                    ind += 1
                    ## Draw observation eq. hyperparameters
                    β, σ2, ϕ = autocorrErrorLinearRegressionSampler(Y, X, errorlags)
                    if ind >= 100
                        factor = -factor
                        X = [ones(nobs) factor]
                    end
                end
            else
                β, σ2, ϕ = autocorrErrorLinearRegressionSampler(Y, X, errorlags)
            end

            ## Fill out HDFM objects
            varcoefs[i, :] = β'
            varvars[i] = σ2
            varlagcoefs[i, :] = ϕ'

            ## Save observation eq. hyperparameter draws
            bsave[dr, ((i-1)*nreg)+1:i*nreg] = β'
            ssave[dr, i] = σ2
            psave2[dr, ((i-1)*errorlags)+1:i*errorlags] = ϕ'

        end

        ##################################
        ##################################
        # Draw factor lag coefficients

        ## Create factor regressor matrix
        X = zeros(nobs, 1 + factorlags)
        X[:, 1] = ones(nobs)
        for j in 1:factorlags
            X[:, 1+j] = lag(factor, j, default = 0.0)
        end
        X = X[(factorlags+1):nobs, :]

        ## Draw ψ
        ψ = linearRegressionSamplerRestrictedVariance(factor[(factorlags+1):nobs], X, 1.0)

        ## Fill out HDFM objects
        fcoefs = ψ

        ## Save new draw of ψ
        psave[dr, :] = ψ'

        ##################################
        ##################################
        # Draw factor

        #draw factor
        #take drawing of World factor
        sinvf1 = sigbig(ψ[2:end], factorlags, nobs)
        f = zeros(nobs, 1)
        H = sinvf1' * sinvf1

        for i = 1:nvar
            sinv1 = sigbig(vec(varlagcoefs[i, :]), errorlags, nobs)
            H = H + ((varcoefs[i, 2]^2 / varvars[i]) * sinv1' * sinv1)
            f = f + (varcoefs[i, 2] / varvars[i]) * sinv1' * sinv1 * (y[:, i])
        end

        Hinv = inv(H)
        f = Hinv * f
        factor = sim_MvNormal(vec(f), Hinv)

        ## Save factor
        Xtsave[:, dr] = factor

        println(dr)
    end

    # Save resulting samples
    Xtsave = Xtsave[:, (burnin)+1:(burnin+ndraws)]
    bsave = bsave[burnin+1:burnin+ndraws, :]
    ssave = ssave[burnin+1:burnin+ndraws, :]
    psave = psave[burnin+1:burnin+ndraws, :]
    psave2 = psave2[burnin+1:burnin+ndraws, :]

    # Save resulting sample means
    F = mean(Xtsave, dims = 2)
    B = mean(bsave, dims = 1)
    S = mean(ssave, dims = 1)
    P = mean(psave, dims = 1)
    P2 = mean(psave2, dims = 1)
    means = DFMMeans(F, B, S, P, P2)

    # Gather all results
    results = DFMResults(Xtsave, bsave, ssave, psave, psave2, means)

    ##################################
    ##################################
    # Return results as single object
    return results
end;
######################
######################
######################
######################
######################
######################
######################
######################
######################
######################
######################
######################


text:
using DrWatson
@quickactivate "StatReth"

# %%
# Bayes: Pr(Earth|Land) = Pr(Land|Earth) * Pr(Earth) / Pr(Land)

p_land_earth = 1 - 0.7
p_land_mars = 1.0
p_earth = 0.5
p_land = p_land_earth * p_earth + p_land_mars * (1 - p_earth)

p_earth_land = p_land_earth * p_earth / p_land


text:
module Day10

using ..ReTest, ..DocStringExtensions

import ..solve, ..PuzzleInfo, ..TEST_DATA_DIRECTORY

abstract type Day10PuzzleInfo <: PuzzleInfo end

"""
Day 10 part 1 puzzle information.

$(FIELDS)
"""
struct Part1PuzzleInfo <: Day10PuzzleInfo
    """Path to input data file."""
    datapath::String
end

"""
Day 10 part 2 puzzle information.

$(FIELDS)
"""
struct Part2PuzzleInfo <: Day10PuzzleInfo
    """Path to input data file."""
    datapath::String
end

"""Map from opening character to corresponding closing character."""
const openertoclosermap = Base.ImmutableDict(
    '(' => ')', '[' => ']', '{' => '}', '<' => '>'
)

"""Map from closing character to syntax error points when an illegal character."""
const illegalcharacterpoints = Base.ImmutableDict(
    ')' => 3,
    ']' => 57,
    '}' => 1197,
    '>' => 25137,
)

"""
    $(FUNCTIONNAME)(closers)

Score a sequence of closing characters `closers` needed to autocomplete a line.
"""
function scoreclosers(closers::Array{Char})
    closerpoints = Dict{Char, Int}(')' => 1, ']' => 2, '}' => 3, '>' => 4)
    score = 0
    while !isempty(closers)
        score = (score * 5) + closerpoints[pop!(closers)]
    end
    score
end

"""
    $(FUNCTIONNAME)(line)

Get the sequence of expected closing characters for `line`, if it is corrupted and
first illegal character if so.
"""
function expectedclosersandifcorrupted(line)
    expectedclosers::Array{Char} = []
    lineiscorrupted = false
    firstillegalchar = undef
    for c in line
        if haskey(openertoclosermap, c)
            push!(expectedclosers, openertoclosermap[c])
        elseif length(expectedclosers) == 0 || pop!(expectedclosers) != c
            lineiscorrupted = true
            firstillegalchar = c
            break
        end
    end
    expectedclosers, lineiscorrupted, firstillegalchar
end

function solve(info::Part1PuzzleInfo)
    errorscore = 0
    firstillegalchar = undef
    open(info.datapath) do input
        for line in eachline(input)
            _, lineiscorrupted, firstillegalchar = expectedclosersandifcorrupted(line)
            if lineiscorrupted
                errorscore += illegalcharacterpoints[firstillegalchar]
            end
        end
    end
    errorscore
end

function solve(info::Part2PuzzleInfo)
    autocompleterscores::Array{Int} = []
    open(info.datapath) do input
        for line in eachline(input)
            expectedclosers, lineiscorrupted, _ = expectedclosersandifcorrupted(line)
            if !lineiscorrupted
                push!(autocompleterscores, scoreclosers(expectedclosers))
            end
        end
    end
    @assert isodd(length(autocompleterscores)) "Expected odd number of scores"
    sort(autocompleterscores)[(length(autocompleterscores) + 1) ÷ 2]
end

@testset begin
    @test solve(Part1PuzzleInfo(joinpath(TEST_DATA_DIRECTORY, "day10.txt"))) == 26397
    @test solve(Part2PuzzleInfo(joinpath(TEST_DATA_DIRECTORY, "day10.txt"))) == 288957
end

end # module


text:
@testset "greaterorqual.jl" begin
    @testset "GreaterOrEqualConstant()" begin
        trailer = SeaPearl.Trailer()
        x = SeaPearl.IntVar(2, 6, "x", trailer)

        constraint = SeaPearl.GreaterOrEqualConstant(x, 3, trailer)

        @test constraint in x.onDomainChange
        @test constraint.active.value
    end
    @testset "propagate!(::GreaterOrEqualConstant)" begin
        trailer = SeaPearl.Trailer()
        x = SeaPearl.IntVar(2, 6, "x", trailer)

        constraint = SeaPearl.GreaterOrEqualConstant(x, 5, trailer)

        toPropagate = Set{SeaPearl.Constraint}()
        prunedDomains = SeaPearl.CPModification()

        @test SeaPearl.propagate!(constraint, toPropagate, prunedDomains)

        @test length(x.domain) == 2
        @test 5 in x.domain
        @test !(4 in x.domain)
        @test prunedDomains == SeaPearl.CPModification("x" => [2, 3, 4])

        y = SeaPearl.IntVar(2, 2, "y", trailer)

        cons2 = SeaPearl.GreaterOrEqualConstant(y, 3, trailer)

        @test !SeaPearl.propagate!(cons2, toPropagate, prunedDomains)

        @test isempty(y.domain)

    end
end


text:
using Test

using LinearAlgebra, Statistics
using LowRankVortex
using TransportBasedInference
using ForwardDiff
using PotentialFlow
import PotentialFlow.Plates: Plate, Points, Blobs
import PotentialFlow.Motions: reset_velocity!
import PotentialFlow.Points
import PotentialFlow.Elements
import PotentialFlow.Properties: @property
import PotentialFlow.Elements: jacobian_position, jacobian_strength, jacobian_param

include("vortex.jl")
include("forecast.jl")
include("convective_complexpotential.jl")
include("pressure.jl")
include("symmetric_pressure.jl")
include("symmetric_analytical_jacobian_pressure_freestream.jl")
include("symmetric_analytical_jacobian_pressure.jl")
include("AD_pressure.jl")
include("analytical_pressure.jl")


text:
using Bukdu # Plug ApplicationController Conn plug pipeline routes get
using HTTP.Messages: setheader

struct CORS <: Plug.AbstractPlug
end

function plug(::Type{CORS}, conn::Conn, origin::String)
    setheader(conn.request.response, "Access-Control-Allow-Origin" => origin)
end

struct RESTController <: ApplicationController
    conn::Conn
end

function index(c::RESTController)
    @info c.conn.request.response.headers
    render(JSON, 42)
end

pipeline(:rest) do conn::Conn
    plug(CORS, conn, "*")
end

routes(:rest) do
    get("/", RESTController, index)
end

Bukdu.start(8080)


text:
using ChebyshevNodes
using Test

@test intoab(5,10,fromab(5,10,8))  == 8
@test intoab(5,10,fromab(5,10,5))  == 5
@test fromab(5,10,intoab(5,10,8))  == 8
@test fromab(5,10,intoab(5,10,10)) == 10


text:
function get_updated_versions_from_test(pkg_ctx::Context, test_ctx::Context)
    pkg_project = deepcopy(pkg_ctx.env.project)
    test_project = deepcopy(test_ctx.env.project)
    test_manifest = deepcopy(test_ctx.env.manifest)
    # abspath! to maintain location of all deved nodes except package
    abspath!(test_ctx, test_manifest)

    lowerbound!(pkg_project)
    lowerbound!(test_project)

    updated_versions = Dict{UUID,VersionNumber}()
    mktempdir() do tmp_pkg_dir
        # dummy project
        tmp_test_dir = mkdir(joinpath(tmp_pkg_dir, "test"))
        mkdir(joinpath(tmp_pkg_dir, "src"))
        touch(joinpath(tmp_pkg_dir, "src/$(pkg_project.name).jl"))

        write_project(pkg_project, projectfile_path(tmp_pkg_dir))
        write_project(test_project, projectfile_path(tmp_test_dir))
        write_manifest(test_manifest, manifestfile_path(tmp_test_dir))
        with_temp_env(tmp_test_dir) do
            # point sandbox_manifest to look for the project in tmp_pkg_dir
            Pkg.develop(PackageSpec(uuid=pkg_project.uuid, path=".."))
            Pkg.update(update_registry = false)
            for (uuid, entry) in Context().env.manifest
                if entry.version !== nothing
                    updated_versions[uuid] = entry.version
                end
            end
        end
    end

    return updated_versions
end

function get_updated_versions(ctx::Context)
    project = deepcopy(ctx.env.project)
    lowerbound!(project)

    updated_versions = Dict{UUID,VersionNumber}()
    with_sandbox(ctx) do tmp
        write_project(project, projectfile_path(tmp))
        Pkg.update(update_registry = false)
        for (uuid, entry) in Context().env.manifest
            if entry.version !== nothing
                updated_versions[uuid] = entry.version
            end
        end
    end

    return updated_versions
end

# Prevent downgrading any deps below the lower bound of their current compat entry
# by replacing each compat entry with an inequality specifier
# e.g "0.2.1, 0.3, 0.5" --> ">= 0.2.1"
function lowerbound!(project::Project)
    for (name::String, uuid::UUID) in project.deps
        if haskey(project.compat, name)
            project.compat[name] = format_lowerbound_compat(semver_spec(project.compat[name]))
        end
    end
    return project
end


text:
using Statistics, Test, MusicManipulations

@testset "Velocity timeseries" begin

    # test object: onbeat notes with velocity ascending from 0 to 159
    notes = Notes{MoreVelNote}(Vector{MoreVelNote}(), 960)
    for i = 0:159
        push!(notes.notes, MoreVelNote(1, i, 960*i, 2))
    end

    #simple time series
    tvec, tseries = timeseries(notes, :velocity, maximum, 0:1)
    @test tvec == collect(0:960:159*960)
    @test tseries == collect(0:159)

    # time series for eights with missing values
    grid = 0:1//2:1
    tvec, tseries = timeseries(notes, :velocity, maximum, grid)

    @test tvec == collect(0:480:159*960+480)
    @test [tseries[i] for i in 1:2:159*2+1] == collect(Float64, 0:159)
    @test count(ismissing, [tseries[i] for i in 2:2:159*2+2]) == 160
    @test count(x -> x isa Float64, [tseries[i] for i in 2:2:159*2+2]) == 0

    # add notes which have been quantized to same bin
    for i = 0:159
        push!(notes.notes, MoreVelNote(1, i+1, 960*i, 2))
    end
    sort!(notes.notes, lt=((x, y)->x.position<y.position))

    # simple time series choosing maximum in bin
    tvec, tseries = timeseries(notes, :velocity, maximum, 0:1)

    @test tvec == collect(Float64, 0:960:159*960)
    @test tseries == collect(Float64, 1:160)

    # simple time series choosing mean value in bin
    tvec, tseries = timeseries(notes, :velocity, mean, 0:1)

    @test tvec == collect(Float64, 0:960:159*960)
    @test tseries == collect(Float64, 1:160) .- 0.5

    # time series for eights with missing and average for bins
    grid = 0:1//2:1
    tvec, tseries = timeseries(notes, :velocity, mean, grid)

    @test tvec == collect(Float64, 0:480:159*960+480)
    @test [tseries[i] for i in 1:2:159*2+1] == collect(Float64, 0:159) .+ 0.5
    @test count(ismissing, [tseries[i] for i in 2:2:159*2+2]) == 160
    @test count(x -> x isa Float64, [tseries[i] for i in 2:2:159*2+2]) == 0

    tvec, tseries = timeseries(notes, :velocity, mean, grid; missingval = 0.0)
    @test tseries isa Vector{Float64}
    @test count(iszero, [tseries[i] for i in 2:2:159*2+2]) == 160

end

@testset "Pitch timeseries" begin
    # test object: onbeat (quarter) notes with pitch ascending
    notes = Notes{Note}(Vector{Note}(), 960)
    vals = 10:20
    L = length(vals)
    for (i, val) in enumerate(vals)
        push!(notes.notes, Note(val, 5, 960*(i-1), 2))
    end

    #simple time series
    tvec, tseries = timeseries(notes, :pitch, maximum, 0:1)
    @test tvec == collect(0:960:(L-1)*960)
    @test tseries == collect(vals)

    # add notes which have been quantized to same bin
    for  (i, val) in enumerate(vals)
        push!(notes.notes, Note(5, 5, 960*(i-1), 2))
    end
    sort!(notes.notes, lt=((x, y)->x.position<y.position))

    # time series for eights with missing and average for bins
    grid = 0:1//2:1
    tvec, tseries = timeseries(notes, :pitch, mean, grid)

    @test tvec == collect(0:480:(L)*960-480)
    @test [tseries[i] for i in 1:2:(L-1)*2+1] == (collect(Float64, vals) .+ 5)./2
    @test count(ismissing, [tseries[i] for i in 2:2:(L-1)*2+2]) == L
    @test count(x -> x isa Float64, [tseries[i] for i in 2:2:(L-1)*2+2]) == 0
end

midipath = dirname(dirname(pathof(MIDI)))*"/test/"
midis = [readMIDIFile(joinpath(@__DIR__, "serenade_full.mid")),
readMIDIFile(joinpath(midipath, "doxy.mid"))
]

@testset "MTD timeseries $i" for i in 1:2

    midi = midis[i]
    piano = getnotes(midi, 4)
    triplets = 0:1//3:1

    tvec, mtd = timeseries(piano, :position, mean, triplets)

    @test tvec[end] ≥ piano[end].position

    lastnote = findlast(x -> x isa Float64, mtd)
    @test tvec[lastnote] + mtd[lastnote] ≤ Int(piano[end].position)

    maxmtd = 1//6 * piano.tpq

    @test maximum(skipmissing(mtd)) ≤ maxmtd
    @test minimum(skipmissing(mtd)) ≥ -maxmtd
    @test any(x -> x > 0, skipmissing(mtd))
    @test any(x -> x < 0, skipmissing(mtd))
    @test count(!ismissing, mtd) + count(ismissing, mtd) == length(mtd)

    # maximum mtd:
    tvec, mtd = timeseries(piano, :position, maximum, triplets)
    lastnote = findlast(x -> x isa Float64, mtd)
    @test tvec[lastnote] + mtd[lastnote] == Int(piano[end].position)

end

@testset "max pitch velocity timeseries $i" for i in 1:2
    midi = midis[i]

    notes = getnotes(midi, 4)

    function f(notes)
        m, i = findmax(pitches(notes))
        notes[i].velocity
    end

    grid = 0:1//3:1
    tvec1, ts1 = timeseries(notes, :velocity, mean, grid)
    tvec2, ts2 = timeseries(notes, f, grid)

    @test findall(ismissing, ts1) == findall(ismissing, ts2)
    @test tvec1 == tvec2
    @test ts1 != ts2
end

@testset "Segmentation" begin
    #test of segmenting functionality
    notes_to_segment = Notes()
    push!(notes_to_segment,Note(67,70,15,540))
    push!(notes_to_segment,Note(70,75,330,125))
    push!(notes_to_segment,Note(60,73,610,829))
    tvec, ts = timeseries(notes_to_segment, :pitch, maximum, 0:1//6:1; segmented = true)
    ts[findall(ismissing,ts)] .= 0 # replace missing with zeros
    @test ts == [67.0, 67.0, 70.0, 0.0, 60.0, 60.0, 60.0, 60.0, 60.0, 0.0, 0.0, 0.0]

    dumnotes = Notes([Note(5, 5, 0, 960)])
    snotes = segment(dumnotes, 0:1//4:1)
    @test all(n -> n.duration == 240, snotes)
    @test all(n -> n.velocity == 5, snotes)
    @test length(snotes) == 4
end


text:
using StochasticGroundMotionSimulation
using Documenter

makedocs(;
    modules=[StochasticGroundMotionSimulation],
    authors="Peter Stafford <p.stafford@me.com>",
    # repo="https://github.com/pstafford/StochasticGroundMotionSimulation.jl/blob/{commit}{path}#L{line}",
    sitename="StochasticGroundMotionSimulation.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://pstafford.github.io/StochasticGroundMotionSimulation.jl",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
        "Model Components" => [
            "Fourier Spectral Parameters" => "fourier_parameters.md",
            "Oscillator Parameters" => "sdof_parameters.md",
            "Random Vibration Parameters" => "random_vibration_parameters.md",
        ],
        "Module Functionality" => [
            "Fourier Components" => [
                "Fourier Amplitude Spectrum" => "fourier_spectrum.md",
                "Source spectrum" => "source_spectrum.md",
                "Path scaling" => "path_scaling.md",
                "Site scaling" => "site_scaling.md",
            ],
            "Method Index" => "method_index.md"
        ]
    ],
)

deploydocs(;
    repo="github.com/pstafford/StochasticGroundMotionSimulation.jl.git",
)


text:
using PyCall
@pyimport modeller

env = modeller.environ()
aln = modeller.alignment(env)
mdl = modeller.model(env, file="1bdm", model_segment=("FIRST:A","LAST:A"))
aln[:append_model](mdl, align_codes="1bdmA", atom_files="1bdm.pdb")
aln[:append](file="TvLDH.ali", align_codes="TvLDH")
aln[:align2d]()
aln[:write](file="TvLDH-1bdmA.ali", alignment_format="PIR")
aln[:write](file="TvLDH-1bdmA.pap", alignment_format="PAP")


text:
using Cambrian
import YAML

test_filename = string(@__DIR__, "/test.yaml")
cfg = get_config(test_filename)

evaluate(e::Evolution) = random_evaluate(e)
populate(e::Evolution) = oneplus_populate(e)

function test_evolution(e::Evolution)
    @test length(e.population) == cfg.n_population
    for i in e.population
        @test all(i.fitness .== -Inf)
    end

    evaluate(e)
    fits = [i.fitness[1] for i in e.population]
    evaluate(e)
    # random fitness, all values should change
    for i in eachindex(e.population)
        @test fits[i] != e.population[i].fitness[1]
    end

    fits = copy([i.fitness[:] for i in e.population])
    step!(e)
    # random fitness, all values should change
    for i in eachindex(e.population)
        @test fits[i] != e.population[i].fitness[1]
    end
end

@testset "Random fitness" begin
    e = Evolution{FloatIndividual}(cfg)
    test_evolution(e)
end

struct MyFloatIndividual <: Individual
    genes::Array{Float64}
    fitness::Array{Float64}
    my_var::Int64
end

function my_init_function(cfg::NamedTuple)
    MyFloatIndividual(rand(cfg.n_genes), -Inf*ones(cfg.d_fitness), 33)
end

@testset "Evolution with custom init function" begin
    e = Evolution{MyFloatIndividual}(cfg; init_function=my_init_function)
    test_evolution(e)
end


text:
using HypothesisTests, UncertainData, Distributions

# Single uncertain values
uval1 = UncertainValue(Normal, 0, 0.2)
uval2 = UncertainValue(Beta, 1, 2)

xs = [UncertainValue(Normal, 0, rand()) for i = 1:10]
ys = [UncertainValue(Gamma, rand(Uniform(2, 3)), rand(Uniform(4, 5))) for i = 1:10]

x = UncertainDataset(xs)
y = UncertainDataset(ys)

#######################
# On uncertain values
######################
@test pvalue(MannWhitneyUTest(uval1, uval2)) isa Float64
@test pvalue(MannWhitneyUTest(uval1, uval2, 10)) isa Float64

@test pvalue(OneSampleTTest(uval1)) isa Float64
@test pvalue(OneSampleTTest(uval1, μ0 = 0.0)) isa Float64
@test pvalue(OneSampleTTest(uval1, uval2)) isa Float64
@test pvalue(OneSampleTTest(uval1, uval2, μ0 = 0.0)) isa Float64

@test pvalue(OneSampleTTest(uval1, 10)) isa Float64
@test pvalue(OneSampleTTest(uval1, 10)) isa Float64
@test pvalue(OneSampleTTest(uval1, uval2, 10)) isa Float64
@test pvalue(OneSampleTTest(uval1, uval2, 10, μ0 = 0.0)) isa Float64

@test pvalue(EqualVarianceTTest(uval1, uval2)) isa Float64
@test pvalue(EqualVarianceTTest(uval1, uval2, 10)) isa Float64
@test pvalue(EqualVarianceTTest(uval1, uval2, μ0 = 0.0)) isa Float64
@test pvalue(EqualVarianceTTest(uval1, uval2, 10, μ0 = 0.0)) isa Float64

@test pvalue(UnequalVarianceTTest(uval1, uval2)) isa Float64
@test pvalue(UnequalVarianceTTest(uval1, uval2, 10)) isa Float64
@test pvalue(UnequalVarianceTTest(uval1, uval2, μ0 = 0.0)) isa Float64
@test pvalue(UnequalVarianceTTest(uval1, uval2, 10, μ0 = 0.0)) isa Float64

@test pvalue(ApproximateTwoSampleKSTest(uval1, uval2)) isa Float64
@test pvalue(ApproximateTwoSampleKSTest(uval1, uval2, 10)) isa Float64

@test pvalue(ExactOneSampleKSTest(uval1, Normal())) isa Float64
@test pvalue(OneSampleADTest(uval1, Normal())) isa Float64

#######################
# On uncertain datasets
######################
@test pvalue(MannWhitneyUTestPooled(x, y)) isa Float64
@test pvalue(MannWhitneyUTestPooled(x, y, 10)) isa Float64
@test pvalue.(MannWhitneyUTestElementWise(x, y)) isa Vector{Float64}
@test pvalue.(MannWhitneyUTestElementWise(x, y, 10)) isa Vector{Float64}

@test pvalue(OneSampleTTestPooled(x)) isa Float64
@test pvalue(OneSampleTTestPooled(x, y)) isa Float64
@test pvalue(OneSampleTTestPooled(x, y, 10)) isa Float64
@test pvalue.(OneSampleTTestElementWise(x, y)) isa Vector{Float64}
@test pvalue.(OneSampleTTestElementWise(x, y, 10)) isa Vector{Float64}
@test pvalue(OneSampleTTestPooled(x, μ0 = 0.0)) isa Float64
@test pvalue(OneSampleTTestPooled(x, y, μ0 = 0.0)) isa Float64
@test pvalue(OneSampleTTestPooled(x, y, 10, μ0 = 0.0)) isa Float64
@test pvalue.(OneSampleTTestElementWise(x, y, μ0 = 0.0)) isa Vector{Float64}
@test pvalue.(OneSampleTTestElementWise(x, y, 10, μ0 = 0.0)) isa Vector{Float64}

@test pvalue(EqualVarianceTTestPooled(x, y)) isa Float64
@test pvalue(EqualVarianceTTestPooled(x, y, 10)) isa Float64
@test pvalue.(EqualVarianceTTestElementWise(x, y)) isa Vector{Float64}
@test pvalue.(EqualVarianceTTestElementWise(x, y, 10)) isa Vector{Float64}

@test pvalue(UnequalVarianceTTestPooled(x, y)) isa Float64
@test pvalue(UnequalVarianceTTestPooled(x, y, 10)) isa Float64
@test pvalue.(UnequalVarianceTTestElementWise(x, y)) isa Vector{Float64}
@test pvalue.(UnequalVarianceTTestElementWise(x, y, 10)) isa Vector{Float64}

@test pvalue(ApproximateTwoSampleKSTestPooled(x, y)) isa Float64
@test pvalue.(ApproximateTwoSampleKSTestElementWise(x, y)) isa Vector{Float64}

@test pvalue(ExactOneSampleKSTestPooled(x, Normal())) isa Float64
@test pvalue.(ExactOneSampleKSTestElementWise(x, Normal())) isa Vector{Float64}

@test pvalue(OneSampleADTestPooled(x, Normal())) isa Float64
@test pvalue.(OneSampleADTestElementWise(x, Normal())) isa Vector{Float64}

@test pvalue(JarqueBeraTestPooled(x)) isa Float64
@test pvalue.(JarqueBeraTestElementWise(x)) isa Vector{Float64}


text:
#######################################
#
# Filtering
#
#######################################

"""
    highpass_filter(a::SSR; cutOff::Real=2, fs::Real=samplingrate(a), order::Int=3, tolerance::Real=0.01, kwargs...)

Applly a high pass filter.

A zero phase high pass filter is applied to the data using `filtfilt`.
A check is performed to ensure the filter does not affect the modulation rate.
The filter coefficents are stored in the processing field.

#### Example

```julia
a = highpass_filter(a)
# or
a = highpass_filter(a, cutOff = 1)
```

"""
function highpass_filter(a::SSR; cutOff::Real=2, fs::Real=samplingrate(a), order::Int=3, tolerance::Real=0.01, kwargs...)

    a.data, f = highpass_filter(a.data, cutOff, fs, order)

    _filter_check(f, modulationrate(a), fs, tolerance)

    _append_filter(a, f)
end

function lowpass_filter(a::SSR; cutOff::Real=150, fs::Real=samplingrate(a), order::Int=3, tolerance::Real=0.01, kwargs...)

    a.data, f = lowpass_filter(a.data, cutOff, fs, order)

    _filter_check(f, modulationrate(a), fs, tolerance)

    #= _append_filter(a, f) =#

    return a
end

function bandpass_filter(a::SSR;
                         lower::Number=modulationrate(a)-1,
                         upper::Number=modulationrate(a)+1,
                         n::Int=24, rp::Number=0.0001, tolerance::Number=0.01, kwargs...)

    # Type 1 Chebychev filter
    # The default options here are optimised for modulation frequencies 4, 10, 20, 40, 80
    # TODO filter check does not work here. Why not?
    # TODO automatic minimum filter order selection

    a.data, f = bandpass_filter(a.data, lower, upper, samplingrate(a), n, rp)

    _filter_check(f, modulationrate(a), samplingrate(a), tolerance)

    _append_filter(a, f)
end

function _filter_check(f::FilterCoefficients, mod_freq::Number, fs::Number, tolerance::Number)
    #
    # Ensure that the filter does not alter the modulation frequency greater than a set tolerance
    #

    mod_change = abs(freqz(f, mod_freq, fs))
    if mod_change > 1 + tolerance || mod_change < 1 - tolerance
        Logging.warn("Filtering has modified modulation frequency greater than set tolerance: $mod_change")
    end
    debug("Filter magnitude at modulation frequency: $(mod_change)")
end

function _append_filter(a::SSR, f::FilterCoefficients; name::AbstractString="filter")
    #
    # Put the filter information in the SSR processing structure
    #

    key_name = new_processing_key(a.processing, name)
    merge!(a.processing, Dict(key_name => f))

    return a
end

#######################################
#
# Downsample
#
#######################################

function downsample(s::SSR, ratio::Rational)

    Logging.info("Downsampling SSR by ratio $ratio")

    dec_filter  = DSP.FIRFilter([1], ratio)

    new_data = zeros(typeof(s.data[1, 1]), round(Int, size(s.data, 1)*ratio), size(s.data, 2))

    for c in 1:size(s.data, 2)

        new_data[:, c] = DSP.filt(dec_filter, vec(s.data[:, c]))
    end

    s.data = new_data

    s.triggers["Index"] = round(Int, s.triggers["Index"] .* ratio)
    if s.triggers["Index"][1] == 0
        s.triggers["Index"][1] = 1
    end

    s.samplingrate = float(samplingrate(s) * ratio) * Hertz

    return s
end

#######################################
#
# Change reference channels
#
#######################################

"""
    rereference(a::SSR, refChan::Union{AbstractString, Array{AbstractString}}; kwargs...)

Reference data to specified channel(s).

#### Example

```julia
a = rereference(a, "Cz")
# or
a = rereference(a, ["P9", "P10"])
```

"""
function rereference(a::SSR, refChan::Union{AbstractString, Array{AbstractString}}; kwargs...)

    a.data = rereference(a.data, refChan, channelnames(a))

    a.reference_channel = [refChan]

    return a
end


text:
# Copyright 2013 Peter Chronz
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

abstract OfpStruct
give_length{T<:OfpStruct}(arr::Vector{T}) = sum(map(give_length, arr))
# XXX Is this slow? Maybe it is faster to first obtain the required size, then
# allocate the array, then writing the value, iterative style.
bytes{T<:OfpStruct}(arr::Vector{T}) = [map(bytes, arr)...]
import Base.string
string{T<:OfpStruct}(arr::Vector{T}, tabs=0) = "$(map((x)->(string(x, tabs)), arr)...)"

# XXX Is there any chance we could make the header immutable again?
type OfpHeader <: OfpStruct
    protoversion::Uint8
    msgtype::Uint8
    msglen::Uint16
    msgidx::Uint32
end
OfpHeader(msgtype, msglen=0x0000, msgidx::Uint32=0x00000000) = OfpHeader(0x01,
    convert(Uint8, msgtype), convert(Uint16, msglen), convert(Uint32, msgidx))
@bytes OfpHeader
@length OfpHeader
@string OfpHeader

abstract OfpMessage <: OfpStruct

# Query for port configuration.
# ofp_queue_get_config_request
immutable OfpQueueGetConfigRequest <: OfpMessage
    header::OfpHeader
    port::Uint16 # Port to be queried. Should refer to a valid physical port
                    # (i.e. < OFPP_MAX).
    pad::Bytes # Length: 2; 32-bits alignment.
    OfpQueueGetConfigRequest(header, port) = begin
        @assert header.msgtype == OFPT_QUEUE_GET_CONFIG_REQUEST
        obj = new(header, port, zeros(Uint8, 2))
        obj.header.msglen = 12
        obj
    end
end
@length OfpQueueGetConfigRequest
@bytes OfpQueueGetConfigRequest
@string OfpQueueGetConfigRequest
@bytesconstructor OfpQueueGetConfigRequest [:pad=>2]

# Creating a new type to bundle all OfpQueueProperties.
abstract OfpQueueProp <: OfpStruct
# Common description for a queue.

immutable OfpQueuePropHeader <: OfpStruct
    property::Uint16 # One of OFPQT_*.
    len::Uint16 # Length of property, including this header.
    pad::Bytes # Length: 4; 64-bit alignment.
    OfpQueuePropHeader(prop, len) = new(prop, len, zeros(Uint8,
        4))
end
@bytes OfpQueuePropHeader
@length OfpQueuePropHeader
@string OfpQueuePropHeader
@bytesconstructor OfpQueuePropHeader [:pad=>4]

immutable OfpQueuePropNone <: OfpQueueProp
    header::OfpQueuePropHeader
    function OfpQueuePropNone(header::OfpQueuePropHeader)
        @assert header.property == OFPQT_NONE
        obj = new(header)
        @assert obj.header.len == 8
        obj
    end
end
@bytes OfpQueuePropNone
@length OfpQueuePropNone
@string OfpQueuePropNone
@bytesconstructor OfpQueuePropNone

immutable UnknownQueueProperty <: Exception
end

# XXX Why is it not allowed to create a constructor for the abstract type?
function OfpQueuePropFactory(body::Bytes)
    header::OfpQueuePropHeader = OfpQueuePropHeader(body[1:8])
    if header.property == OFPQT_MIN_RATE
        @assert length(body) == header.len
        return OfpQueuePropMinRate(body)
    elseif header.property == OFPQT_NONE
        @assert length(body) == 8
        return OfpQueuePropNone(body)
    else
        throw(UnknownQueueProperty())
    end
end
function nextqueueprop(body::Bytes)
    header::OfpQueuePropHeader = OfpQueuePropHeader(body[1:8])
    (OfpQueuePropFactory(body[1:header.len]), length(body) > header.len ?
        body[header.len + 1:end] : nothing)
end
function OfpQueueProps(body::Bytes)
    props = Array(OfpQueueProp, 0)
    bdy = body
    while bdy != nothing
        (prop::OfpQueueProp, bdy) = nextqueueprop(bdy)
        append!(props, [prop])
    end
    props
end

# Min-Rate queue property description.
immutable OfpQueuePropMinRate <: OfpQueueProp
    header::OfpQueuePropHeader # prop: OFPQT_MIN_RATE, len: 16.
    rate::Uint16 # In 1/10 of a percent; >1000 -> disabled.
    pad::Bytes # Length 6; 64-bit alginment.
    OfpQueuePropMinRate(header, rate) = begin
        @assert header.property == OFPQT_MIN_RATE
        new(header, rate, zeros(Uint8, 6))
    end
end
@length OfpQueuePropMinRate
@bytes OfpQueuePropMinRate
@string OfpQueuePropMinRate
@bytesconstructor OfpQueuePropMinRate [:pad=>6]

# TODO Consider making these kinds of types generic by parameterizing with
# abstract types used in collections as fields. In this type this would amount
# to parameterizing for OfpQueueProp. Probably parameterizing the whole type
# would be a bad idea, but at least providing conversion would be good.
# Promotion and conversion rules? Only the constructor should be parameterized,
# then there need to be conversion rules for collections of concrete types of
# OfpQueueProp. Then conversion rules will convert in the respective
# constructors.
immutable OfpPacketQueue <: OfpStruct
    queue_id::Uint32 # id for the specific queue.
    len::Uint16 # Length in bytes of this queue desc.
    pad::Bytes # Length: 2; 64-bit alignment.
    properties::Vector{OfpQueueProp} # List of properties. XXX The spec lists
        # OfpQueuePropHeader as type of properties. Probably however the list is
        # not supposed to only contain the headers but also the full properties.
    OfpPacketQueue(queue_id, len, properties::Vector{OfpQueueProp}) = begin
            obj = new(queue_id, len, zeros(Uint8, 2), properties)
            @assert len == give_length(obj)
            obj
    end
end
@length OfpPacketQueue
@bytes OfpPacketQueue
@string OfpPacketQueue
function OfpPacketQueue(body::Bytes)
    queue_id = btoui(body[1], body[2], body[3], body[4])
    len = btoui(body[5], body[6])
    OfpPacketQueue(queue_id, len, OfpQueueProps(body[9:len]))
end

immutable OfpQueueGetConfigReply <: OfpMessage
    header::OfpHeader
    port::Uint16
    pad::Bytes # Length 6;
    queues::Vector{OfpPacketQueue} # List of configured queues.
    # This one is for when we are creating this message to be sent.
    function OfpQueueGetConfigReply(port::Uint16, queues::Vector{OfpPacketQueue})
        configreply = new(OfpHeader(OFPT_GET_CONFIG_REPLY, uint16(8)), port, zeros(Uint8, 6), queues)
        len = give_length(configreply)
        configreply.header.msglen = uint16(len)
        configreply
    end
    # This one is for when we are assembling the message from bytes.
    function OfpQueueGetConfigReply(header, port, queues)
        configreply = new(header, port, zeros(Uint8, 6), queues)
        len = give_length(configreply)
        configreply.header.msglen = len
        @assert header.msgtype == OFPT_QUEUE_GET_CONFIG_REPLY
        configreply
    end
end
@bytes OfpQueueGetConfigReply
@length OfpQueueGetConfigReply
@string OfpQueueGetConfigReply
@bytesconstructor OfpQueueGetConfigReply [:pad=>6]

immutable OfpError <: OfpMessage
    header::OfpHeader
    typ::Uint16 # Should be "type", which however is a keyword in Julia.
    code::Uint16
    data::Bytes # ASCII text for OFPET_HELLO FAILED, at least 64 bytes of the
        # failed request for OFP_BAD_REQUEST CODE, OFP_BAD_ACTION_CODE,
        # OFP_FLOW_MOD_FAILED_CODE, OFP_PORT_MOD_FAILED_CODE or
        # OFPET_QUEUE_OP_FAILED.
    OfpError(header, typ, code, data) = begin
        @assert header.msgtype == OFPT_ERROR
        obj = new(header, typ, code, data)
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpError
@length OfpError
@string OfpError
@bytesconstructor OfpError
import Base.string
function string(error::OfpError)
    typestr = if error.typ == OFPET_HELLO_FAILED
        "OFPET_HELLO_FAILED"
    elseif error.typ == OFPET_BAD_REQUEST
        "OFPET_BAD_REQUEST"
    elseif error.typ == OFPET_BAD_ACTION
        "OFPET_BAD_ACTION"
    elseif error.typ == OFPET_FLOW_MOD_FAILED
        "OFPET_FLOW_MOD_FAILED"
    elseif error.typ == OFPET_PORT_MOD_FAILED
        "OFPET_PORT_MOD_FAILED"
    elseif error.typ == OFPET_QUEUE_OP_FAILED
        "OFPET_QUEUE_OP_FAILED"
    else
        throw(UnrecognizedErrorType(error))
    end
    codestr = if error.code == OFPBRC_BAD_VERSION
        "OFPBRC_BAD_VERSION"
    elseif error.code == OFPBRC_BAD_TYPE
        "OFPBRC_BAD_TYPE"
    elseif error.code == OFPBRC_BAD_STAT
        "OFPBRC_BAD_STAT"
    elseif error.code == OFPBRC_BAD_VENDOR
        "OFPBRC_BAD_VENDOR"
    elseif error.code == OFPBRC_BAD_SUBTYPE
        "OFPBRC_BAD_SUBTYPE"
    elseif error.code == OFPBRC_EPERM
        "OFPBRC_EPERM"
    elseif error.code == OFPBRC_BAD_LEN
        "OFPBRC_BAD_LEN"
    elseif error.code == OFPBRC_BUFFER_EMPTY
        "OFPBRC_BUFFER_EMPTY"
    elseif error.code == OFPBRC_BUFFER_UNKNOWN
        "OFPBRC_BUFFER_UNKNOWN"
    else
        throw(UnrecognizedErrorCode(error))
    end
    str::String = "<header: $(string(error.header)), type:
        $(typestr)($(error.typ)), code: $(codestr)($(error.code)), data:
        $(error.data)>"
    str
end
type UnrecognizedErrorType <: Exception
    error::OfpError
end
type UnrecognizedErrorCode <: Exception
    error::OfpError
end
# Description of physical port
immutable OfpPhyPort <: OfpStruct
    port_no::Uint16
    hw_addr::Bytes # length of array: OFP_MAX_ETH_ALEN
    name::Bytes # Null-terminated. Length: OFP_MAX_PORT_NAME_LEN.
    config::Uint32 # Bitmap of OFPC_* flags.
    state::Uint32 # Bitmap of OFPPS_* flags.
    # Bitmaps of OFPPF_* that describe features. All bits zeroed if unsupported
    # or unavailable.
    curr::Uint32 # Current features.
    advertised::Uint32 # Features being advertised by the port.
    supported::Uint32 # Features supported by the port.
    peer::Uint32 # Features advertised by peer.
    OfpPhyPort(port_no, hw_addr, name, config, state, curr, advertised,
        supported, peer) = begin
        @assert length(hw_addr) == OFP_MAX_ETH_ALEN
        @assert length(name) == OFP_MAX_PORT_NAME_LEN
        new(port_no, hw_addr, name, config, state, curr, advertised, supported,
            peer)
    end
end
@length OfpPhyPort
@string OfpPhyPort [:name]
@bytes OfpPhyPort
@bytesconstructor OfpPhyPort [:hw_addr=>OFP_MAX_ETH_ALEN, :name=>OFP_MAX_PORT_NAME_LEN]

immutable OfpSwitchFeatures <: OfpMessage
    header::OfpHeader
    datapath_id::Uint64 # Datapath unique ID. The lower 48-bits are for a MAC
                        # address, while the upper 16 bits are implementer-defined
    n_buffers::Uint32 # Max packets buffered at once
    n_tables::Uint8 # Number of tables supported by datapath
    pad::Bytes # Length 3; Align to 64 bits
    capabilities::Uint32 # Bitmap of supported "ofp_capabilities"
    actions::Uint32 # Bitmap of supported "ofp_action_types"
    ports::Vector{OfpPhyPort} # Port definitions. The number of ports is
                            # inferred from the length field in the header.
    OfpSwitchFeatures(header, datapath_id, n_buffers, n_tables, capabilities,
        actions, ports) = begin
        @assert header.msgtype == OFPT_FEATURES_REPLY
        obj = new(header, datapath_id, n_buffers, n_tables, zeros(Uint8, 3), capabilities, actions, ports)
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpSwitchFeatures
@length OfpSwitchFeatures
@string OfpSwitchFeatures
@bytesconstructor OfpSwitchFeatures [:pad=>3]

# Switch configuration
immutable OfpSwitchConfig <: OfpMessage
    header::OfpHeader
    flags::Uint16 # OFPC_* flags.
    miss_send_len::Uint16 # Max bytes of new flow that datapath should send to
                            # the controller
    OfpSwitchConfig(header, flags, miss_send_len) = begin
        @assert header.msgtype == OFPT_GET_CONFIG_REQUEST || header.msgtype ==
            OFPT_GET_CONFIG_REPLY
        obj = new(header, flags, miss_send_len)
        obj.header.msglen = 12
        obj
    end
end
@bytes OfpSwitchConfig
@length OfpSwitchConfig
@string OfpSwitchConfig
@bytesconstructor OfpSwitchConfig

# Flow match structures
immutable OfpMatch <: OfpStruct
    wildcards::Uint32 # Wildcards fields
    in_port::Uint16 # Input switch port.
    dl_src::Bytes # Length: OFP_MAX_ETH_ALEN; Ethernet source address.
    dl_dst::Bytes # Length: OFP_MAX_ETH_ALEN; Ehternet destination address.
    dl_vlan::Uint16 # Input VLAN id.
    dl_vlan_pcp::Uint8 # Input VLAN priority.
    pad1::Uint8 # Align to 64 bits.
    dl_type::Uint16 # Ethernet frame type.
    nw_tos::Uint8 # IP ToS (actually DSCP field, 6 bits).
    nw_proto::Uint8 # IP protocol or lower 8 bits of ARP opcode.
    pad2::Bytes # Length: 2; Algin to 64 bits.
    nw_src::Uint32 # IP source address.
    nw_dst::Uint32 # IP destination address.
    tp_src::Uint16 # TCP/UDP source port.
    tp_dst::Uint16 # TCP/UDP destination port.
    # XXX Is it better to use type annotations on constructors, which will
    # probably lead to a not method error, which is hard to read or to create a
    # catch all constructor, which will probably throw an error about a specific
    # type mismatch (no conversion)?
    OfpMatch(wildcards, in_port, dl_src, dl_dst, dl_vlan, dl_vlan_pcp, dl_type,
        nw_tos, nw_proto, nw_src, nw_dst, tp_src, tp_dst) = begin
        @assert length(dl_src) == OFP_MAX_ETH_ALEN
        @assert length(dl_dst) == OFP_MAX_ETH_ALEN
        new(wildcards, in_port, dl_src, dl_dst, dl_vlan, dl_vlan_pcp, 0x00,
            dl_type, nw_tos, nw_proto, b"\x00\x00", nw_src, nw_dst, tp_src,
            tp_dst)
    end
end
@bytes OfpMatch
@length OfpMatch
@string OfpMatch
@bytesconstructor OfpMatch [:pad2=>2, :dl_src=>OFP_MAX_ETH_ALEN, :dl_dst=>OFP_MAX_ETH_ALEN]

# XXX Using the same naming scheme as in the spec. It is not really a header but rather
# a supertype for all actions. OfpAction would be a more suitable name.
abstract OfpActionHeader <: OfpStruct
function OfpActionHeaderFactory(body::Bytes)
    typ::Uint16 = btoui(body[1], body[2])
    if typ == OFPAT_OUTPUT
        return OfpActionOutput(body)
    elseif typ == OFPAT_ENQUEUE
        return OfpActionEnqueue(body)
    elseif typ == OFPAT_SET_VLAN_VID
        return OfpActionVlanVid(body)
    elseif typ == OFPAT_SET_VLAN_PCP
        return OfpActionVlanPcp(body)
    elseif typ == OFPAT_SET_DL_SRC || typ == OFPAT_SET_DL_DST
        return OfpActionDlAddress(body)
    elseif typ == OFPAT_SET_NW_SRC || typ == OFPAT_SET_NW_DST
        return OfpActionNwAddress(body)
    elseif typ == OFPAT_SET_NW_TOS
        return OfpActionNwTos(body)
    elseif typ == OFPAT_SET_TP_SRC || typ == OFPAT_SET_TP_DST
        return OfpActionTpPort(body)
    elseif typ == OFPAT_VENDOR
        return OfpActionVendor(body)
    elseif typ == OFPAT_STRIP_VLAN
        return OfpActionEmpty(body)
    else
        error("Unsupported action header type: $(typ)")
    end
end
function nextactionheader(body::Bytes)
    len = btoui(body[3], body[4])
    (OfpActionHeaderFactory(body[1:len]), length(body) > len ? body[len + 1:end]
        : nothing)
end
function OfpActionHeaders(body::Bytes)
    actions::Vector{OfpActionHeader} = Array(OfpActionHeader, 0)
    bdy = body
    while bdy != nothing
        (action, bdy) = nextactionheader(bdy)
        actions = [actions, action]
    end
    actions
end

immutable OfpActionEmpty <: OfpActionHeader
    typ::Uint16 # OFPAT_STRIP_VLAN.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 4.
    OfpActionEmpty(typ, len) = begin
        @assert typ == OFPAT_STRIP_VLAN
        obj = new(typ, len)
        obj.len == 4
        obj
    end
end
@bytes OfpActionEmpty
@length OfpActionEmpty
@string OfpActionEmpty
@bytesconstructor OfpActionEmpty

immutable OfpActionOutput <: OfpActionHeader
    typ::Uint16 # OFPAT_OUTPUT.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    port::Uint16 # Output port.
    max_len::Uint16 # Max length to send to the controller.
    OfpActionOutput(typ, len, port, max_len) = begin
        @assert typ == OFPAT_OUTPUT
        obj = new(typ, len, port, max_len)
        @assert obj.len == 8
        obj
    end
end
@bytes OfpActionOutput
@length OfpActionOutput
@string OfpActionOutput
@bytesconstructor OfpActionOutput

immutable OfpActionEnqueue <: OfpActionHeader
    typ::Uint16 # OFPAT_ENQUEUE.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 16.
    port::Uint16 # Port that queue belongs to. Should refer to a valid physical
        # port (i.e. < OFPP_MAX) or OFPP_IN_PORT.
    pad::Bytes # Length: 6 bytes for 64-bit alignment
    queue_id::Uint32 # Where to enqueue the packets.
    OfpActionEnqueue(typ, len, port, queue_id) = begin
        @assert typ == OFPAT_ENQUEUE
        obj = new(typ, len, port, zeros(Uint8, 6), queue_id)
        @assert obj.len == 16
        obj
    end
end
@bytes OfpActionEnqueue
@length OfpActionEnqueue
@string OfpActionEnqueue
@bytesconstructor OfpActionEnqueue [:pad=>6]

immutable OfpActionVlanVid <: OfpActionHeader
    typ::Uint16 # OFPAT_SET_VLAN_VID.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    vlan_vid::Uint16 # VLAN id.
    pad::Bytes # Length: 2 bytes for 64-bit alignment
    OfpActionVlanVid(typ, len, vlan_vid) = begin
        @assert typ == OFPAT_SET_VLAN_VID
        obj = new(typ, len, vlan_vid, zeros(Uint8, 2))
        @assert obj.len == 8
        obj
    end
end
@bytes OfpActionVlanVid
@length OfpActionVlanVid
@string OfpActionVlanVid
@bytesconstructor OfpActionVlanVid [:pad=>2]

immutable OfpActionVlanPcp <: OfpActionHeader
    typ::Uint16 # OFPAT_SET_VLAN_PCP.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    vlan_pcp::Uint8 # VLAN priority.
    pad::Bytes # Length: 3
    OfpActionVlanPcp(typ, len, vlan_pcp) = begin
        @assert typ == OFPAT_SET_VLAN_PCP
        obj = new(typ, len, vlan_pcp, zeros(Uint8, 3))
        @assert obj.len == 8
        obj
    end
end
@bytes OfpActionVlanPcp
@length OfpActionVlanPcp
@string OfpActionVlanPcp
@bytesconstructor OfpActionVlanPcp [:pad=>3]

# Exception for message constructors which rely on providing the right constant
# as message type.
type WrongMessageType <: Exception
end
# Excpetion thrown if the constructor arguments do not satisfy some invariance
# constraints.
type MessageInvarianceException <: Exception
end

# OFPAT_SET_DL_SRC/DST
immutable OfpActionDlAddress <: OfpActionHeader
    typ::Uint16 # OFPAT_SET_DL_SRC/DST.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    dl_addr::Bytes # Length: OFP_MAX_ETH_ALEN; Ethernet address.
    pad::Bytes # Length: 6
    OfpActionDlAddress(typ, len, dl_addr) = begin
        @assert typ == OFPAT_SET_DL_SRC || typ == OFPAT_SET_DL_DST
        @assert length(dl_addr) == OFP_MAX_ETH_ALEN
        obj = new(typ, len, dl_addr, zeros(Uint8, 6))
        @assert obj.len == give_length(obj)
        obj
    end
end
@bytes OfpActionDlAddress
@length OfpActionDlAddress
@string OfpActionDlAddress
@bytesconstructor OfpActionDlAddress [:dl_addr=>OFP_MAX_ETH_ALEN, :pad=>6]

# OFPAT_SET_NW_SRC/DST
immutable OfpActionNwAddress <: OfpActionHeader
    typ::Uint16 # OFPAT_SET_NW_SRC/DST.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    nw_addr::Uint32 # IP address.
    OfpActionNwAddress(typ, len, nw_addr) = begin
        @assert typ == OFPAT_SET_NW_SRC || typ == OFPAT_SET_NW_DST
        obj = new(typ, len, nw_addr)
        @assert obj.len == 8
        obj
    end
end
@bytes OfpActionNwAddress
@length OfpActionNwAddress
@string OfpActionNwAddress
@bytesconstructor OfpActionNwAddress

# OFPAT_SET_NW_TOS
immutable OfpActionNwTos <: OfpActionHeader
    typ::Uint16 # OFPAT_SET_NW_TOS.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    nw_tos::Uint8 # IP ToS (DSCP field, 6 bits).
    pad::Bytes # Length 3.
    OfpActionNwTos(typ, len, nw_tos) = begin
        @assert typ == OFPAT_SET_NW_TOS
        obj = new(typ, len, nw_tos, zeros(Uint8, 3))
        @assert obj.len == 8
        obj
    end
end
@bytes OfpActionNwTos
@length OfpActionNwTos
@string OfpActionNwTos
@bytesconstructor OfpActionNwTos [:pad=>3]

# OFPAT_TP_PORT
immutable OfpActionTpPort <: OfpActionHeader
    typ::Uint16 # OFPAT_SET_TP_SRC/DST.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    tp_port::Uint16 # TCP/UDP port.
    pad::Bytes # Length 2.
    OfpActionTpPort(typ, len, tp_port) = begin
        if typ != OFPAT_SET_TP_SRC && typ != OFPAT_SET_TP_DST
            throw(WrongMessageType())
        end
        @assert typ == OFPAT_SET_TP_SRC || typ == OFPAT_SET_TP_DST
        obj = new(typ, len, tp_port, zeros(Uint8, 2))
        @assert obj.len == 8
        obj
    end
end
@bytes OfpActionTpPort
@length OfpActionTpPort
@string OfpActionTpPort
@bytesconstructor OfpActionTpPort [:pad=>2]

# OFPAT_VENDOR
immutable OfpActionVendor <: OfpActionHeader
    typ::Uint16 # OFPAT_VENDOR.
    len::Uint16 # Length of action, including this header. This is the length of
                # action, including any padding to make it 64-bit algined. 8.
    vendor::Uint32 # Vendor ID, which takes the same form as in OfpVendorHeader.
    body::Bytes # Vendor-specific extension.
    OfpActionVendor(typ, len, vendor, body::Bytes) = begin
        @assert typ == OFPAT_VENDOR
        obj = new(OFPAT_VENDOR, len, vendor, body)
        @assert obj.len == 8 + length(body)
        obj
    end
end
@bytes OfpActionVendor
@length OfpActionVendor
@string OfpActionVendor
@bytesconstructor OfpActionVendor [] [:body=>:(len - 8)]

# Packet in messages
immutable OfpPacketIn <: OfpMessage
    header::OfpHeader
    buffer_id::Uint32 # ID assigned by datapath.
    total_len::Uint16 # Full length of frame.
    in_port::Uint16 # Port on which frame was received.
    reason::Uint8 # Reason packet is being sent (one of OFPR_*).
    pad::Uint8
    data::Bytes  # Ethernet frame, halfway through 32-bit word, so the
                           # IP header is 32-bit aligned. The amount of data is inferred from the length
                           # field in the header. Because of padding, offsetof(struct ofp_packet_in,
                           # data) == sizeof(struct ofp_packet_in) - 2.
    OfpPacketIn(header, buffer_id, total_len, in_port, reason, data) = begin
        @assert header.msgtype == OFPT_PACKET_IN
        obj = new(header, buffer_id, total_len, in_port, reason, 0x00, data)
        obj.header.msglen = 8 + 10 + length(data)
        obj
    end
end
@bytes OfpPacketIn
# perf
#@length OfpPacketIn
give_length(pi::OfpPacketIn) = 8 + 10 + length(pi.data)
@string OfpPacketIn
@bytesconstructor OfpPacketIn

# Port status messages
immutable OfpPortStatus <: OfpMessage
    header::OfpHeader
    reason::Uint8 # One of OFPPR_*
    pad::Bytes # length: 7; Align to 64 bits
    desc::OfpPhyPort
    OfpPortStatus(header, reason, desc) = begin
        @assert header.msgtype == OFPT_PORT_STATUS
        obj = new(header, reason, zeros(Uint8, 7), desc)
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpPortStatus
@length OfpPortStatus
@string OfpPortStatus
@bytesconstructor OfpPortStatus [:pad=>7]

# Modify StateMessages
# Flow setup and teardown (controller -> datapath)
immutable OfpFlowMod <: OfpMessage
    header::OfpHeader
    match::OfpMatch # Fields to match
    cookie::Uint64 # Opaque controller-issued identifier.
    # Flow actions.
    command::Uint16 # One of OFPFC_*.
    idle_timeout::Uint16 # Idle time before discarding (seconds).
    hard_timeout::Uint16 # Max time before discarding (seconds).
    priority::Uint16 # Priority level of flow entry.
    buffer_id::Uint32 # Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*.
    out_port::Uint16 # For OFPFC_DELETE* commands, require matching entries to
                        # include this as an output port. A value of OFPP_NONE indicates no
                        # restriction.
    flags::Uint16 # One of OFPFF_*.
    actions::Vector{OfpActionHeader} # The action length is inferred from the
                                        # length field in the header.
    OfpFlowMod(header, match, cookie, command, idle_timeout, hard_timeout,
        priority, buffer_id, out_port, flags, actions) = begin
            @assert header.msgtype == OFPT_FLOW_MOD
            # Create the partially initialized object.
            obj = new(header, match, cookie, command, idle_timeout, hard_timeout,
                priority, buffer_id, out_port, flags, actions)
            obj.header.msglen = give_length(obj)
            obj
    end
    OfpFlowMod(match, cookie, command, idle_timeout, hard_timeout,
        priority, buffer_id, out_port, flags, actions) = begin
        ofpflowmod = new(OfpHeader(OFPT_FLOW_MOD, uint8(8)), match, cookie,
            command, idle_timeout, hard_timeout, priority, buffer_id, out_port,
            flags, actions)
        ofpflowmod.header.msglen = give_length(ofpflowmod)
        ofpflowmod
    end
end
@bytes OfpFlowMod
@length OfpFlowMod
@string OfpFlowMod
@bytesconstructor OfpFlowMod

immutable OfpPortMod <: OfpMessage
    header::OfpHeader
    port_no::Uint16
    hw_addr::Bytes # Length: OFP_MAX_ETH_ALEN. The hardware address is not
        # configurable. This is used to sanity-check the request, so it must be the
        # same as returned in an ofp_phy_port struct.
    config::Uint32 # Bitmap of OFPPC_* flags.
    mask::Uint32 # Bitmap of OFPPC_* flags to be changed.
    advertise::Uint32 # Bitmap of "ofp_port_feature"s. Zero all bits to prevent
                        # any action taking place.
    pad::Bytes # Length 4. Pad to 64-bits.
    OfpPortMod(header, port_no, hw_addr, config, mask, advertise) = begin
        @assert header.msgtype == OFPT_PORT_MOD
        @assert length(hw_addr) == OFP_MAX_ETH_ALEN
        obj = new(header, port_no, hw_addr, config, mask, advertise, zeros(Uint8, 4))
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpPortMod
@length OfpPortMod
@string OfpPortMod
@bytesconstructor OfpPortMod [:hw_addr=>OFP_MAX_ETH_ALEN, :pad=>4]

immutable OfpEmptyMessage <: OfpMessage
    header::OfpHeader
end
@bytes OfpEmptyMessage
@length OfpEmptyMessage
@string OfpEmptyMessage
@bytesconstructor OfpEmptyMessage

type UnrecognizedMessageError <: Exception
end

# New types that will be used to bundle the body-types for
# ofp_stat_request. Furthermore it will serve for parameterizing
# OfpStatsRequest.
abstract OfpStatsRequestBody <: OfpStruct
# ofp_stat_reply. Furthermore it will serve for parameterizing
# OfpStatsReply.
abstract OfpStatsReplyBody <: OfpStruct
# Body of reply to OFPST_DESC request. Each entry is a NULL-terminated ASCII
# string.
immutable OfpDescStats <: OfpStatsReplyBody
    # Strings are zero-padded to the left with '\0'
    mfr_desc::Bytes # Length: DESC_STR_LEN; Manufacturer description.
    hw_desc::Bytes # Length: DESC_STR_LEN; Hardware description.
    sw_desc::Bytes # Length: DESC_STR_LEN; Software description.
    serial_num::Bytes # Length: SERIAL_NUM_LEN; Serial number.
    dp_desc::Bytes # Length: DESC_STR_LEN; Human readable description of
        # datapath.
    OfpDescStats(mfr_desc, hw_desc, sw_desc, serial_num, dp_desc) = begin
        @assert length(mfr_desc) == DESC_STR_LEN
        @assert length(hw_desc) == DESC_STR_LEN
        @assert length(sw_desc) == DESC_STR_LEN
        @assert length(serial_num) == SERIAL_NUM_LEN
        @assert length(dp_desc) == DESC_STR_LEN
        new(mfr_desc, hw_desc, sw_desc, serial_num, dp_desc)
    end
end
@bytes OfpDescStats
@length OfpDescStats
@string OfpDescStats [:mfr_desc, :hw_desc, :sw_desc, :serial_num, :dp_desc]
@bytesconstructor OfpDescStats [:mfr_desc=>DESC_STR_LEN, :hw_desc=>DESC_STR_LEN, :sw_desc=>DESC_STR_LEN, :serial_num=>SERIAL_NUM_LEN, :dp_desc=>DESC_STR_LEN]

# Body for ofp_stats_request for type OFPST_FLOW.
immutable OfpFlowStatsRequest <: OfpStatsRequestBody
    match::OfpMatch # Fields to match.
    table_id::Uint8 # ID of table to read (from ofp_table_stats), 0xff for all
        # tables or 0xfe for emergency.
    pad::Uint8 # Align to 32-bits.
    out_port::Uint16 # Require matching entries to include this as an output
        # port. A value of OFPP_NONE indicates no restriction.
    OfpFlowStatsRequest(match, table_id, out_port) = new(match, table_id, 0x00,
        out_port)
end
@bytes OfpFlowStatsRequest
@length OfpFlowStatsRequest
@string OfpFlowStatsRequest
@bytesconstructor OfpFlowStatsRequest

# Body of reply to OFPST_FLOW request.
immutable OfpFlowStats <: OfpStatsReplyBody
    length::Uint16 # Length of this entry.
        # avoid a naming conflict with the function "length", which can collide
        # inside the macros
    table_id::Uint8 # ID of table flow came from.
    pad::Uint8
    match::OfpMatch # Description of fields.
    duration_sec::Uint32 # Time flow has been alive in seconds.
    duration_nsec::Uint32 # Time flow has been alive in nanoseconds byeond
        # duration_sec.
    priority::Uint16 # Priority of the entry. Only meaningful when this is not
        # an exact-match entry.
    idle_timeout::Uint16 # Number of seconds idle before expiration.
    hard_timeout::Uint16 # Number of secondsbefore expiration.
    pad2::Bytes # Length: 6; Align to 64-bits.
    cookie::Uint64 # Opaque controller-issued identifier.
    packet_count::Uint64 # Number of packets in flow.
    byte_count::Uint64 # Number of bytes in flow.
    actions::Vector{OfpActionHeader} # Actions.
    OfpFlowStats(length, table_id, match, duration_sec, duration_nsec, priority,
        idle_timeout, hard_timeout, cookie, packet_count, byte_count, actions) =
        begin
            obj = new(length, table_id, 0x00, match, duration_sec,
                duration_nsec, priority, idle_timeout, hard_timeout,
                zeros(Uint8, 6), cookie, packet_count, byte_count, actions)
            @assert obj.length == give_length(obj)
            obj
        end
end
@bytes OfpFlowStats
@length OfpFlowStats
@string OfpFlowStats
@bytesconstructor OfpFlowStats [:pad2=>6]

# Body for ofp_stats_request of type OFPST_AGGREGATE
immutable OfpAggregateStatsRequest <: OfpStatsRequestBody
    match::OfpMatch # Fields to match.
    table_id::Uint8 # ID of table to read (from ofp_table_stats). 0xff for all
        # tables or 0xfe for emergency.
    pad::Uint8 # Align to 32 bits.
    out_port::Uint16 # Require matching entries to include this as an output
        # port. A value of OFPP_NONE indicates no restriction.
    OfpAggregateStatsRequest(match, table_id, out_port) = new(match, table_id,
        0x00, out_port)
end
@bytes OfpAggregateStatsRequest
@length OfpAggregateStatsRequest
@string OfpAggregateStatsRequest
@bytesconstructor OfpAggregateStatsRequest

# Body of reply to OFPST_AGGREGATE request.
immutable OfpAggregateStatsReply <: OfpStatsReplyBody
    packet_count::Uint64 # Number of packets in flows.
    byte_count::Uint64 # Number of bytes in flows.
    flow_count::Uint32 # Number of flows.
    pad::Bytes # Length: 4; Align to 64 bits.
    OfpAggregateStatsReply(packet_count, byte_count, flow_count) =
        new(packet_count, byte_count, flow_count, zeros(Uint8, 4))
end
@bytes OfpAggregateStatsReply
@length OfpAggregateStatsReply
@string OfpAggregateStatsReply
@bytesconstructor OfpAggregateStatsReply [:pad=>4]

# Body of reply to OFPST_TABLE request.
immutable OfpTableStats <: OfpStatsReplyBody
    table_id::Uint8 # Identifier of table. Lower numbered tables are consulted
        # first.
    pad::Bytes # Length: 3; Align to 32-bits.
    name::Bytes # Length: OFP_MAX_TABLE_NAME_LEN
    wildcards::Uint32 # Bitmap of OFPFW_* wildcards that are supported by the
        # table.
    max_entries::Uint32 # Max number of entries supported.
    active_count::Uint32 # Number of active entries.
    lookup_count::Uint64 # Number of packets looked up in table.
    matched_count::Uint64 # Number of packets that hit table.
    OfpTableStats(table_id, name, wildcards, max_entries, active_count,
        lookup_count, matched_count) = begin
        @assert length(name) == OFP_MAX_TABLE_NAME_LEN
        new(table_id, zeros(Uint8, 3), name, wildcards, max_entries,
            active_count, lookup_count, matched_count)
    end
end
@bytes OfpTableStats
@length OfpTableStats
@string OfpTableStats [:name]
@bytesconstructor OfpTableStats [:pad=>3, :name=>OFP_MAX_TABLE_NAME_LEN]

# Body for ofp_stats_request of type OFPST_PORT.
immutable OfpPortStatsRequest <: OfpStatsRequestBody
    port_no::Uint16 # OFPST_PORT message must request statistics either for a
        #single port (specified in port_no) or for all ports (if port_no ==
        # OFPP_NONE).
    pad::Bytes # Length: 6
    OfpPortStatsRequest(port_no) = new(port_no, zeros(Uint8, 6))
end
@bytes OfpPortStatsRequest
@length OfpPortStatsRequest
@string OfpPortStatsRequest
@bytesconstructor OfpPortStatsRequest [:pad=>6]

# Body of reply to OFPST_PORT request. If a counter is unsupported, set the
# field to all ones.
immutable OfpPortStats <: OfpStatsReplyBody
    port_no::Uint16
    pad::Bytes # Length: 6; Align to 64-bits.
    rx_packets::Uint64 # Number of received packets.
    tx_packets::Uint64 # Number of transmitted packets.
    rx_bytes::Uint64 # Number of received bytes.
    tx_bytes::Uint64 # Number of transmitted bytes.
    rx_dropped::Uint64 # Number of packets dropped by RX.
    tx_dropped::Uint64 # Number of packets dropped by TX.
    rx_errors::Uint64 # Number of receive errors. This is a super-set of more
        # specific receive errors and should be greater than or equal to the sum
        # of all rx_*_err values.
    tx_errors::Uint64 # Number of transmit errors . Thsi is a super-set of more
        # specific transmit errors and should be greater than or equal to the
        # sum of all tx_*_err values (none currently defined.)
    rx_frame_err::Uint64 # Number of frame alignment errors.
    rx_over_err::Uint64 # Number of packets with RX overrun.
    rx_crc_err::Uint64 # Number of CRC errors.
    collisions::Uint64 # Number of collisions.
    OfpPortStats(port_no, rx_packets, tx_packets, rx_bytes, tx_bytes,
        rx_dropped, tx_dropped, rx_errors, tx_errors, rx_frame_err, rx_over_err,
        rx_crc_err, collisions) = new(port_no, zeros(Uint8, 6), rx_packets,
        tx_packets, rx_bytes, tx_bytes, rx_dropped, tx_dropped, rx_errors,
        tx_errors, rx_frame_err, rx_over_err, rx_crc_err, collisions)
end
@bytes OfpPortStats
@length OfpPortStats
@string OfpPortStats
@bytesconstructor OfpPortStats [:pad=>6]

immutable OfpQueueStatsRequest <: OfpStatsRequestBody
    port_no::Uint16 # All ports if OFPP_ALL.
    pad::Bytes # Length:2; Align to 32-bits.
    queue_id::Uint32 # All queues if OFPQ_ALL.
    OfpQueueStatsRequest(port_no, queue_id) = new(port_no, zeros(Uint8, 2),
        queue_id)
end
@bytes OfpQueueStatsRequest
@length OfpQueueStatsRequest
@string OfpQueueStatsRequest
@bytesconstructor OfpQueueStatsRequest [:pad=>2]

immutable OfpQueueStats <: OfpStatsReplyBody
    port_no::Uint16
    pad::Bytes # Length: 2; Align to 32-bits.
    queue_id::Uint32 # Queue id.
    tx_bytes::Uint64 # Number of transmitted bytes.
    tx_packets::Uint64 # Number of transmitted packets.
    tx_errors::Uint64 # Number of packets dropped due to overrun.
    OfpQueueStats(port_no, queue_id, tx_bytes, tx_packets, tx_errors) =
        new(port_no, zeros(Uint8, 2), queue_id, tx_bytes, tx_packets, tx_errors)
end
@bytes OfpQueueStats
@length OfpQueueStats
@string OfpQueueStats
@bytesconstructor OfpQueueStats [:pad=>2]

immutable OfpVendorStatsRequest <: OfpStatsRequestBody
    vendor_id::Bytes # Length: 4
    body::Bytes # The rest of the message is vendor-defined.
    OfpVendorStatsRequest(vendor_id, body) = begin
        @assert length(vendor_id) == 4
        new(vendor_id, body)
    end
end
@bytes OfpVendorStatsRequest
@length OfpVendorStatsRequest
@string OfpVendorStatsRequest
@bytesconstructor OfpVendorStatsRequest [:vendor_id=>4]

immutable OfpVendorStatsReply <: OfpStatsReplyBody
    vendor_id::Bytes # Length: 4
    body::Bytes # The rest of the message is vendor-defined.
    OfpVendorStatsReply(vendor_id, body) = begin
        @assert length(vendor_id) == 4
        new(vendor_id, body)
    end
end
@bytes OfpVendorStatsReply
@length OfpVendorStatsReply
@string OfpVendorStatsReply
@bytesconstructor OfpVendorStatsReply [:vendor_id=>4]

# ofp_stats_request
immutable OfpStatsRequest{T<:OfpStatsRequestBody} <: OfpMessage
    header::OfpHeader
    typ::Uint16 # One of the OFPST_* constants.
    flags::Uint16 # OFPSF_REQ_* flags (none yet defined).
    body::T  # Body of the request.
    OfpStatsRequest(header, typ, flags, body) = begin
        obj = new(header, typ, flags, body)
        @assert header.msgtype == OFPT_STATS_REQUEST
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpStatsRequest
@length OfpStatsRequest
@string OfpStatsRequest
function OfpStatsRequest(header::OfpHeader, body::Bytes)
    @assert header.msgtype == OFPT_STATS_REQUEST
    typ::Uint16 = btoui(body[1], body[2])
    flags::Uint16 = btoui(body[3], body[4])
    if typ == OFPST_FLOW
        bdy = OfpFlowStatsRequest(body[5:end])
    elseif typ == OFPST_AGGREGATE
        bdy = OfpAggregateStatsRequest(body[5:end])
    elseif typ == OFPST_PORT
        bdy = OfpPortStatsRequest(body[5:end])
    elseif typ == OFPST_QUEUE
        bdy = OfpQueueStatsRequest(body[5:end])
    elseif typ == OFPST_VENDOR
        bdy = OfpVendorStatsRequest(body[5:end])
    else
        error("Unsupported type $(typ) for OfpStatsRequest.")
    end
    obj = OfpStatsRequest{typeof(bdy)}(header, typ, flags, bdy)
    obj.header.msglen = give_length(obj)
    obj
end

# ofp_stats_reply
immutable OfpStatsReply{T<:OfpStatsReplyBody} <: OfpMessage
    header::OfpHeader
    typ::Uint16 # One of the OFPST_* constants.
    flags::Uint16 # OFPSF_REPLY_* flags.
    body::T # Body of the reply.
    OfpStatsReply(header, typ, flags, body) = begin
        @assert header.msgtype == OFPT_STATS_REPLY
        obj = new(header, typ, flags, body)
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpStatsReply
@length OfpStatsReply
@string OfpStatsReply
function OfpStatsReply(header::OfpHeader, body::Bytes)
    typ::Uint16 = btoui(body[1], body[2])
    flags::Uint16 = btoui(body[3], body[4])
    if typ == OFPST_DESC
        bdy = OfpDescStats(body[5:end])
    elseif typ == OFPST_FLOW
        bdy = OfpStats(body[5:end])
    elseif typ == OFPST_AGGREGATE
        bdy = OfpAggregateStatsReply(body[5:end])
    elseif typ == OFPST_TABLE
        bdy = OfpTableStats(body[5:end])
    elseif typ == OFPST_PORT
        bdy = OfpPortStats(body[5:end])
    elseif typ == OFPST_QUEUE
        bdy = OfpQueueStats(body[5:end])
    elseif typ == OFPST_VENDOR
        bdy = OfpVendorStatsReply(body[5:end])
    else
        error("Unsupported type $(typ) for OfpStatsReply.")
    end
    obj = OfpStatsReply{typeof(bdy)}(header, typ, flags, bdy)
    obj.header.msglen = give_length(obj)
    obj
end

# ofp_packet_out
# Send packet (controller -> datapath).
immutable OfpPacketOut <: OfpMessage
    header::OfpHeader
    buffer_id::Uint32 # ID assigned by datapath (-1 if none). Short quiz
        # question: How do you assign a value of -1 to an unsigned integer type?
        # --> 0xFFFFFFFF.
    in_port::Uint16 # Packet's input port (OFPP_NONE if none).
    actions_len::Uint16 # Size of action array in bytes.
    actions::Vector{OfpActionHeader} # Actions.
    # data::Bytes XXX For some reason this is commented out in the spec.
    OfpPacketOut(header, buffer_id, in_port, actions_len, actions) = begin
        @assert header.msgtype == OFPT_PACKET_OUT
        obj = new(header, buffer_id, in_port, actions_len, actions)
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpPacketOut
#@length OfpPacketOut
give_length(po::OfpPacketOut) = 8 + 8 + reduce((l, a)->l + give_length(a), 0, po.actions)
@string OfpPacketOut
@bytesconstructor OfpPacketOut

# Flow removed (datapath -> controller).
# ofp_flow_removed
immutable OfpFlowRemoved <: OfpMessage
    header::OfpHeader
    match::OfpMatch #
    cookie::Uint64 #
    priority::Uint16 # Priority level of flow entry.
    reason::Uint8 # One of OFPRR_*.
    pad::Uint8 # Align to 32-bits.
    duration_sec::Uint32 # Time flow was alive in seconds.
    duration_nsec::Uint32 # Time flow was alive in nanoseconds beyond
        # duration_sec.
    idle_timeout::Uint16 # Idle timeout from original flow mod.
    pad2::Bytes # Length:2; Align to 64-bits.
    packet_count::Uint64
    byte_count::Uint64
    OfpFlowRemoved(header, match, cookie, priority, reason, duration_sec,
        duration_nsec, idle_timeout, packet_count, byte_count) = begin
        @assert header.msgtype == OFPT_FLOW_REMOVED
        obj = new(header, match, cookie, priority, reason, 0x00, duration_sec,
            duration_nsec, idle_timeout, zeros(Uint8, 2), packet_count, byte_count)
        obj.header.msglen = give_length(obj)
        obj
    end
end
@bytes OfpFlowRemoved
@length OfpFlowRemoved
@string OfpFlowRemoved
@bytesconstructor OfpFlowRemoved [:pad2=>2]

# Vendor extension.
# ofp_vendor_header
# TODO Make this a parametric type, with the body value being of the parameter
# type.
immutable OfpVendorHeader <: OfpMessage
    header::OfpHeader # Type OFPT_VENDOR
    vendor::Uint32 # Vendor ID:
                    # - MSB 0: low-order bytes are IEEE OUI.
                    # - MSB != 0: defined by OpenFlow consortium.
    body::Bytes # Vendor-defined arbitrary additional data.
    OfpVendorHeader(header, vendor, body) = begin
        @assert header.msgtype == OFPT_VENDOR
        obj = new(header, vendor, body)
        obj.header.msglen = 12 + length(body)
        obj
    end
end
@bytes OfpVendorHeader
@length OfpVendorHeader
@string OfpVendorHeader
@bytesconstructor OfpVendorHeader

max_stars_repo_name: pchronz/juliaflow

max_issues_repo_name: pchronz/juliaflow

max_forks_repo_name: pchronz/juliaflow


text:
using Test, ReferenceTests, BSON
using ParallelStencil
import ParallelStencil: @reset_parallel_stencil

ENV["USE_GPU"] = false
ENV["DO_VIZ"]  = false
ENV["DO_SAVE"] = false
ENV["DO_SAVE_VIZ"] = false
ENV["NX"] = 32
ENV["NY"] = 32
ENV["NZ"] = 32
ENV["USE_RETURN"] = true

# Reference test using ReferenceTests.jl
comp(d1, d2) = keys(d1) == keys(d2) && all([ isapprox(v1, v2) for (v1,v2) in zip(values(d1), values(d2))])