text:
# tested with symmetries = false
struct tri_struct
    v1::Int64
    v2::Int64
    v3::Int64
end

struct triangles_iterator
    tri::tri_struct
    triinfo::tri_struct
    symmetries::Bool
    S::Union{UnitRange{Int64},Vector{Int64},Int}
    rp::Vector{Int}
    ci::Vector{Int}
    vindicator::Vector{Bool}
    windicator::Vector{Bool}#only needed for the unique case
end

function next_triangle_unique!(tri::tri_struct,triinfo::tri_struct,
    S::Union{UnitRange{Int64},Vector{Int64},Int},
    rp::Vector{Int},
    ci::Vector{Int},
    vindicator::Vector{Bool},
    windicator::Vector{Bool})
    v,w,x = (tri.v1,tri.v2,tri.v3)
    vi,wi,xi = (triinfo.v1,triinfo.v2,triinfo.v3)
    # start with checking x
    wrng = rp[v]:rp[v+1]-1
    xrng = rp[w]:rp[w+1]-1
    # v<w<x
    if xi < length(xrng)
        xi += 1
        new_x = ci[xrng[xi]]
        while xi < length(xrng) && (new_x <= w || vindicator[new_x] == 0 || windicator[new_x] == true)
            xi += 1
            new_x = ci[xrng[xi]]
        end # either A[v,new_x] = 1 or out of spots
        if vindicator[new_x] != 0 && w < new_x && windicator[new_x] == false #= it hasn't been fully used before =#
        # if A[v,new_x] != 0 && w < new_x
            return tri_struct(v,w,new_x),tri_struct(vi,wi,xi)
        end
    end

    windicator[w] = true
    while wi < length(wrng)
        # done with it
        wi += 1
        new_w = ci[wrng[wi]]

        # if v < new_w
        if windicator[new_w] == false
            xrng = rp[new_w]:rp[new_w+1]-1
            xi = 1
            new_x = ci[xrng[xi]]
            while xi < length(xrng) && (new_x <= new_w || vindicator[new_x] == 0 || windicator[new_x] == true)
                xi += 1
                new_x = ci[xrng[xi]]
            end
            if vindicator[new_x] != 0 && new_w < new_x && windicator[new_x] == false
            # if A[v,new_x] != 0 && new_w < new_x
                return tri_struct(v,new_w,new_x),tri_struct(vi,wi,xi)
            end
        end
    end

    while vi < length(S)
        vi += 1
        new_v = S[vi]
        update_vindicator!(vindicator,new_v,rp,ci)
        windicator .=0
        windicator[S[1:vi-1]] .= 1

        wrng = rp[new_v]:rp[new_v+1]-1
        wi = 0
        while wi < length(wrng)
            wi += 1
            new_w = ci[wrng[wi]]
            # if new_v < new_w
            if windicator[new_w] == false
                xrng = rp[new_w]:rp[new_w+1]-1
                xi = 1
                new_x = ci[xrng[xi]]
                # keep trying to find something until...
                while xi < length(xrng) && (new_x <= new_w || vindicator[new_x] == 0 || windicator[new_x] == true #=i.e. we used it before =#)
                    xi += 1
                    new_x = ci[xrng[xi]]
                end
                if vindicator[new_x] != 0 && new_w < new_x && windicator[new_x] == false
                # if A[new_v,new_x] != 0 && new_w < new_x
                    return tri_struct(new_v,new_w,new_x),tri_struct(vi,wi,xi)
                end
            end
        end
    end
    # if this was reached, no more triangles
    return tri_struct(0,0,0),tri_struct(0,0,0)
end
# this implemetation loops over the unique triangles only and for each unique triangle creates the 5 remaining permutations
# this turned out to be much faster in practice -- as the graph gets bigger we would need to look for more non-unique triangles
# in contrast here, we just fetch the 5 remaining permutations of the unique triangle found
#=
For a small 10-by-10 clique:
new implementation:
julia> size(A)
(10, 10)

julia> @btime collect(triangles(A;symmetries=true))
  33.490 μs (18 allocations: 53.23 KiB)

old:
julia> @btime collect(triangles(A;symmetries=true))
  51.747 μs (18 allocations: 53.23 KiB)
=#
function next_triangle!(tri::tri_struct,triinfo::tri_struct,
    S::Union{UnitRange{Int64},Vector{Int64},Int},
    rp::Vector{Int},
    ci::Vector{Int},
    vindicator::Vector{Bool},
    windicator::Vector{Bool},
    step_tri::Int)
    if step_tri <= 4
        return _iterate_on_current_triangle(tri,step_tri),triinfo
    else
        return next_triangle_unique!(tri_struct(tri.v3,tri.v2,tri.v1),triinfo,S,rp,ci,vindicator,windicator)
    end
end

function triprint(t::tri_struct)
    @show t.v1,t.v2,t.v3
end

function _iterate_on_current_triangle(cur_tri::tri_struct,step::Int)
    # only 5 cases, enumerate
    if step == 0
        return tri_struct(cur_tri.v1,cur_tri.v3,cur_tri.v2)
    elseif step == 1
        return tri_struct(cur_tri.v3,cur_tri.v1,cur_tri.v2)
    elseif step == 2
        return tri_struct(cur_tri.v1,cur_tri.v3,cur_tri.v2)
    elseif step == 3
        return tri_struct(cur_tri.v2,cur_tri.v3,cur_tri.v1)
    elseif step == 4
        return tri_struct(cur_tri.v1,cur_tri.v3,cur_tri.v2)
    else
        error("This error should not be reached. Step must be 0 <= step <= 4")
    end
end
function Base.iterate(iter::triangles_iterator, state=(iter.tri, iter.triinfo,0))
    element, elinfo, step_tri = state

    if element == tri_struct(0, 0, 0)
        update_vindicator!(iter.vindicator,iter.S[1],iter.rp,iter.ci)
        iter.windicator.=0
        return nothing
    end

    if iter.symmetries
        v1,v2 = next_triangle!(element,elinfo,iter.S,iter.rp,iter.ci,iter.vindicator,iter.windicator,step_tri)
        step_tri += 1
        step_tri %= 6
    else
        v1,v2 = next_triangle_unique!(element,elinfo,iter.S,iter.rp,iter.ci,iter.vindicator,iter.windicator)
    end
    return (element, (v1,v2,step_tri))
end

function find_first_triangle(S::Union{UnitRange{Int64},Vector{Int64},Int},
    rp::Vector{Int},
    ci::Vector{Int},
    n::Int)
    vi = 1
    v = S[vi]
    rng = rp[v]:rp[v+1]-1
    while !(length(rng)>0) && vi<length(S)
        vi +=1
        v = S[vi]
        rng = rp[v]:rp[v+1]-1
    end
    vindicator = zeros(Bool,n)
    windicator = zeros(Bool,n)

    #either reached the end and the last value is invalid, thus there are no triangles
    if vi == length(S) && !(length(rng)>0)
        return tri_struct(0,0,0),tri_struct(0,0,0),vindicator,windicator # this graph has no edges!
    else
        @inbounds for rpi = rp[v]:rp[v+1]-1
            w = ci[rpi]
            vindicator[w] = true
        end
        wi = 1
        w = ci[rng[wi]]
        vec1,vec2 = next_triangle_unique!(tri_struct(v,w,0),tri_struct(vi,wi,0),S,rp,ci,vindicator,windicator)
        return vec1,vec2,vindicator,windicator
    end
end

function update_vindicator!(vindicator::Vector{Bool},v::Int,rp::Vector{Int},ci::Vector{Int})
    vindicator .= false
    @inbounds for rpi = rp[v]:rp[v+1]-1
        w = ci[rpi]
        vindicator[w] = true
    end
end

# old function: (uses the matrix A)
# function triangles(A::SparseMatrixCSC{T,Int64},S::Union{UnitRange{Int64},Vector{Int64},Int};symmetries=false) where T
#     M = MatrixNetwork(A)

#     if is_undirected(M) == false
#         error("Only undirected (symmetric) inputs are allowed")
#     end

#     rp,ci,ai = (M.rp,M.ci,M.vals)
#     if typeof(findfirst(ai.<0)) != Nothing
#         error("only positive edge weights allowed")
#     end

#     v1,v2 = find_first_triangle(S,rp,ci,A)
#     first_triangle = v1 #onetriangle(v1[1],v1[2],v1[3])
#     first_triangle_info = v2 #onetriangle_info(v2[1],v2[2],v2[3])
#     my_tri_iter = triangles_iterator(first_triangle,first_triangle_info,symmetries,S,rp,ci,A)
# end

"""
- triangles(A) return an iterator for all the triangles in a graph A
- triangles(A,i) return an iterator for all triangles in a graph A involving node i
- triangles(A,S) return an iterator for all triangles in a graph A involving a node in S
- triangles(A,S;symmetries=true) return an iterator of all triangles in A involving a node in S with symmetries
    (i.e. a triangle (i,j,k) is counted 6 times)
sample run:
-----------
```
A = load_matrix_network("clique-10");
mytriangles = triangles(A)
for tri in mytriangles
    MatrixNetworks.triprint(tri)
end
z = collect(mytriangles)
ei,ej,ek = unzip_triangles(z)
```
A quick example to access the first triangle:
-------
```
julia> tri = first(mytriangles)
MatrixNetworks.tri_struct(1, 2, 3)

julia> tri.v1,tri.v2,tri.v3
(1, 2, 3)
```
"""
function triangles(M::MatrixNetwork{T},S::Union{UnitRange{Int64},Vector{Int64},Int};symmetries=false) where T

    if is_undirected(M) == false
        error("Only undirected (symmetric) inputs are allowed")
    end

    rp,ci,ai = (M.rp,M.ci,M.vals)
    if typeof(findfirst(ai.<0)) != Nothing
        error("only positive edge weights allowed")
    end

    v1,v2,vindicator,windicator = find_first_triangle(S,rp,ci,M.n)
    first_triangle = v1 #onetriangle(v1[1],v1[2],v1[3])
    first_triangle_info = v2 #onetriangle_info(v2[1],v2[2],v2[3])
    my_tri_iter = triangles_iterator(first_triangle,first_triangle_info,symmetries,S,rp,ci,vindicator,windicator)
end
function triangles(A::SparseMatrixCSC{T,Int64},S::Union{UnitRange{Int64},Vector{Int64},Int};symmetries=false) where T

    if maximum(S) > A.n
        error("triangles(A,S): the maximum value in S must be <= the number of nodes in the graph")
    end

    if issymmetric(A) == false
        error("Only undirected (symmetric) inputs are allowed")
    end

    rp,ci,ai = A.colptr,A.rowval,A.nzval
    if typeof(findfirst(ai.<0)) != Nothing
        error("Only positive edge weights allowed")
    end

    v1,v2,vindicator,windicator = find_first_triangle(S,rp,ci,A.n)
    first_triangle = v1 #onetriangle(v1[1],v1[2],v1[3])
    first_triangle_info = v2 #onetriangle_info(v2[1],v2[2],v2[3])
    my_tri_iter = triangles_iterator(first_triangle,first_triangle_info,symmetries,S,rp,ci,vindicator,windicator)
end

# generic functions
triangles(G::SparseMatrixCSC{T,Int64};symmetries=false) where T = triangles(G,1:size(G,1);symmetries=symmetries)
triangles(M::MatrixNetwork{T};symmetries=false) where T = triangles(M,1:M.n;symmetries=symmetries)
# the following are no longer needed because of the general triangles function above
# triangles(G::SparseMatrixCSC{T,Int64}) where T = triangles(G,1:size(G,1))
# triangles(G::SparseMatrixCSC{T,Int64},i::Int64) where T =  triangles(G,i:i)
# triangles(G::SparseMatrixCSC{T,Int64},i::Int64;symmetries=false) where T =  triangles(G,i;symmetries=symmetries)

import Base.collect
function collect(tris::triangles_iterator)
    alltriangles = Array{Tuple{Int,Int,Int}}(undef,0)
    for ti in tris
        push!(alltriangles,(ti.v1,ti.v2,ti.v3))
    end
    return alltriangles
end

function unzip_triangles(z::Vector{Tuple{Int64,Int64,Int64}})
    n = length(z)
    ei = zeros(Int,n)
    ej = zeros(Int,n)
    ek = zeros(Int,n)
    @inbounds for i = 1:n
        ei[i],ej[i],ek[i] = z[i]
    end
    return ei,ej,ek
end


text:
module ParrotHelperCustomFakeHeart

using ..Ahorn, Maple

@mapdef Entity "ParrotHelper/CustomFakeHeart" CustomFakeHeart(x::Integer, y::Integer, presets::String="Gray", color::String="", fakeHeartDialog::String="CH9_FAKE_HEART", keepGoingDialog::String="CH9_KEEP_GOING", flag::String="fake_heart", removeCameraTriggers::Bool=false)

const placements = Ahorn.PlacementDict(
   "Custom Fake Heart (ParrotHelper)" => Ahorn.EntityPlacement(
	  CustomFakeHeart
   )
)

const heartTypes = ["Blue", "Red", "Gold", "Gray", ""]

Ahorn.editingOptions(entity::CustomFakeHeart) = Dict{String, Any}(
  "presets" => heartTypes
)

function getColor(color)
	if haskey(Ahorn.XNAColors.colors, color)
		return Ahorn.XNAColors.colors[color]

	else
		try
			return ((Ahorn.argb32ToRGBATuple(parse(Int, replace(color, "#" => ""), base=16))[1:3] ./ 255)..., 1.0)

		catch

		end
	end

	return (1.0, 1.0, 1.0, 1.0)
end

function Ahorn.selection(entity::CustomFakeHeart)
	x, y = Ahorn.position(entity)
	return Ahorn.getSpriteRectangle("collectables/heartGem/3/00", x, y)
end

function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::CustomFakeHeart, room::Maple.Room)
	if entity.presets == "Blue"
		Ahorn.drawSprite(ctx, "collectables/heartGem/0/00", 0, 0)
	elseif entity.presets == "Red"
		Ahorn.drawSprite(ctx, "collectables/heartGem/1/00", 0, 0)
	elseif entity.presets == "Gold"
		Ahorn.drawSprite(ctx, "collectables/heartGem/2/00", 0, 0)
	elseif entity.presets == "Gray"
		Ahorn.drawSprite(ctx, "collectables/heartGem/3/00", 0, 0)
	else
		inputcolor = entity.color
		color = getColor(inputcolor)
		Ahorn.drawSprite(ctx, "collectables/ParrotHelper/FakeHeart/00", 0, 0, tint=color)
	end
end

end

ext: jl

max_stars_repo_name: Parrotapocalypse/ParrotHelper

max_issues_repo_name: Parrotapocalypse/ParrotHelper

max_forks_repo_name: Parrotapocalypse/ParrotHelper


text:
module DCELs

using RecipesBase
using ResumableFunctions
using UUIDs

export AbstractGeometry, AbstractVertex, AbstractEdge, AbstractFace, AbstractTessellation
export GeometryError
export Vertex, Edge, Face, Tessellation
export edge_path, edge_loop
export vertices, edges, faces
export connect
export find_outer_face
export fix_faces!
export validate_geometry

#= Abstract Types =#
"""
    AbstractGeometry

Abstract supertype for all geometry objects.
"""
abstract type AbstractGeometry end

"""
    AbstractVertex

Abstract supertype for all vertex objects.
"""
abstract type AbstractVertex <: AbstractGeometry end

"""
    AbstractEdge

Abstract supertype for all edge objects.
"""
abstract type AbstractEdge <: AbstractGeometry end

"""
    AbstractFace

Abstract supertype for all face objects.
"""
abstract type AbstractFace <: AbstractGeometry end

"""
    AbstractTessellation

Abstract supertype for all tessellations.
"""
abstract type AbstractTessellation <: AbstractGeometry end

#= Basic types =#
@inline sid(s::String) = s * "-" * string(uuid4())

"""
    Edge <: AbstractEdge

Edge between two vertices.

# Fields
- `id::String`: Unique ID.
- `next`::Union{Edge, Nothing}`: Next edge alongside the corresponding face (counterclockwise).
- `prev`::Union{Edge, Nothing}`: Previous edge alongside the corresponding face (counterclockwise).
- `twin`::Union{Edge, Nothing}`: Twin edge.
- `prev`: Vertex that the edge points to.
- `face`: Face bounded by the edge.
"""
mutable struct Edge <: AbstractEdge
    id::String
    next::Union{Edge, Nothing}
    prev::Union{Edge, Nothing}
    twin::Union{Edge, Nothing}
    dest
    face
end

"""
    Edge()

Create an `Edge` with a random UUID4 ID prefixed by "E-", and all other fields set to `nothing`.
"""
Edge() = Edge(sid("E"), nothing, nothing, nothing, nothing, nothing)

"""
    Vertex <: AbstractVertex

2D point in the plane.

# Fields
- `id::String`: Unique ID.
- `x::Real`: x coordinate.
- `y::Real`: y coordinate.
- `edge::Union{Edge, Nothing}`: Arbitrary edge that points towards this vertex.
"""
mutable struct Vertex <: AbstractVertex
    id::String
    x::Real
    y::Real
    edge::Union{Edge, Nothing}
end

"""
    Vertex()

Create a `Vertex` with a random UUID4 ID prefixed by "V-", with coordinates `(NaN, NaN)`,
and `edge=nothing`.
"""
Vertex() = Vertex(sid("V"), NaN, NaN, nothing)

"""
    Vertex(x::Real, y::Real)

Create a `Vertex` with a random UUID4 ID prefixed by "V-", with coordinates `(x, y)`,
and `edge=nothing`.
"""
Vertex(x::Real, y::Real) = Vertex(sid("V"), x, y, nothing)

"""
    Face <: AbstractFace

Area in the plane bounded by edges.

# Fields
- `id::String`: Unique ID.
- `edge::Union{Edge, Nothing}`: Arbitrary edge that bounds this face.
"""
mutable struct Face <: AbstractFace
    id::String
    edge::Union{Edge, Nothing}
end

"""
    Face()

Create a `Face` with a random UUID4 ID prefixed by "F-", and `edge=nothing`.
"""
Face() = Face(sid("F"), nothing)

"""
    Tessellation <: AbstractTessellation

Collection of vertices, edges and faces.

# Fields
- `edges::Set{Edge}`: Set of edges belonging to the tessellation.
- `vertices::Set{Vertex}`: Set of vertices.
- `faces::Set{Face}`: Set of faces.
"""
mutable struct Tessellation <: AbstractTessellation
    edges::Set{Edge}
    vertices::Set{Vertex}
    faces::Set{Face}
end

"""
    Tessellation()

Create a `Tessellation` with no edges, vertices or faces.
"""
Tessellation() = Tessellation(Set(), Set(), Set())

#= Exceptions =#
"""
    GeometryError(msg::String)

Geometry is inconsistent.
"""
mutable struct GeometryError <: Exception
    msg::String
end

#= Printing =#
function Base.show(io::IO, edge::Edge)
    print(io,
    """
    ID: $(edge.id)
    Next: $(edge.next === nothing ? "nothing" : edge.next.id)
    Prev: $(edge.prev === nothing ? "nothing" : edge.prev.id)
    Twin: $(edge.twin === nothing ? "nothing" : edge.twin.id)
    Dest: $(edge.dest === nothing ? "nothing" : edge.dest.id)
    Face: $(edge.face === nothing ? "nothing" : edge.face.id)
    """
    )
end

function Base.show(io::IO, vertex::Vertex)
    print(io,
    """
    ID: $(vertex.id)
    Coords: ($(vertex.x), $(vertex.y))
    Edge: $(vertex.edge === nothing ? "nothing" : vertex.edge.id)
    """
    )
end

function Base.show(io::IO, face::Face)
    print(io,
    """
    ID: $(face.id)
    Edge: $(face.edge === nothing ? "nothing" : face.edge.id)
    """
    )
end

function Base.show(io::IO, tess::Tessellation)
    print(io,
    """
    Tessellation with:
    $(length(tess.vertices)) vertices
    $(length(tess.edges)) edges
    $(length(tess.faces)) faces
    """
    )
end

function Base.showerror(io::IO, e::GeometryError)
    print(io,
    """
    GeometryError\n$(e.msg)
    """
    )
end

#= Arithmetic =#
Base.:+(a::T, b::T) where {T <: AbstractVertex} = T(a.x+b.x, a.y+b.y)
Base.:-(a::T, b::T) where {T <: AbstractVertex} = T(a.x-b.x, a.y-b.y)

#= Iterators =#
@doc """
    edge_path(start::AbstractEdge, finish::AbstractEdge; reverse::Bool=false)

Yield all edges in the list `[start, start.next, start.next.next, ..., finish.prev]`. If
`start` and `finish` do not bound the same face, this will never terminate.

If `reverse=true`, the order will be `[start, start.prev, ..., finish.next]`.
"""
edge_path(start::AbstractEdge, finish::AbstractEdge; reverse::Bool=false)

@resumable function edge_path(start::AbstractEdge, finish::AbstractEdge; reverse::Bool=false)
    @yield start
    edge = start
    if reverse
        while edge.prev != finish
            edge = edge.prev
            @yield edge
        end
    else
        while edge.next != finish
            edge = edge.next
            @yield edge
        end
    end
end

@doc """
    edge_loop(start::AbstractEdge; reverse::Bool=false)

Yield all edges in the list `[start, start.next, start.next.next, ..., start.prev]`. If
`reverse=true`, this will instead yield `[start, start.prev, ..., start.next]`.
"""
edge_loop(start::AbstractEdge; reverse::Bool=false)

@resumable function edge_loop(start::AbstractEdge; reverse::Bool=false)
    for edge in edge_path(start, start, reverse=reverse)
        @yield edge
    end
end

@doc """
    edges(vertex::AbstractVertex; reverse::Bool=false)

Yield all edges that point to `vertex`, in counterclockwise order
(or clockwise if `reverse=true`).
"""
edges(vertex::AbstractVertex; reverse::Bool=false)

@resumable function edges(vertex::AbstractVertex; reverse::Bool=false)
    start = edge = vertex.edge
    @yield start
    if reverse
        while edge.next.twin != start
            edge = edge.next.twin
            @yield edge
        end
    else
        while edge.twin.prev != start
            edge = edge.twin.prev
            @yield edge
        end
    end
end

@doc """
    edges(face::AbstractFace; reverse::Bool=false)

Yield all edges that bound `face`, in counterclockwise order
(or clockwise if `reverse=true`).
"""
edges(face::AbstractFace; reverse::Bool=false)

@resumable function edges(face::AbstractFace; reverse::Bool=false)
    for edge in edge_loop(face.edge, reverse=reverse)
        @yield edge
    end
end

@doc """
    vertices(edge::AbstractEdge; reverse::Bool=false)

Yield both endpoints of `edge`, starting at the destination (or origin if `reverse=true`).
"""
vertices(edge::AbstractEdge; reverse::Bool=false)

@resumable function vertices(edge::AbstractEdge; reverse::Bool=false)
    if reverse
        @yield edge.twin.dest
        @yield edge.dest
    else
        @yield edge.dest
        @yield edge.twin.dest
    end
end

@doc """
    vertices(face::AbstractFace; reverse::Bool=false)

Yield all vertices that touch `face`, in counterclockwise order
(or clockwise if `reverse=true`).
"""
vertices(face::AbstractFace; reverse::Bool=false)

@resumable function vertices(face::AbstractFace; reverse::Bool=false)
    for edge in edges(face, reverse=reverse)
        @yield edge.dest
    end
end

@doc """
    faces(edge::AbstractEdge; reverse::Bool=false)

Yield both faces that `edge` touches, starting by the left one (or right if `reverse=true`).
"""
faces(edge::AbstractEdge; reverse::Bool=false)

@resumable function faces(edge::AbstractEdge; reverse::Bool=false)
    if reverse
        @yield edge.twin.face
        @yield edge.face
    else
        @yield edge.face
        @yield edge.twin.face
    end
end

@doc """
    faces(vertex::AbstractVertex; reverse::Bool=false)

Yield all of the faces that touch `vertex`, in counterclockwise order
(or clockwise if `reverse=true`).
"""
faces(vertex::AbstractVertex; reverse::Bool=false)

@resumable function faces(vertex::AbstractVertex; reverse::Bool=false)
    for edge in edges(vertex, reverse=reverse)
        @yield edge.face
    end
end

@doc """
    faces(face::AbstractFace; reverse::Bool=false)

Yield all faces that share an edge with `face`, in counterclockwise order
(or clockwise if `reverse=true`).
"""
faces(face::AbstractFace; reverse::Bool=false)

@resumable function faces(face::AbstractFace; reverse::Bool=false)
    for edge in edges(face, reverse=reverse)
        @yield edge.twin.face
    end
end

#= Sorting =#
@inline function _pseudoangle(dx::Real, dy::Real)
    p = dx / (abs(dx) + abs(dy))
    return dy < 0 ? p - 1 : 1 - p
end

@inline function _pseudoangle_unsigned(dx::Real, dy::Real)
    r = _pseudoangle(dx, dy)
    r = r < 0 ? r + 4 : r
end

function _is_sorted_ccw(a::AbstractVertex, b::AbstractVertex, c::AbstractVertex, center::AbstractVertex)
    ang_a = _pseudoangle_unsigned(a.x - center.x, a.y - center.y)
    ang_b = _pseudoangle_unsigned(b.x - center.x, b.y - center.y)
    ang_c = _pseudoangle_unsigned(c.x - center.x, c.y - center.y)
    return (ang_a <= ang_b <= ang_c) || (ang_b <= ang_c <= ang_a) || (ang_c <= ang_a <= ang_b)
end

"""
    squeeze_edge(vertex::AbstractVertex, edge::AbstractEdge)

Returns an edge `h` such that `h.dest = vertex`, and if you rotate `edge` counterclockwise
around `vertex`, the first edge you'll meet is `h`.
"""
function squeeze_edge(vertex::AbstractVertex, edge::AbstractEdge)
    if vertex.edge === nothing
        return edge
    end

    iter = edges(vertex)
    res1 = iterate(iter)
    res2 = iterate(iter)
    if res2 === nothing
        return res1[1]
    end

    e1, e2 = res1[1], res2[1]
    v1 = e1.twin.dest
    v2 = e2.twin.dest
    u = edge.twin.dest

    while !_is_sorted_ccw(v1, u, v2, vertex)
        res2 = iterate(iter)
        if res2 === nothing
            return vertex.edge
        end
        e1 = e2
        e2 = res2[1]
        v1, v2 = e1.twin.dest, e2.twin.dest
    end
    return e2
end

#= Topological operations =#
function _connect_vertices!(u::AbstractVertex, v::AbstractVertex)
    h1, h2 = Edge(), Edge()
    h1.twin, h2.twin = h2, h1
    h1.dest, h2.dest = v, u

    ha = squeeze_edge(v, h1)
    hb = squeeze_edge(u, h2)

    u.edge, v.edge = h2, h1
    h1.face = h2.face = hb.face

    h1.prev, h1.next = hb, ha.next
    h2.prev, h2.next = ha, hb.next
    ha.next, hb.next = h2, h1
    h1.next.prev, h2.next.prev = h1, h2
    return h1
end

function _update_edges_face!(edge::AbstractEdge, f::AbstractFace)
    for e in edge_loop(edge)
        e.face = f
    end
end

function _split_face!(u::AbstractVertex, v::AbstractVertex)
    h1 = _connect_vertices!(u, v)
    h2 = h1.twin

    f = h1.face
    f1, f2 = Face(), Face()
    f1.edge, f2.edge = h1, h2

    _update_edges_face!(h2, f2)
    _update_edges_face!(h1, f1)

    return h1, f
end

function _connect_manual!(
    tess::AbstractTessellation,
    u::AbstractVertex,
    v::AbstractVertex,
    split::Bool
)
    if split
        h, f = _split_face!(u, v)
        delete!(tess.faces, f)
        push!(tess.faces, h.face)
    else
        h = _connect_vertices!(u, v)
    end

    push!(tess.edges, h)
    return h
end

function _connect_automatic!(
    tess::AbstractTessellation,
    u::AbstractVertex,
    v::AbstractVertex
)
    h1, h2 = Edge(), Edge()
    h1.twin, h2.twin = h2, h1
    h1.dest, h2.dest = v, u

    ha = squeeze_edge(v, h1)
    hb = squeeze_edge(u, h2)

    edges_connected = false
    if ha != h1
        for h in edge_loop(ha)
            if h == hb
                edges_connected = true
                break
            end
        end
    end

    u.edge, v.edge = h2, h1
    h1.face = h2.face = hb.face

    h1.prev, h1.next = hb, ha.next
    h2.prev, h2.next = ha, hb.next
    ha.next, hb.next = h2, h1
    h1.next.prev, h2.next.prev = h1, h2

    if edges_connected
        f = h1.face
        f1, f2 = Face(), Face()
        f1.edge, f2.edge = h1, h2

        _update_edges_face!(h2, f2)
        _update_edges_face!(h1, f1)

        delete!(tess.faces, f)
        push!(tess.faces, f1, f2)
    end

    push!(tess.edges, h1)
    return h1
end

"""
    connect(args...; kwargs...)

Connect two vertices.

# Arguments
- `tess::AbstractTessellation`: Tessellation to update.
- `u::AbstractVertex`: First vertex to connect.
- `v::AbstractVertex`: Second vertex to connect.

# Keywords
- `split::Union{Bool, Nothing}=nothing`: Whether connecting `u` and `v` will create a new face. If `nothing`, it will be determined automatically, at the cost of more operations.

# Returns
- `h`: The newly created edge from `u` to `v`.`
"""
function connect(
    tess::AbstractTessellation,
    u::AbstractVertex,
    v::AbstractVertex;
    split::Union{Bool, Nothing}=nothing
)
    if split === nothing
        h = _connect_automatic!(tess, u, v)
    else
        h = _connect_manual!(tess, u, v, split)
    end
    return h
end

# TODO: Add special case for triangulation? (All faces are triangular except outer)
"""
    find_outer_face(tess::AbstractTessellation)

Return the unbounded face of `tess`.
"""
function find_outer_face(tess::AbstractTessellation)
    min_y, min_vertex = Inf, nothing
    for vertex in tess.vertices
        if vertex.edge !== nothing && vertex.y < min_y
            min_y = vertex.y
            min_vertex = vertex
        end
    end

    min_angle, min_edge = Inf, nothing
    for edge in edges(min_vertex)
        other = edge.twin.dest
        vec = other - min_vertex
        ang = _pseudoangle_unsigned(vec.x, vec.y)
        if ang < min_angle
            min_angle = ang
            min_edge = edge
        end
    end

    return min_edge.face
end

#= Fixing =#
"""
    fix_faces!(tess::AbstractTessellation)

Create the missing faces between the edges of `tess`. It is assumed that the `prev` and
`next` fields are consistent.
"""
function fix_faces!(tess::AbstractTessellation)
    if !isempty(tess.faces)
        throw(GeometryError("Set of faces must be empty."))
    end

    visited = Set()
    for full_edge in tess.edges
        for edge in [full_edge, full_edge.twin]
            if !(edge in visited)
                face = Face()
                face.edge = edge
                push!(tess.faces, face)

                for e in edge_loop(edge)
                    e.face = face
                    push!(visited, e)
                end
            end
        end
    end
end

#= Validity =#
# TODO: Simplify this.
function validate_geometry(edges::AbstractVector{<:AbstractEdge})
    for edge_pair in edges
        for edge in [edge_pair, edge_pair.twin]
            if edge.twin === nothing
                throw(GeometryError("Edge has no twin, $(edge.id)."))
            end

            if edge.next === nothing
                throw(GeometryError("Edge has no next, $(edge.id)."))
            end

            if edge.prev === nothing
                throw(GeometryError("Edge has no prev, $(edge.id)."))
            end

            if edge != edge.twin.twin
                throw(GeometryError("twin violation at $(edge.id), $(edge.twin.id)."))
            end

            if edge.prev.next != edge
                throw(GeometryError("prev-next violation at $(edge.id)."))
            end

            if edge.next.prev != edge
                throw(GeometryError("next-prev violation at $(edge.id)."))
            end

            count = 1
            temp = edge
            while temp.next != edge
                temp = temp.next
                count += 1
                if count > 2 * length(edges)
                    throw(GeometryError("Open edge loop starting at $(edge.id)."))
                end
            end

            count = 1
            temp = edge
            while temp.prev != edge
                temp = temp.prev
                count += 1
                if count > 2 * length(edges)
                    throw(GeometryError("Open edge loop starting at $(edge.id)."))
                end
            end
        end
    end
end

# TODO: Simplify this.
function validate_geometry(vertices::AbstractVector{<:AbstractVertex})
    for vertex in vertices
        if vertex.edge === nothing
            return nothing
        end
        iter = edges(vertex)
        r1 = iterate(iter)
        r2 = iterate(iter)
        if r2 === nothing
            return nothing
        end
        r3 = iterate(iter)
        while r3 !== nothing
            v1, v2, v3 = r1[1].twin.dest, r2[1].twin.dest, r3[1].twin.dest
            if !_is_sorted_ccw(v1, v2, v3, vertex)
                throw(GeometryError("Vertices not in clockwise order: $(v1.id), $(v2.id), $(v3.id)."))
            end
            r1, r2 = r2, r3
            r3 = iterate(iter)
        end
    end
end

#= Plotting =#
@recipe function f(vertex::AbstractVertex; vertex_labels=false)
    if vertex_labels
        series_annotations := vertex.id[end-2:end]
    end
    seriestype := :scatter
    legend := false
    color --> "black"
    return [vertex.x], [vertex.y]
end

@recipe function f(
    vertices::AbstractVector{<:AbstractVertex};
    vertex_labels=false,
)
    l = length(vertices)

    if l == 0
        return nothing
    end

    x, y = Vector{Real}(undef, l), Vector{Real}(undef, l)
    if vertex_labels
        z = Vector{String}(undef, l)
    end
    for i in 1:l
        x[i], y[i] = vertices[i].x, vertices[i].y
        if vertex_labels
            z[i] = vertices[i].id[end-2:end]
        end
    end

    seriestype := :scatter
    legend := false
    color --> "black"
    if vertex_labels
        series_annotations := z
    end
    return x, y
end

@recipe function f(
    edges::AbstractVector{<:AbstractEdge};
    endpoints = false,
    edge_labels = false,
    vertex_labels = false,
)
    l = length(edges)

    x, y = Vector{Real}(undef, 3*l), Vector{Real}(undef, 3*l)
    if endpoints || vertex_labels
        vertices = Set{Vertex}()
    end
    if edge_labels
        xl, yl, zl = Vector{Real}(undef, l), Vector{Real}(undef, l), Vector{String}(undef, l)
    end

    for (i, edge) in enumerate(edges)
        a, b = edge.dest, edge.twin.dest
        x[3*i-2], y[3*i-2] = a.x, a.y
        x[3*i-1], y[3*i-1] = b.x, b.y
        x[3*i], y[3*i] = NaN, NaN
        if edge_labels
            xl[i], yl[i] = (a.x + b.x)/2, (a.y + b.y)/2
            zl[i] = "$(edge.twin.id[end-2:end])/$(edge.id[end-2:end])"
        end
        if endpoints || vertex_labels
            push!(vertices, a, b)
        end
    end

    if edge_labels
        @series begin
            seriestype := :scatter
            markeralpha := 0
            series_annotations := zl
            return xl, yl
        end
    end

    if endpoints || vertex_labels
        lv = length(vertices)
        xv, yv = Vector{Float64}(undef, lv), Vector{Float64}(undef, lv)
        if vertex_labels
            zv = Vector{String}(undef, lv)
        end

        for (i, vertex) in enumerate(vertices)
            xv[i], yv[i] = vertex.x, vertex.y
            if vertex_labels
                zv[i] = vertex.id[end-2:end]
            end
        end

        @series begin
            seriestype := :scatter
            color --> "black"
            if !endpoints
                markeralpha := 0
            end
            if vertex_labels
                series_annotations := zv
            end
            return xv, yv
        end
    end

    seriestype := :path
    color --> "black"
    legend := false
    return x, y
end

@recipe function f(vertices::AbstractSet{<:AbstractVertex})
    return collect(vertices)
end

@recipe function f(edges::AbstractSet{<:AbstractEdge})
    return collect(edges)
end

end


text:
using CUDAnative

function kernel_vadd(a, b, c)
    i = (blockIdx().x-1) * blockDim().x + threadIdx().x
    c[i] = a[i] + b[i]

    return nothing
end

Base.code_llvm(stdout, kernel_vadd, Tuple{CuDeviceArray{Float32,1,AS.Global},
                                          CuDeviceArray{Float32,1,AS.Global},
                                          CuDeviceArray{Float32,1,AS.Global}})

using TinyCassette
struct Ctx end

Base.code_llvm(stdout, TinyCassette.execute,
               Tuple{Ctx, typeof(kernel_vadd),
                     CuDeviceArray{Float32,1,AS.Global},
                     CuDeviceArray{Float32,1,AS.Global},
                     CuDeviceArray{Float32,1,AS.Global}})


text:
function AEMFile()
    return AEMFile(())
end

function add_attitude_block(obj::AEMFile)
    return jcall(obj, "addAttitudeBlock", void, ())
end

function check_time_systems(obj::AEMFile)
    return jcall(obj, "checkTimeSystems", void, ())
end

function get_attitude_blocks(obj::AEMFile)
    return jcall(obj, "getAttitudeBlocks", List, ())
end

function get_satellites(obj::AEMFile)
    return jcall(obj, "getSatellites", Map, ())
end


text:
"""
Port of K.jpg's OpenSimplex 2, smooth variant ("SuperSimplex") to Julia
"""
module OpenSimplex

const PRIME_X = 0x5205402B9270C86F
const PRIME_Y = 0x598CD327003817B5
const PRIME_Z = 0x5BCC226E9FA0BACB
const PRIME_W = 0x56CC5227E58F554B
const HASH_MULTIPLIER = 0x53A3F72DEEC546F5

abstract type NoiseType end

p4(v) = (v2 = v * v ; v2 * v2)

# constants & lookup tables

include("grad2.jl")
include("grad3.jl")
include("grad4.jl")

# Noise Evaluators

export noise

function noise end

include("noise2d.jl")
include("noise3d.jl")
include("noise4d.jl")
include("vertexcodes.jl")

# Utility

#=
function fastFloor(x::Float64)
    xi = Int64(x)
    return x < xi ? xi - 1 : xi
end
=#
fastFloor(x::Float64) = floor(Int64, x)
end # module OpenSimplex


text:
function test_construction(obj_class, args...)
    try
        obj_class(args...)
        return true
    catch y
        @show y
        return false
    end
end

max_stars_repo_name: mkschleg/JuliaRL

max_issues_repo_name: mkschleg/JuliaRL

max_forks_repo_name: mkschleg/JuliaRL


text:
using kMeansClustering
using Test
using Random
using Clustering

@testset "kMeansClustering.jl" begin
    Random.seed!(1)
    n_points=1000
    dim=4
    mat = rand(dim,n_points)
    k = 10
    r = mykmeansclustering(mat,k) # organize mat into k clusters
    @test size(r[1]) == (dim,k)
    @test length(r[3]) == n_points
    c=r[4]
    kmeans!(mat,c)
    @test sortslices(r[1],dims=2) == sortslices(c,dims=2)
end


text:
abstract type PrimeField <: Number end

infield(x::Number,y::Number) = x >= 0 && x < y

"Represents FieldElement type in which 𝑛 ∈ 𝐹𝑝 and 𝑝 ∈ ℙ"
struct FieldElement <: PrimeField
    𝑛::Integer
    𝑝::Integer
    FieldElement(𝑛,𝑝) = !infield(𝑛,𝑝) ? throw(DomainError("𝑛 is not in field range")) : new(𝑛,𝑝)
end

"Formats PrimeField as 𝑛 : 𝐹ₚ"
function show(io::IO, z::PrimeField)
    print(io, z.𝑛, " : 𝐹", z.𝑝)
end

"Returns true if both 𝑛 and 𝑝 are the same"
==(𝑋₁::PrimeField,𝑋₂::PrimeField) = 𝑋₁.𝑝 == 𝑋₂.𝑝 && 𝑋₁.𝑛 == 𝑋₂.𝑛
==(::PrimeField,::Integer) = false

"Adds two numbers of the same field"
function +(𝑋₁::PrimeField,𝑋₂::PrimeField)
    if 𝑋₁.𝑝 != 𝑋₂.𝑝
        throw(DomainError("Cannot operate on two numbers in different Fields"))
    else
        𝑛 = mod(𝑋₁.𝑛 + 𝑋₂.𝑛, 𝑋₁.𝑝)
        return typeof(𝑋₁)(𝑛, 𝑋₁.𝑝)
    end
end

"Substracts two numbers of the same field"
function -(𝑋₁::PrimeField,𝑋₂::PrimeField)
    if 𝑋₁.𝑝 != 𝑋₂.𝑝
        throw(DomainError("Cannot operate on two numbers in different Fields"))
    else
        𝑛 = mod(𝑋₁.𝑛 - 𝑋₂.𝑛, 𝑋₁.𝑝)
        return typeof(𝑋₁)(𝑛, 𝑋₁.𝑝)
    end
end

"Multiplies two numbers of the same field"
function *(𝑋₁::PrimeField,𝑋₂::PrimeField)
    if 𝑋₁.𝑝 != 𝑋₂.𝑝
        throw(DomainError("Cannot operate on two numbers in different Fields"))
    else
        𝑛 = mod(𝑋₁.𝑛 * 𝑋₂.𝑛, 𝑋₁.𝑝)
        return typeof(𝑋₁)(𝑛, 𝑋₁.𝑝)
    end
end

"Multiplies a PrimeField by an Integer"
function *(𝑐::Integer,𝑋::PrimeField)
    𝑛 = mod(𝑐 * 𝑋.𝑛, 𝑋.𝑝)
    return typeof(𝑋)(𝑛, 𝑋.𝑝)
end

"Returns 𝑋ᵏ using Fermat's Little Theorem"
function ^(𝑋::PrimeField,𝑘::Int)
    𝑛 = powermod(𝑋.𝑛, mod(𝑘, (𝑋.𝑝 - 1)), 𝑋.𝑝)
    return typeof(𝑋)(𝑛, 𝑋.𝑝)
end

"Returns 1/𝑋 as a special case of exponentiation where 𝑘 = -1"
function inv(𝑋::PrimeField)
    𝑛 = powermod(𝑋.𝑛, mod(-1, (𝑋.𝑝 - 1)), 𝑋.𝑝)
    return typeof(𝑋)(𝑛, 𝑋.𝑝)
end

function div(𝑋₁::PrimeField,𝑋₂::PrimeField)
    return 𝑋₁ / 𝑋₂
end

"Returns 𝑋₁/𝑋₂ using Fermat's Little Theorem"
function /(𝑋₁::PrimeField,𝑋₂::PrimeField)
    if 𝑋₁.𝑝 != 𝑋₂.𝑝
        throw(DomainError("Cannot operate on two numbers in different Fields"))
    else
        𝑛 = mod(𝑋₁.𝑛 * powermod(𝑋₂.𝑛, 𝑋₁.𝑝 - 2, 𝑋₁.𝑝), 𝑋₁.𝑝)
        return typeof(𝑋₁)(𝑛, 𝑋₁.𝑝)
    end
end


text:
export
  getCliqVariableInferredPercent,
  getCliqVariableMoreInitDims,
  getVariablePossibleDim

## Description
# Dim              -- Native dimension of Variable/Factor
# Possible         -- Maximum dimension possible in variable
# Solvable         -- number of dimensions which can be resolved from current state
# Inferred         -- Current inferered dimension available
# suffix-Fraction  -- Report as percentage fraction
# XPercentage      -- Report ratio of X over Possible

## Major objectives
#
# getCliqVariableInferredPercent
# getCliqVariableMoreInitDims

## Variables

"""
    $SIGNATURES

Return the number of dimensions this variable vertex `var` contains.

Related

getVariableInferredDim, getVariableInferredDimFraction
"""
getVariableDim(vard::VariableNodeData)::Int = getVariableType(vard) |> getDimension
getVariableDim(var::DFGVariable)::Int = getVariableDim(getSolverData(var))

"""
    $SIGNATURES

Return the number of projected dimensions into a variable during inference.

Notes
- `saturate` clamps return value to no greater than variable dimension

Related

getVariableDim, getVariableInferredDimFraction, getVariableInferredDim, getVariableDim
"""
getVariableInferredDim(vard::VariableNodeData, saturate::Bool=false) = saturate && getVariableDim(vard) < vard.inferdim ? getVariableDim(vard) : vard.inferdim
getVariableInferredDim(var::DFGVariable, saturate::Bool=false) = getVariableInferredDim(getSolverData(var), saturate)
function getVariableInferredDim(fg::G, varid::Symbol, saturate::Bool=false) where G <: AbstractDFG
  getVariableInferredDim(getVariable(fg, varid), saturate)
end

"""
    $SIGNATURES

Return the number of projected dimensions into a variable during inference as a percentage fraction.

Notes
- `saturate` clamps return value to no greater than variable dimension

Related

getVariableDim, getVariableInferredDim, getVariableDim
"""
getVariableInferredDimFraction(vard::VariableNodeData, saturate::Bool=false)::Float64 = getVariableInferredDim(vard, saturate) / getVariableDim(vard)
getVariableInferredDimFraction(var::DFGVariable, saturate::Bool=false)::Float64 = getVariableInferredDim(getSolverData(var), saturate)
function getVariableInferredDimFraction(dfg::G, varid::Symbol, saturate::Bool=false)::Float64 where G <: AbstractDFG
  getVariableInferredDimFraction(getVariable(dfg, varid), saturate)
end

## Factors

"""
    $SIGNATURES

Return the sum of factor dimensions connected to variable as per the factor graph `fg`.

Related

getFactorSolvableDim, getVariableDim, getVariableInferredDim, getFactorDim, isCliqFullDim
"""
function getVariablePossibleDim(fg::G, var::DFGVariable, fcts::Vector{Symbol}=ls(fg, var.label))::Float64 where G <: AbstractDFG
  alldims = 0.0
  for fc in fcts
    alldims += getFactorDim(fg, fc)
  end
  return alldims
end
function getVariablePossibleDim(fg::G,
                                varid::Symbol,
                                fcts::Vector{Symbol}=ls(fg, varid)  )::Float64 where G <: AbstractDFG
  #
  getVariablePossibleDim(fg, getVariable(fg, varid))
end

"""
    $SIGNATURES

Return the total inferred dimension available for variable from factor based on current inferred status of other connected variables.

Notes
- Accumulate the factor dimension fractions:  Sum [0..1]*zdim
- Variable dimenion fractions are inferdim / vardim
- Variable dimension are saturated at vardim for the calculating solve dimensions

Related

getVariablePossibleDim, getVariableDim, getVariableInferredDim, getFactorDim, getFactorSolvableDim, isCliqFullDim
"""
function getFactorInferFraction(dfg::G,
                                idfct::Symbol,
                                varid::Symbol,
                                saturate::Bool=false  )::Float64 where G <: AbstractDFG
  # get all other variables
  allvars = lsf(dfg, idfct)
  lievars = setdiff(allvars, [varid;])

  # get all other var dimensions with saturation
  len = length(lievars)
  fracs = map(lv->getVariableInferredDimFraction(dfg, lv, true), lievars)

  if length(fracs) == 0
    return 0.0
  end

  # the dimension of leave one out variables dictate if this factor can prodive full information on leave out variable.
  return cumprod(fracs)[end]
end

"""
    $SIGNATURES

Return the total inferred/solvable dimension available for variable based on current inferred status of other factor connected variables.

Notes
- Accumulate the factor dimension fractions:  Sum [0..1]*zdim
- Variable dimenion fractions are inferdim / vardim
- Variable dimension are saturated at vardim for the calculating solve dimensions

Related

getVariablePossibleDim, getVariableDim, getVariableInferredDim, getFactorDim, getFactorInferFraction, isCliqFullDim
"""
function getFactorSolvableDimFraction(dfg::G,
                                      idfct::Symbol,
                                      varid::Symbol,
                                      saturate::Bool=false  )::Float64 where G <: AbstractDFG
  #
  # get all other variables
  allvars = lsf(dfg, idfct)

  # prior/unary
  if length(allvars) == 1
    return 1.0
  end

  # general case
  lievars = setdiff(allvars, [varid;])

  # get all other var dimensions with saturation
  len = length(lievars)
  fracs = map(lv->getVariableInferredDimFraction(dfg, lv, true), lievars)

  # the dimension of leave one out variables dictate if this factor can prodive full information on leave out   variable.
  return cumprod(fracs)[end]
end
function getFactorSolvableDimFraction(dfg::G,
                                      fct::DFGFactor,
                                      varid::Symbol,
                                      saturate::Bool=false  )::Float64 where G <: AbstractDFG
  #
  getFactorSolvableDimFraction(dfg,fct.label,varid,saturate)
end

function getFactorSolvableDim(dfg::AbstractDFG,
                              idfct::Symbol,
                              varid::Symbol,
                              saturate::Bool=false  )::Float64
  #
  return getFactorSolvableDimFraction(dfg,idfct,varid,saturate)*getFactorDim(dfg, idfct)
end
function getFactorSolvableDim(dfg::AbstractDFG,
                              fct::DFGFactor,
                              varid::Symbol,
                              saturate::Bool=false  )::Float64
  #
  return getFactorSolvableDimFraction(dfg,fct,varid,saturate)*getFactorDim(fct)
end

"""
    $SIGNATURES

Return the total solvable dimension for each variable in the factor graph `dfg`.

Notes
- "Project" the solved dimension from other variables through connected factors onto each variable.
"""
function getVariableSolvableDim(dfg::AbstractDFG,
                                varid::Symbol,
                                fcts::Vector{Symbol}=ls(dfg, varid) )

  sd = 0.0
  for fc in fcts
    sd += getFactorSolvableDim(dfg,fc,varid)
  end
  return sd
end

## Combined Variables and Factors

"""
    $SIGNATURES

Return the current dimensionality of solve for each variable in a clique.
"""
function getCliqVariableInferDims(dfg::AbstractDFG,
                                  cliq::TreeClique,
                                  saturate::Bool=true,
                                  fraction::Bool=true  )::Dict{Symbol,Float64}
  #
  # which variables
  varids = getCliqAllVarIds(cliq)

  # and what inferred dimension in this dfg
  retd = Dict{Symbol,Float64}()
  for varid in varids
    retd[varid] = getVariableInferredDim(dfg, varid)
  end

  return retd
end

# """
#     $SIGNATURES
#
# Return the directly achievable dimensionality of solve for each variable in a clique.
#
# Related
#
# getFactorSolvableDim
# """
# function getCliqVarPossibleDim(dfg::G,
#                                     cliq::TreeClique,
#                                     saturate::Bool=true,
#                                     fraction::Bool=true  )::Dict{Symbol, Float64}
#   #
#   # variables and factors associated with this clique
#   vars = getCliqAllVarIds(cliq)
#   # fcts = getCliqAllFactIds(cliq)
#   # rows = length(fcts)
#   cols = length(vars)
#
#   # for output result
#   dict = Dict{Symbol,Float64}()
#
#   for j in 1:cols
#     dict[vars[j]] += getVariablePossibleDim(dfg, vars[j])
#   end
#
# end

"""
    $SIGNATURES

Return dictionary of clique variables and percentage of inference completion for each.

Notes
- Completion means (relative to clique subgraph) ratio of inferred dimension over possible solve dimension.

Related

getCliqVariableMoreInitDims
"""
function getCliqVariableInferredPercent(dfg::G, cliq::TreeClique) where G <: AbstractDFG

  # cliq variables factors
  vars = getCliqAllVarIds(cliq)
  # fcts = getCliqAllFactIds(cliq)
  # rows = length(fcts)
  # nvars = length(vars)

  # for output result
  dict = Dict{Symbol,Float64}()

  # possible variable infer dim
  # current variable infer dim
  # calculate ratios in [0,1]
  for var in getCliqAllVarIds(cliq)# 1:nvars
      dict[var] = getVariableInferredDim(dfg, var)
      dict[var] /= getVariablePossibleDim(dfg, var)
  end

  # return dict with result
  return dict
end

"""
    $SIGNATURES

Return a dictionary with the number of immediately additionally available inference
dimensions on each variable in a clique.

Related

getCliqVariableInferredPercent
"""
function getCliqVariableMoreInitDims( dfg::AbstractDFG,
                                      cliq::TreeClique  )
  #
  # cliq variables factors
  vars = getCliqAllVarIds(cliq)

  # for output result
  dict = Dict{Symbol,Float64}()

  # possible variable infer dim
  # current variable infer dim
  for vari in vars
    dict[vari] = getVariableSolvableDim(dfg, vari)
    dict[vari] -= getVariableInferredDim(dfg, vari)
  end

  # return dict with result
  return dict
end

"""
    $SIGNATURES

Return true if the variables solve dimension is equal to the sum of connected factor dimensions.

Related

getVariableInferredDimFraction, getVariableDim, getVariableInferredDim, getVariablePossibleDim
"""
function isCliqFullDim( fg::AbstractDFG,
                        cliq::TreeClique )::Bool
  #
  # get various variable percentages
  red = getCliqVariableInferredPercent(fg, cliq)

  # if all variables are solved to their full potential
  return abs(sum(collect(values(red))) - length(red)) < 1e10
end

"""
    $SIGNATURES

Return a vector of clique index `::Int` for descending order solvable dimensions of each clique.

Notes
- Uses the number of inferable/solvable dimension information in descending order.
- Uses function fetchCliqSolvableDims/getCliqVariableMoreInitDims to retrieved cached values of new solvable/inferable dimensions.

Related

fetchCliqSolvableDims, getCliqVariableMoreInitDims, getSubFgPriorityInitOrder
"""
function getCliqSiblingsPriorityInitOrder(tree::AbstractBayesTree,
                                          prnt::TreeClique,
                                          dwninitmsgs::LikelihoodMessage,
                                          logger=ConsoleLogger() )::Vector{Int}
  #
  sibs = getChildren(tree, prnt)
  len = length(sibs)
  tdims = Vector{Int}(undef, len)
  sidx = Vector{Int}(undef, len)
  for idx in 1:len
    cliqd = getCliqueData(sibs[idx])
    with_logger(logger) do
      @info "$(now()), getCliqSiblingsPriorityInitOrder, sidx=$sidx of $len, $(cliqd.frontalIDs[1]), dwninitmsgs.childSolvDims=$(dwninitmsgs.childSolvDims)"
    end
    flush(logger.stream)
    sidx[idx] = sibs[idx].id

        # NEW accumulate the solvableDims for each sibling (#910)
        # FIXME rather determine this using static tree structure and values from dwnmsg (#910)
        @show sidx, dwninitmsgs.childSolvDims
        # @show haskey(dwninitmsgs.childSolvDims,sidx) ? dwninitmsgs.childSolvDims[sidx] : 0

    # FIXME comment out as part of #910
    sidims = fetchCliqSolvableDims(sibs[idx])
    accmSolDims = collect(values(sidims))
    @show tdims[idx] = sum(accmSolDims)
    with_logger(logger) do
      @info "$(now()), prnt=getCliqSiblingsPriorityInitOrder, sidx=$sidx of $len, tdims[idx]=$(tdims[idx])"
    end
    flush(logger.stream)
  end
  p = sortperm(tdims, rev=true)
  with_logger(logger) do
    @info "getCliqSiblingsPriorityInitOrder, done p=$p"
  end
  return sidx[p]
end

"""
    $SIGNATURES

Return a vector of variable labels `::Symbol` for descending order solvable dimensions of each clique.

Notes
- EXPERIMENTAL, NOT EXPORTED
- Uses the number of inferable/solvable dimension information in descending order.
- Uses function getVariableSolvableDim to retrieved cached values of new solvable/inferable dimensions.

Related

getVariableSolvableDim, getCliqSiblingsPriorityInitOrder
"""
function getSubFgPriorityInitOrder(sfg::G, logger=ConsoleLogger()) where G <: AbstractDFG

    vars = ls(sfg)
    len = length(vars)
    tdims = Vector{Int}(undef, len)
    for idx in 1:len
      tdims[idx] = getVariableSolvableDim(sfg, vars[idx])
    end
    p = sortperm(tdims, rev=true)
    with_logger(logger) do
      @info "getSubFgPriorityInitOrder -- ordered vars=$(vars[p])"
      @info "getSubFgPriorityInitOrder -- ordered tdims=$(tdims[p])"
    end
    return vars[p]
end


text:
using Makie
using Rotations

function trans(x,y,z)
    urange=-π:0.1π:π
    rs = RotZ.(urange)
    rs = transpose.(rs)
    rs = hcat(rs...)
    [x y z] * rs
end

function torus()
    s=Scene()
    t=4.
    r=1.
    param = -π:0.1π:π
    N=length(param)
    x = t .+ r .* cos.(param)
    y = zeros(size(x))
    z = t .+ r .* sin.(param)
    N=length(x)
    ret=trans(x,y,z)
    ret=reshape(ret,N,3,Int(size(ret)[2]//3))
    ret=permutedims(ret,(1,3,2))
    X=ret[:,:,1]
    Y=ret[:,:,2]
    Z=ret[:,:,3]
    scatter!(s,vcat(X...),vcat(Y...),vcat(Z...))
    #X,Y,Z=hcat(X...),hcat(Y...),hcat(Z...)
    for i in 1:size(X)[1]
        lines!(s,X[i,:],Y[i,:],Z[i,:])
    end

    for j in 1:size(X)[2]
        lines!(s,X[:,j],Y[:,j],Z[:,j])
    end

    s |> display

end

torus()

max_stars_repo_name: terasakisatoshi/juliaExer

max_issues_repo_name: terasakisatoshi/juliaExer

max_forks_repo_name: terasakisatoshi/juliaExer


text:
test_doc(generated, docstring) = @test deconstruct(generated)[:docstring] == docstring
test_doc(spec::SpecFunc, docstring) = test_doc(document(spec, wrap(spec)), docstring)
test_doc(spec::SpecStruct, docstring) = test_doc(document(spec, add_constructor(spec)), docstring)

@testset "Generated documentation" begin
    test_doc(
        struct_by_name(:VkExtent2D),
        """

            Extent2D(width::Integer, height::Integer)

        Arguments:
        - `width::Integer`
        - `height::Integer`

        """,
    )

    test_doc(
        struct_by_name(:VkInstanceCreateInfo),
        """

            InstanceCreateInfo(enabled_layer_names::AbstractArray{<:AbstractString}, enabled_extension_names::AbstractArray{<:AbstractString}; next = C_NULL, flags = 0, application_info = C_NULL)

        Arguments:
        - `enabled_layer_names::AbstractArray{<:AbstractString}`
        - `enabled_extension_names::AbstractArray{<:AbstractString}`
        - `next`: defaults to `C_NULL`
        - `flags`: defaults to `0`
        - `application_info`: defaults to `C_NULL`

        """,
    )

    test_doc(
        func_by_name(:vkEnumerateInstanceExtensionProperties),
        """

            enumerate_instance_extension_properties(; layer_name = C_NULL)::Result{Vector{ExtensionProperties}, VulkanError}

        Return codes:
        - Error:
          - `VK_ERROR_OUT_OF_HOST_MEMORY`
          - `VK_ERROR_OUT_OF_DEVICE_MEMORY`
          - `VK_ERROR_LAYER_NOT_PRESENT`

        Arguments:
        - `layer_name`: defaults to `C_NULL`

        """,
    )

    test_doc(
        func_by_name(:vkDestroyDevice),
        """

            destroy_device(device::Device; allocator = C_NULL)::Cvoid

        Arguments:
        - `device::Device` (externsync)
        - `allocator`: defaults to `C_NULL`

        """,
    )

    test_doc(
        func_by_name(:vkGetPipelineCacheData),
        """

            get_pipeline_cache_data(device::Device, pipeline_cache::PipelineCache)::Result{Tuple{UInt, Ptr{Cvoid}}, VulkanError}

        Return codes:
        - Error:
          - `VK_ERROR_OUT_OF_HOST_MEMORY`
          - `VK_ERROR_OUT_OF_DEVICE_MEMORY`

        Arguments:
        - `device::Device`
        - `pipeline_cache::PipelineCache`

        !!! warning
            The pointer returned by this function holds memory owned by Julia. It is therefore **your** responsibility to free it after use (e.g. with `Libc.free`).

        """,
    )

    test_doc(
        func_by_name(:vkWriteAccelerationStructuresPropertiesKHR),
        """

            write_acceleration_structures_properties_khr(device::Device, acceleration_structures::AbstractArray{<:AccelerationStructureKHR}, query_type::VkQueryType, data_size::Integer, data::Ptr{Cvoid}, stride::Integer)::Result{VkResult, VulkanError}

        Return codes:
        - Error:
          - `VK_ERROR_OUT_OF_HOST_MEMORY`
          - `VK_ERROR_OUT_OF_DEVICE_MEMORY`

        Arguments:
        - `device::Device`
        - `acceleration_structures::AbstractArray{<:AccelerationStructureKHR}`
        - `query_type::VkQueryType`
        - `data_size::Integer`
        - `data::Ptr{Cvoid}` (must be a valid pointer with `data_size` bytes)
        - `stride::Integer`

        """,
    )
end