text:
#############################################################
# Lidar Sensor
#TODO move to a separate repo (AutomotiveSensors.jl)

mutable struct LidarSensor
    angles::Vector{Float64}
    ranges::Vector{Float64}
    range_rates::Vector{Float64}
    max_range::Float64
    poly::ConvexPolygon
end
function LidarSensor(nbeams::Int;
    max_range::Float64=100.0,
    angle_offset::Float64=0.0,
    angle_spread::Float64=2*pi,
    )

    if nbeams > 1
        angles = collect(range(angle_offset-angle_spread/2, stop=angle_offset + angle_spread/2, length=nbeams+1))[2:end]
    else
        angles = Float64[]
        nbeams = 0
    end

    ranges = Array{Float64}(undef, nbeams)
    range_rates = Array{Float64}(undef, nbeams)
    LidarSensor(angles, ranges, range_rates, max_range, ConvexPolygon(4))
end
nbeams(lidar::LidarSensor) = length(lidar.angles)
function observe!(lidar::LidarSensor, scene::Scene, roadway::Roadway, vehicle_index::Int)
    state_ego = scene[vehicle_index].state
    egoid = scene[vehicle_index].id
    ego_vel = polar(state_ego.v, state_ego.posG.θ)

    # precompute the set of vehicles within max_range of the vehicle
    in_range_ids = Set()
    for veh in scene
        if veh.id != egoid
            distance = norm(VecE2(state_ego.posG - veh.state.posG))
            # account for the length and width of the vehicle by considering
            # the worst case where their maximum radius is aligned
            distance = distance - hypot(veh.def.length/2.,veh.def.width/2.)
            if distance < lidar.max_range
                push!(in_range_ids, veh.id)
            end
        end
    end

    # compute range and range_rate for each angle
    for (i,angle) in enumerate(lidar.angles)
        ray_angle = state_ego.posG.θ + angle
        ray_vec = polar(1.0, ray_angle)
        ray = VecSE2(state_ego.posG.x, state_ego.posG.y, ray_angle)

        range = lidar.max_range
        range_rate = 0.0
        for veh in scene
            # only consider the set of potentially in range vehicles
            if in(veh.id, in_range_ids)
                to_oriented_bounding_box!(lidar.poly, veh)

                range2 = AutomotiveDrivingModels.get_collision_time(ray, lidar.poly, 1.0)
                if !isnan(range2) && range2 < range
                    range = range2
                    relative_speed = polar(veh.state.v, veh.state.posG.θ) - ego_vel
                    range_rate = proj(relative_speed, ray_vec, Float64)
                end
            end
        end
        lidar.ranges[i] = range
        lidar.range_rates[i] = range_rate
    end

    lidar
end
# function render_lidar!(rendermodel::RenderModel, lidar::LidarSensor, posG::VecSE2{Float64};
#     color::Colorant=colorant"white",
#     line_width::Float64 = 0.05,
#     )

#     for (angle, range, range_rate) in zip(lidar.angles, lidar.ranges, lidar.range_rates)
#         ray = VecSE2(posG.x, posG.y, posG.θ + angle)
#         t = 2 / (1 + exp(-range_rate))
#         ray_color = t > 1.0 ? lerp(RGBA(1.0,1.0,1.0,0.5), RGBA(0.2,0.2,1.0,0.5), t/2) :
#                               lerp(RGBA(1.0,0.2,0.2,0.5), RGBA(1.0,1.0,1.0,0.5), t)
#         render_ray!(rendermodel::RenderModel, ray, color=ray_color, length=range, line_width=line_width)
#     end
#     rendermodel
# end

#############################################################
# Road Line Lidar Sensor

mutable struct RoadlineLidarSensor
    angles::Vector{Float64}
    ranges::Matrix{Float64} # [n_lanes by nbeams]
    max_range::Float64
    poly::ConvexPolygon
end
nbeams(lidar::RoadlineLidarSensor) = length(lidar.angles)
nlanes(lidar::RoadlineLidarSensor) = size(lidar.ranges, 1)
function RoadlineLidarSensor(nbeams::Int;
    max_range::Float64=100.0,
    max_depth::Int=2,
    angle_offset::Float64=0.0,
    )

    if nbeams > 1
        angles = collect(range(angle_offset, stop=2*pi+angle_offset, length=nbeams+1))[2:end]
    else
        angles = Float64[]
        nbeams = 0
    end
    ranges = Array{Float64}(undef, max_depth, nbeams)
    RoadlineLidarSensor(angles, ranges, max_range, ConvexPolygon(4))
end
function _update_lidar!(lidar::RoadlineLidarSensor, ray::VecSE2{Float64}, beam_index::Int, p_lo::VecE2, p_hi::VecE2)
    test_range = get_intersection_time(Projectile(ray, 1.0),
        AutomotiveDrivingModels.LineSegment(p_lo, p_hi))

    if !isnan(test_range)
        n_ranges = size(lidar.ranges, 1)
        for k in 1 : n_ranges
            if test_range < lidar.ranges[k,beam_index]
                for l in k+1 : n_ranges
                    lidar.ranges[l,beam_index] = lidar.ranges[l-1,beam_index]
                end
                lidar.ranges[k,beam_index] = test_range
                break
            end
        end
    end

    lidar
end
function _update_lidar!(lidar::RoadlineLidarSensor, ray::VecSE2{Float64}, beam_index::Int, lane::Lane, roadway::Roadway; check_right_lane::Bool=false)
    halfwidth = lane.width/2
    Δ = check_right_lane ? -π/2 : π/2
    p_lo = convert(VecE2, lane.curve[1].pos + polar(halfwidth, lane.curve[1].pos.θ + Δ))
    for j in 2:length(lane.curve)
        p_hi = convert(VecE2, lane.curve[j].pos + polar(halfwidth, lane.curve[j].pos.θ + Δ))
        _update_lidar!(lidar, ray, beam_index, p_lo, p_hi)
        p_lo = p_hi
    end
    if has_next(lane)
        lane2 = next_lane(lane, roadway)
        pt = lane2.curve[1]
        p_hi = convert(VecE2, pt.pos + polar(lane2.width/2, pt.pos.θ + Δ))
        _update_lidar!(lidar, ray, beam_index, p_lo, p_hi)
    end
    lidar
end
function _update_lidar!(lidar::RoadlineLidarSensor, ray::VecSE2{Float64}, beam_index::Int, roadway::Roadway)

    for seg in roadway.segments
        for lane in seg.lanes

            # always check the left lane marking
            _update_lidar!(lidar, ray, beam_index, lane, roadway)

            # only check the right lane marking if this is the first lane
            if lane.tag.lane == 1
                _update_lidar!(lidar, ray, beam_index, lane, roadway, check_right_lane=true)
            end
        end
    end

    lidar
end
function observe!(lidar::RoadlineLidarSensor, scene::Scene, roadway::Roadway, vehicle_index::Int)
    state_ego = scene[vehicle_index].state
    egoid = scene[vehicle_index].id
    ego_vel = polar(state_ego.v, state_ego.posG.θ)

    fill!(lidar.ranges, lidar.max_range)
    for (beam_index,angle) in enumerate(lidar.angles)
        ray_angle = state_ego.posG.θ + angle
        ray = VecSE2(state_ego.posG.x, state_ego.posG.y, ray_angle)

        _update_lidar!(lidar, ray, beam_index, roadway)
    end

    lidar
end
# function render_lidar!(rendermodel::RenderModel, lidar::RoadlineLidarSensor, posG::VecSE2{Float64};
#     color::Colorant=RGBA(1.0,1.0,1.0,0.5),
#     line_width::Float64 = 0.05,
#     depth_level::Int = 1, # if 1, render first lanes struck by beams, if 2 render 2nd ...
#     )

#     for (angle, range) in zip(lidar.angles, lidar.ranges[depth_level,:])
#         ray = VecSE2(posG.x, posG.y, posG.θ + angle)
#         render_ray!(rendermodel::RenderModel, ray, color=color, length=range, line_width=line_width)
#     end
#     rendermodel
# end

#############################################################
# Road Line Lidar Culling

struct LanePortion
    tag::LaneTag
    curveindex_lo::Int
    curveindex_hi::Int
end
mutable struct RoadwayLidarCulling
    is_leaf::Bool

    x_lo::Float64
    x_hi::Float64
    y_lo::Float64
    y_hi::Float64

    top_left::RoadwayLidarCulling
    top_right::RoadwayLidarCulling
    bot_left::RoadwayLidarCulling
    bot_right::RoadwayLidarCulling

    lane_portions::Vector{LanePortion}

    function RoadwayLidarCulling(x_lo::Float64, x_hi::Float64, y_lo::Float64, y_hi::Float64, lane_portions::Vector{LanePortion})
        retval = new()
        retval.is_leaf = true
        retval.x_lo = x_lo
        retval.x_hi = x_hi
        retval.y_lo = y_lo
        retval.y_hi = y_hi
        retval.lane_portions = lane_portions
        retval
    end
    function RoadwayLidarCulling(x_lo::Float64, x_hi::Float64, y_lo::Float64, y_hi::Float64,
                                 top_left::RoadwayLidarCulling,
                                 top_right::RoadwayLidarCulling,
                                 bot_left::RoadwayLidarCulling,
                                 bot_right::RoadwayLidarCulling,)
        retval = new()
        retval.is_leaf = false
        retval.x_lo = x_lo
        retval.x_hi = x_hi
        retval.y_lo = y_lo
        retval.y_hi = y_hi
        retval.top_left = top_left
        retval.top_right = top_right
        retval.bot_left = bot_left
        retval.bot_right = bot_right
        retval
    end
end
RoadwayLidarCulling() = RoadwayLidarCulling(typemax(Float64), typemin(Float64), typemax(Float64), typemin(Float64), LanePortion[])

function Base.get(rlc::RoadwayLidarCulling, x::Real, y::Real)
    if rlc.is_leaf
        return rlc
    else
        if y < (rlc.y_lo + rlc.y_hi)/2
            if x < (rlc.x_lo + rlc.x_hi)/2
                return get(rlc.bot_left, x, y)
            else
                return get(rlc.bot_right, x, y)
            end
        else
            if x < (rlc.x_lo + rlc.x_hi)/2
                return get(rlc.top_left, x, y)
            else
                return get(rlc.top_right, x, y)
            end
        end
    end
end
function ensure_leaf_in_rlc!(rlc::RoadwayLidarCulling, x::Real, y::Real, fidelity_x::Real, fidelity_y::Real)

    leaf = get(rlc, x, y)
    @assert(leaf.is_leaf)

    x_lo, x_hi = leaf.x_lo, leaf.x_hi
    y_lo, y_hi = leaf.y_lo, leaf.y_hi
    while (x_hi - x_lo) > fidelity_x || (y_hi - y_lo) > fidelity_y # drill down

        x_mid = (x_hi + x_lo)/2
        y_mid = (y_hi + y_lo)/2

        leaf.top_left  = RoadwayLidarCulling(x_lo, x_mid, y_mid, y_hi, LanePortion[])
        leaf.top_right = RoadwayLidarCulling(x_mid, x_hi, y_mid, y_hi, LanePortion[])
        leaf.bot_left  = RoadwayLidarCulling(x_lo, x_mid, y_lo, y_mid, LanePortion[])
        leaf.bot_right = RoadwayLidarCulling(x_mid, x_hi, y_lo, y_mid, LanePortion[])
        leaf.is_leaf = false

        leaf = get(leaf, x, y)
        x_lo, x_hi = leaf.x_lo, leaf.x_hi
        y_lo, y_hi = leaf.y_lo, leaf.y_hi
    end

    rlc
end
function get_lane_portions(roadway::Roadway, x::Real, y::Real, lane_portion_max_range::Float64)
    P = VecE2(x, y)
    Δ² = lane_portion_max_range*lane_portion_max_range

    lane_portions = LanePortion[]

    for seg in roadway.segments
        for lane in seg.lanes
            f = curvept -> normsquared(VecE2(curvept.pos - P)) ≤ Δ²
            i = findfirst(f, lane.curve)
            if i != nothing
                j = findlast(f, lane.curve)
                @assert(j != nothing)
                push!(lane_portions, LanePortion(lane.tag, i, j))
            end
        end
    end

    lane_portions
end

function RoadwayLidarCulling(
    roadway::Roadway,
    lane_portion_max_range::Float64, # lane portions will be extracted such that points within lane_portion_max_range are in the leaves
    culling_fidelity::Float64, # the culling will drill down until Δx, Δy < culling_fidelity
    )

    #=
    1 - get area bounds
    2 - ensure all points in rlc
    3 - for each leaf, construct all lane portions
    =#

    # get area bounds
    x_lo = typemax(Float64)
    x_hi = typemin(Float64)
    y_lo = typemax(Float64)
    y_hi = typemin(Float64)
    for seg in roadway.segments
        for lane in seg.lanes
            for curvept in lane.curve
                pos = curvept.pos
                x_lo = min(pos.x, x_lo)
                x_hi = max(pos.x, x_hi)
                y_lo = min(pos.y, y_lo)
                y_hi = max(pos.y, y_hi)
            end
        end
    end
    x_lo -= lane_portion_max_range
    x_hi += lane_portion_max_range
    y_lo -= lane_portion_max_range
    y_hi += lane_portion_max_range
    root = RoadwayLidarCulling(x_lo, x_hi, y_lo, y_hi, LanePortion[])

    # ensure all points are in rlc
    x = x_lo - culling_fidelity
    while x < x_hi
        x += culling_fidelity
        y = y_lo - culling_fidelity
        while y < y_hi
            y += culling_fidelity
            ensure_leaf_in_rlc!(root, x, y, culling_fidelity, culling_fidelity)
        end
    end

    nodes = RoadwayLidarCulling[root]
    while !isempty(nodes)
        node = pop!(nodes)
        if node.is_leaf
            x = (node.x_lo + node.x_hi)/2
            y = (node.y_lo + node.y_hi)/2
            append!(node.lane_portions, get_lane_portions(roadway, x, y, lane_portion_max_range))
        else
            push!(nodes, node.top_left)
            push!(nodes, node.top_right)
            push!(nodes, node.bot_left)
            push!(nodes, node.bot_right)
        end
    end

    root
end

function _update_lidar!(
    lidar::RoadlineLidarSensor,
    ray::VecSE2{Float64},
    beam_index::Int,
    roadway::Roadway,
    lane_portion::LanePortion;
    check_right_lane::Bool=false
    )

    lane = roadway[lane_portion.tag]

    halfwidth = lane.width/2
    Δ = check_right_lane ? -π/2 : π/2
    p_lo = convert(VecE2, lane.curve[lane_portion.curveindex_lo].pos +
                          polar(halfwidth, lane.curve[lane_portion.curveindex_lo].pos.θ + Δ))
    for j in lane_portion.curveindex_lo+1:lane_portion.curveindex_hi
        p_hi = convert(VecE2, lane.curve[j].pos + polar(halfwidth, lane.curve[j].pos.θ + Δ))
        _update_lidar!(lidar, ray, beam_index, p_lo, p_hi)
        p_lo = p_hi
    end
    if lane_portion.curveindex_hi == length(lane.curve) && has_next(lane)
        lane2 = next_lane(lane, roadway)
        pt = lane2.curve[1]
        p_hi = convert(VecE2, pt.pos + polar(lane2.width/2, pt.pos.θ + Δ))
        _update_lidar!(lidar, ray, beam_index, p_lo, p_hi)
    end
    lidar
end
function _update_lidar!(lidar::RoadlineLidarSensor, ray::VecSE2{Float64}, beam_index::Int, roadway::Roadway, rlc::RoadwayLidarCulling)

    leaf = get(rlc, ray.x, ray.y)
    for lane_portion in leaf.lane_portions

        # always update the left lane marking
        _update_lidar!(lidar, ray, beam_index, roadway, lane_portion)

        # only check the right lane marking if this is the first lane
        if lane_portion.tag.lane == 1
            _update_lidar!(lidar, ray, beam_index, roadway, lane_portion, check_right_lane=true)
        end
    end
    lidar
end
function observe!(lidar::RoadlineLidarSensor, scene::Scene, roadway::Roadway, vehicle_index::Int, rlc::RoadwayLidarCulling)
    state_ego = scene[vehicle_index].state
    egoid = scene[vehicle_index].id
    ego_vel = polar(state_ego.v, state_ego.posG.θ)

    fill!(lidar.ranges, lidar.max_range)
    for (beam_index,angle) in enumerate(lidar.angles)
        ray_angle = state_ego.posG.θ + angle
        ray = VecSE2(state_ego.posG.x, state_ego.posG.y, ray_angle)

        _update_lidar!(lidar, ray, beam_index, roadway, rlc)
    end

    lidar
end


text:
using Base.Test

using PEGParser
using PEGParser: AndRule, IntegerRule, FloatRule
import Base.float
using Compat

function test_default_or_action()
  @grammar grammar begin
    start = "a" | "b"
  end

  (ast, pos, error) = parse(grammar, "a")

  # default_or_action returns the one child from the or-rule
  @test length(ast.children) == 0
  @test ast.value == "a"
end

function test_custom_action1()
	@grammar grammar begin
		# transform to just the integer value
		start = ("value:" + integer){ parse(Int, _2.value) }
	end

	(ast, pos, error) = parse(grammar, "value:5")

	# AST was convert to a single integer
	@test ast == 5
end

function test_custom_action2()
	@grammar grammar begin
		start = (integer + "::integer"){ parse(Int, _1.value) } |
				(float + "::float"){ parse(Float64, _1.value) }
	end

	(ast, pos, error) = parse(grammar, "5::integer")
	@test typeof(ast) == Int
	@test ast == 5

	(ast, pos, error) = parse(grammar, "5.0::float")
	@test typeof(ast) == Float64
	@test ast == 5.0
end

function test_custom_action3()
	@grammar grammar begin
		start = ((integer + "::integer") | (float + "::float")){ _1 }
	end

	(ast, pos, error) = parse(grammar, "5::integer")

	# should have the first and-rule
	@test ast.ruleType == AndRule
	@test length(ast.children) == 2
	@test ast.children[1].ruleType == IntegerRule
	@test ast.children[1].value == "5"

	(ast, pos, error) = parse(grammar, "5.0::float")
	@test ast.ruleType == AndRule
	@test length(ast.children) == 2
	@test ast.children[1].ruleType == FloatRule
	@test ast.children[1].value == "5.0"
end

function test_custom_action4()
	@grammar grammar begin
		start = ((r"[a-zA-Z][a-zA-Z0-9_]*" + "::integer"){ symbol(_1.value) } +
			"=" + integer{ parse(Int, _0) }){ (_1, _3) }
	end

	(ast, pos, error) = parse(grammar, "foo2::integer=5")
	@test length(ast) == 2
	@test ast[1] == :foo2
	@test ast[2] == 5
end

test_default_or_action()
test_custom_action1()
test_custom_action2()
test_custom_action3()
test_custom_action4()

ext: jl


text:
# This code to be inserted into raytracer.jl with include("rays.jl")

struct Ray
    origin::Point3
    direction::Vector3
    tmin::Float64
    tmax::Float64
    depth::Int64
end

Ray(origin::Point3, direction::Vector3) = Ray(origin, direction, 0.0, Inf, 1)
Ray(parent::Ray, origin::Point3, direction::Vector3) = Ray(origin, direction, 0.0, Inf, parent.depth+1)

(R::Ray)(t::Real) = R.origin + t*R.direction

function (transform::Transformation)(R::Ray)
    return Ray(transform(R.origin), transform(R.direction), R.tmin, R.tmax, R.depth)
end

########################################################
# Ray generators (for radiation pressure computations)

"""
    ray_parallel(radius::Real)

Generate a ray along the -Z axis at a random point on a disk of given radius in the XY
plane.

"""
function ray_parallel(radius::Real)
    return ray_parallel(radius, rand(), rand())
end

"""
    ray_parallel(radius::Real, u1::Real, u2::Real)

Generate a ray along the -Z axis at a random point on a disk of given radius in the XY
plane.

This is the deterministic version of the function, taking two numbers `u1` and `u2` which
should be uniform random numbers in the [0, 1] interval to generate uniformly sampled
points on the disk.

"""
function ray_parallel(radius::Real, u1::Real, u2::Real)
    r = sqrt(u1) * radius
    phi = 2π * u2
    ray_origin = Point3(r * cos(phi), r * sin(phi), radius + 1.0)
    return Ray(ray_origin, -Z_AXIS)
end

"""
    ray_from_point(radius::Real, distance::Real, u1::Real, u2::Real)

Generate a Ray from a point which is the given distance on the Z axis, at a random point
on a sphere of given radius around the origin.

"""
function ray_from_point(radius::Real, distance::Real)
    return ray_from_point(radius, distance, rand(), rand())
end

"""
    ray_from_point(radius::Real, distance::Real, u1::Real, u2::Real)

Generate a Ray from a point which is the given distance on the Z axis, at a point on a
sphere of given radius around the origin.

This is the deterministic version of the function, taking two numbers `u1` and `u2` which
should be uniform random numbers in the [0, 1] interval to generate uniformly sampled
points in the cone from the distance to the visible cross section of the sphere.

"""
function ray_from_point(radius::Real, distance::Real, u1::Real, u2::Real)
    cos_th = sqrt(1.0 - (radius / distance)^2)
    z = cos_th + (1.0 - cos_th) * u1
    phi = 2π * u2
    r = sqrt(1.0 - z^2)
    direction = Vector3(r*cos(phi), r*sin(phi), -z)
    return Ray(Point3(0.0, 0.0, distance), direction)
end

max_stars_repo_name: dronir/GRAYTR

max_issues_repo_name: dronir/GRAYTR

max_forks_repo_name: dronir/GRAYTR


text:
using PEGParser

@grammar calc1 begin
  start = (number + op + number){
  	eval(_2)(_1, _3)
  }

  op = plus | minus
  number = (-space + r"[0-9]+"){parse(Int, _1.value)}
  plus = (-space + "+"){symbol(_1.value)}
  minus = (-space + "-"){symbol(_1.value)}
  space = r"[ \t\n\r]*"
end

data = "4+5"
(ast, pos, error) = parse(calc1, data)
println(ast)


text:
function  echteCTDaten(Aufloesung)
#Daten1 = load("../Computertomographie_Bilder/Bild48log.jpg");
Daten1 = load("../Computertomographie_Bilder/Bild48log2.jpg");
Daten = Gray.(Daten1)
    #print(Daten2)
out = convert(Array{Float64,2}, Daten); #convert GrayMatrix into Float64, bei Vektor müsste die 2 durch eine 1ersetzt werden
Summe = zeros(Aufloesung,Aufloesung);
Durchschnitt = zeros(Aufloesung,Aufloesung);
Laenge = size(out,2)/Aufloesung;
SummeZeile = zeros(Int(Laenge),1);

if Aufloesung == 2 || Aufloesung == 4 || Aufloesung == 8 || Aufloesung == 16 || Aufloesung == 32 || Aufloesung == 64 || Aufloesung == 128 || Aufloesung == 256 || Aufloesung == 512
    for i = 1:Aufloesung #geht die Zeilen durch
        for j = 1:Aufloesung #geht die Spalten durch
            a = Int((i-1)*Laenge+1);
            b = Int(i*Laenge);
            c = Int((j-1)*Laenge+1);
            d = Int(j*Laenge);
            Matrix = out[a:b,c:d];
            SummeZeile = sum(Matrix, dims=2); # Jede Zeile wird aufaddiert und in die entsprechende Zeile des SummeZeilevektors geschrieben
            Summe = sum(SummeZeile,dims=1)/(Laenge^2);# Einträge von SummeZeile werden aufaddiertund normiert
            Durchschnitt[i,j] = Summe[1,1];
        end
    end
     #imshow(Durchschnitt)#;truesize([200 200]);
else
    print("Die Auflösung muss eine Zweierpotenz sein.");
end

zurueck = convert(Array{Gray{Normed{UInt8,8}},2},Durchschnitt)
return zurueck;

end


text:
"""

anki_find_notes(tag::Array)

This

"""
function anki_find_notes(tag::Array)
    tag_query = ""
    for t in tag
        tag_query = tag_query * " tag:$t"
    end
    response = HTTP.request(
        :POST,
        "http://localhost:8765",
        [],
        """{
\"action\": \"findNotes\",
\"version\": 6,
\"params\": {
	\"query\": \"$tag_query\"
	}
}""",
    )
    String(response.body) |> JSON.parse
end

"""

anki_find_note(tag::String)

This

"""
function anki_find_note(tag::String)
    tags = anki_get_tags()
    findfirst(==(tag), tags) |> ind -> deleteat!(tags, ind)
    tag_query = "tag:$tag"
    for t in tags
        tag_query = tag_query * " -tag:$t"
    end
    response = HTTP.request(
        :POST,
        "http://localhost:8765",
        [],
        """{
\"action\": \"findNotes\",
\"version\": 6,
\"params\": {
	\"query\": \"$tag_query\"
	}
}""",
    )
    String(response.body) |> JSON.parse
end

"""

anki_remove_tag(card_id::Int, tag::String)

"""
function anki_remove_tag(card_id, tag)
    tag_query = ""
    for t in tag
        tag_query = tag_query * "$t "
    end

    if length(card_id) == 1
        card_id = "[$card_id]"
    end

    response = HTTP.request(
        :POST,
        "http://localhost:8765",
        [],
        """{
       \"action\": \"removeTags\",
       \"version\": 6,
       \"params\": {
       	\"notes\": $card_id,
       	\"tags\": \"$tag_query\"
       	}
       }""",
    )
    String(response.body) |> JSON.parse
end

"""

anki_suspend_card(card_id::Array{Int64})

Suspends an array of given card IDs within Anki.

"""
function anki_suspend_card(card_id::Array{Int64})
    response = HTTP.request(
        :POST,
        "http://localhost:8765",
        [],
        """{
\"action\": \"suspend\",
\"version\": 6,
\"params\": {
	\"cards\": $card_id
	}
}""",
    )
end


text:
using Test
using TreeTools

println("##### splits #####")

nwk1 = "(((A1,A2),(B1,B2),(C1,C2)),D,E)"
t = node2tree(TreeTools.parse_newick(nwk1)) #
S = SplitList(t)

@testset "1" begin
	expected = [
		["A1", "A2"],
		["B1", "B2"],
		["C1", "C2"],
		["A1", "A2", "B1", "B2", "C1", "C2"],
		["A1", "A2", "B1", "B2", "C1", "C2", "D", "E"]
	]
	@test [leaves(S,i) for i in eachindex(S)] == expected
	@test isequal(S, expected)
	@test S == expected
end

s4 = S.splitmap["NODE_4"] # ["B1", "B2"] ~ [3,4]
s5 = S.splitmap["NODE_5"] # ["C1", "C2"] ~ [5,6]
s45 = TreeTools.joinsplits(s4,s5)
@testset "2" begin
	@test s4 == Split([3,4])
	@test s4 != Split([3,4,5])
	@test in(s4, S)
	@test in(s5, S)
	@test !in(s45, S)
	@test s45.dat == Int[3,4,5,6]
	@test !isnothing(findfirst(==(s5), S.splits))
	@test isnothing(findfirst(==(s45), S.splits))
end

@testset "3" begin
	@test !TreeTools.isroot(S.splitmap["NODE_4"], S.mask)
	@test !TreeTools.isroot(S.splitmap["NODE_2"], S.mask)
	@test TreeTools.isroot(S.splitmap["NODE_1"], S.mask)
	@testset for n in internals(t)
		@test !TreeTools.isleaf(S.splitmap[n.label])
	end
end

@testset "4" begin
	@testset for s in S, t in S
		@test arecompatible(s,t)
		@test arecompatible(s,t,S.mask)
		@test arecompatible(s,t,rand(Bool,8))
	end
	@test arecompatible(s4, s45)
	@test arecompatible(s5, s45)
end

@testset "5" begin
	t1 = Split([1,3])
	t2 = Split([1,2,3])
	t3 = Split([1,3,7,8])
	u = Split([7,8])
	@test !iscompatible(t1, S)
	@test !iscompatible(t2, S)
	@test !iscompatible(t3, S)
	@test iscompatible(u, S)
end

# Let's pretend we found (A1,A2,B1,B2) and (C1,C2,D) to be MCCs
Smcc = SplitList(S.leaves)
append!(Smcc.splits, [Split([1,2,3,4]), Split([5,6,7])])
@testset "6" begin
	Smapped = TreeTools.map_splits_to_tree(Smcc, t)
	@test leaves(Smapped, 1) == ["A1", "A2", "B1", "B2"]
	@test leaves(Smapped, 2) == ["A1", "A2", "B1", "B2", "C1", "C2", "D"]
	@test iscompatible(Smapped[1], S)
	@test iscompatible(Smapped[2], S)
end

# Unions
U = union(S, Smcc)
Sc = deepcopy(S)
union!(Sc, Smcc)
@testset "7" begin
	@test in(Smcc.splits[1], U)
	@test in(Smcc.splits[2], U)
	@test length(U) == length(S) + 2
	@test U == Sc
end

println()


text:
using TetGen
using LinearAlgebra

"""
     random_delaunay(;npoints=20)

Create a Delaunay tetrahedralization  from a random set of points.
This is  a tetrahedralization of the convex hull of the points.
If the points are in general position (which we assume here),
it is unique.
"""
function random_delaunay(;npoints=20)
    input=TetGen.RawTetGenIO{Cdouble}(pointlist=rand(3,npoints))
    tetrahedralize(input, "Q")
end

"""
   cube(;vol=1)

Tetrahedralization of cube witn maximum tetrahedron volume.
"""
function cube(;vol=1)
    input=TetGen.RawTetGenIO{Cdouble}()
    input.pointlist=[0 0 0;
                     1 0 0;
                     1 1 0;
                     0 1 0;
                     0 0 1;
                     1 0 1;
                     1 1 1;
                     0 1 1]'

    TetGen.facetlist!(input,[1 2 3 4;
                             5 6 7 8;
                             1 2 6 5;
                             2 3 7 6;
                             3 4 8 7;
                             4 1 5 8]')
    tetrahedralize(input, "pQa$(vol)")
end

"""
   cubewithhole(;vol=1)

Tetrahedralization of cube witn maximum tetrahedron volume.
"""
function cubewithhole(;vol=1)
    input=TetGen.RawTetGenIO{Cdouble}()
    outerpoints=[-1 -1 -1;
                 1 -1 -1;
                 1 1 -1;
                 -1 1 -1;
                 -1 -1 1;
                 1 -1 1;
                 1 1 1;
                 -1 1 1;]'

    input.pointlist=hcat(outerpoints,outerpoints/2)

    outerfacets=[1 2 3 4;
                 5 6 7 8;
                 1 2 6 5;
                 2 3 7 6;
                 3 4 8 7;
                 4 1 5 8;
                 ]'

    input.holelist=[0 0 0;]'

    TetGen.facetlist!(input,hcat(outerfacets,outerfacets.+8))
    tetrahedralize(input, "pQa$(vol)")
end

"""
   cube_localref()

   Tetrahedralization of cube with local refinement callback
"""

function cube_localref()

    tetunsuitable() do pa,pb,pc,pd
        vol=det(hcat(pb-pa,pc-pa,pd-pa))/6
        center=0.25*(pa+pb+pc+pd)-[0.5,0.5,0.5]
        vol> 0.05*norm(center)^2.5
    end

    input=TetGen.RawTetGenIO{Cdouble}()
    input.pointlist=[0 0 0;
                     1 0 0;
                     1 1 0;
                     0 1 0;
                     0 0 1;
                     1 0 1;
                     1 1 1;
                     0 1 1]'

    TetGen.facetlist!(input,[1 2 3 4;
                             5 6 7 8;
                             1 2 6 5;
                             2 3 7 6;
                             3 4 8 7;
                             4 1 5 8]')
    tetrahedralize(input, "pQa")
end

"""
   prism(;vol=1)

Tetrahedralization of a prism with maximum tetrahedron volume.
"""
function prism(vol=2)
    input=TetGen.RawTetGenIO{Cdouble}()
    input.pointlist=[0 0 0;
                     1 0 0;
                     0 1 0;
                     0 0 1;
                     1 0 1;
                     0 1 1]'

    TetGen.facetlist!(input,[[1,2,3],
                             [4,5,6],
                             [1,2,5,4],
                             [2,3,6,5],
                             [3,1,4,6]])

    tetrahedralize(input, "pQa$(vol)")
end

"""
   prism(;vol1=0.01, vol2=0.2)

Tetrahedralization of a prism with two regions with different tet volumes
"""
function material_prism(;vol1=0.01, vol2=0.1)
    input=TetGen.RawTetGenIO{Cdouble}()

    input.pointlist=[0 0 0;
                     1 0 0;
                     0 1 0;
                     0 0 1;
                     1 0 1;
                     0 1 1;
                     0 0 2;
                     1 0 2;
                     0 1 2]'

    TetGen.facetlist!(input,[[1,2,3],
                             [7,8,9],
                             [1,2,5,4],
                             [2,3,6,5],
                             [3,1,4,6],
                             [1,2,5,4].+3,
                             [2,3,6,5].+3,
                             [3,1,4,6].+3])

    input.facetmarkerlist=[1,2,3,3,3,3,3,3]
    input.regionlist=[0.1 0.1 0.5 1 vol1;
                      0.1 0.1 1.5 2 vol2]'

    tetrahedralize(input, "paAqQ")
end

"""
   cutprism(;vol=0.05)

Tetrahedralization of a prism with  a prism cut out in the middle.
This tests the use of facet holes.
"""
function cutprism(;vol=0.05)
    input=TetGen.RawTetGenIO{Cdouble}()
    input.pointlist=[0 0 0;
                     1 0 0;
                     0 1 0;

                     0 0 10;
                     1 0 10;
                     0 1 10;

                     -1 -1 0;  # 7
                     2 -1 0;
                     2 2 0;
                     -1 2 0;

                     -1 -1 10;  # 11
                     2 -1 10;
                     2 2 10;
                     -1 2 10]'

    push!(input.facetlist,RawFacet([Cint[1, 2, 3],Cint[7, 8, 9, 10]], [0.1 0.1 0.0;]))
    push!(input.facetlist,RawFacet([Cint[4, 5, 6],Cint[11,12,13,14]], [0.1 0.1 1.0;]))
    push!(input.facetlist,RawFacet([Cint[1, 2, 5, 4 ]], Array{Cdouble,2}(undef,0,0)))
    push!(input.facetlist,RawFacet([Cint[2, 3, 6, 5 ]], Array{Cdouble,2}(undef,0,0)))
    push!(input.facetlist,RawFacet([Cint[3, 1, 4, 6 ]], Array{Cdouble,2}(undef,0,0)))
    push!(input.facetlist,RawFacet([Cint[7, 8, 12,11]], Array{Cdouble,2}(undef,0,0)))
    push!(input.facetlist,RawFacet([Cint[8, 9, 13,12]], Array{Cdouble,2}(undef,0,0)))
    push!(input.facetlist,RawFacet([Cint[9, 10,14,13]], Array{Cdouble,2}(undef,0,0)))
    push!(input.facetlist,RawFacet([Cint[10,7, 11,14]], Array{Cdouble,2}(undef,0,0)))

    input.facetmarkerlist=[1,2,3,3,3,4,5,6,7]
    input.regionlist=[ -0.1 -0.1 0.1 1 vol;]'
    tetrahedralize(input, "paqAQ")
end


text:
"""
    SpikeTrains(TT, I;
        rec_start, rec_finish, stim_start, stim_finish,
        kwargs...)

A wrapper object for spike trains. `TT` are the spike times, `I` are the
indices/identities of the neurons.

"""
mutable struct SpikeTrains
    TT::Vector{Vector{Float64}}
    # I::Vector{Int} # move this to metadata
    rec_start::Float64  # usually just 0.0
    rec_finish::Float64 # defaults to maximum(map(maximum,TT))
    stim_start::Float64 # the start and end times of stimulus presentation
    stim_finish::Float64
    metadata::Dict{Any,Any}

    function SpikeTrains(TT;
        stim_start=0.0, stim_finish=maximum(map(maximum, TT)),
        rec_start=0.0, rec_finish=max(stim_finish, maximum(map(maximum, TT))),
        kwargs...)
        # use I to select which spike trains to keep, and while we're at it make
        # sure that TT ends up being the right type.
        # trains = [map(Float64, TT[i][rec_start .< TT[i] .< rec_finish]) for i in I]
        trains = [map(Float64, T[rec_start .< T .< rec_finish]) for T in TT]
        new(trains, rec_start, rec_finish, stim_start, stim_finish, Dict(kwargs))
    end
end

n_cells(ST::SpikeTrains) = length(ST.TT)

function show(io::IO, ST::SpikeTrains)
    println(io, "Spike times for $(n_cells(ST)) neurons.")
    println(io, "Recording start/finish: $(ST.rec_start) / $(ST.rec_finish) ")
    println(io, "Stimulus start/finish:  $(ST.stim_start) / $(ST.stim_finish)")
    show_metadata(io,ST)
end

"""
    histogram(ST, binsize; t0=ST.t_i, tmax=ST.t_f, N_bins=ceil(Int,(tmax - t0)/binsize))::Matrix{Float64}

Returns a `N_bins` by `N_cells` matrix where the `i,j`th entry is the number of
spikes that occurred in bin `i` from neuron `j`. By default returns a histogram
of spikes that happened during stimulus presentation. To truncate/extend the
histogram, specify `N_bins`. Truncation ignores spikes that happen later than
the end of the bins; extension pads with 0s. `binsize` is in seconds.

"""
function histogram(ST::SpikeTrains, binsize::Float64;
    t0::Float64=ST.stim_start, tmax::Float64=ST.stim_finish,
    N_bins::Int=ceil(Int,(tmax - t0)/binsize))

    N_cells = n_cells(ST)
    # N_bins = ceil(Int,(tmax - t0)/binsize)
    spike_hist = zeros(N_bins, N_cells)
    for (j,S) = enumerate(ST.TT)
        Stilde = ceil.(Int, S[t0 .< S .< tmax]/binsize)
        max_idx = max(maximum(Stilde), N_bins)
        spike_hist[:,j] = sparsevec(Stilde, ones(length(Stilde)), max_idx)[1:N_bins]
        # sparsevec has a nice constructor that automatically does the counting
        # I want, so...
        # spike_hist[:,j] = sparsevec(ceil(Int, Stilde/binsize), ones(length(Stilde)), N_bins)
    end
    return spike_hist
end

"""
    raster(ST, binsize; t0, tmax)::BitMatrix

Returns `histogram(ST, binsize; t0=t0, tmax=tmax) .> 0`
"""
raster(ST::SpikeTrains, binsize::Float64; t0::Float64=ST.stim_start, tmax::Float64=ST.stim_finish, N_bins::Int=ceil(Int,(tmax - t0)/binsize)) = histogram(ST, binsize; t0=t0, tmax=tmax, N_bins=N_bins) .> 0


text:
module ALifeBenchmark

# export RandomModel
# export run_simulation
#
# export History
# export get_default_history, get_detailed_history, get_by_environment
#
# export get_total_fitness_variance, get_abiotic_variance, get_mutational_variance, get_biotic_variance, get_inherent_variance

# Analysis

using SimpleWeightedGraphs: induced_subgraph, adjacency_matrix
export load_collected_data, save_calculated, add_logged_organisms

export get_genotype, get_snapshot_ids, get_time, get_snapshot, get_trials

export get_phenotype_similarity, get_most_frequent_genotypes, get_reachable_diversity
export get_reachable_fitness, get_snapshot, get_T_similarity, get_evolutionary_potential
export get_adaption_of_genotype, get_adaption_of_snapshot
export is_reproducing
export get_entropy
export get_genotype_diversity
export get_neutrality, get_neutrality_null_model

export _wasserstein, get_genotype_distribution

export save_offspring_log, build_genotype_graph, analyse_graph, calculate_phenotype_graph!
export GGraphData, save_graph_data, load_graph_data
export build_genotype_graph!, build_phenotype_graph!, build_neutral_networks!, analyse_phenotype_graph, get_neutral_networks_by_g_sampling

export get_diversity_threshold, analyse_neutral_networks, analyse_neutral_network_graph
export get_average_nn_size, get_average_radius, get_average_diameter, get_average_clustering, get_nn_size_percentile
export get_average_phenotype_robustness, get_average_phenotype_evolvability, get_average_phenotype_evolvability_robustness_cor
export get_shape_space_covering

# Null Models

export get_house_of_cards_null_model, get_complete_house_of_cards_null_model

# Tierra

export TierraModel
export TierrianOrganism

export SMALL_ANCESTOR, LARGE_ANCESTOR

export execute_slice!, SLICE_SIZE

export collect_distribution

export print_program

# Geb

export GebModel
export GebOrganism

export execute!

# Analysis

include("utils/organism.jl")

# include("history/types.jl")
# include("history/history.jl")#

# include("utils/group_by.jl")#

# include("utils/model.jl")
# include("random_model.jl")#

# include("analysis/statistics.jl")
# include("analysis/selective_factors.jl")

include("analysis/config.jl")
include("logging/statistic_model.jl")
include("logging/collect_distribution.jl")
include("logging/logger.jl")
include("logging/run_logger.jl")

include("analysis/collected_data.jl")
include("analysis/sample_distributions.jl")

include("analysis/estimator.jl")

include("analysis/utils.jl")
include("analysis/graph_utils.jl")

include("analysis/GP_maps/graph_data.jl")

include("analysis/statistics/phenotype_similarity.jl")
include("analysis/statistics/reachable_diversity.jl")
include("analysis/statistics/reachable_fitness.jl")
include("analysis/statistics/T_similarity.jl")
include("analysis/statistics/evolutionary_potential.jl")
include("analysis/statistics/level_of_adaption.jl")
include("analysis/statistics/reproducability.jl")
include("analysis/statistics/entropy.jl")
include("analysis/statistics/population_metrics.jl")

include("analysis/GP_maps/build_gp_map.jl")
include("analysis/GP_maps/analyse_neutral_networks.jl")

# Null Models

include("null_model/gp_null_models.jl")

# Tierra

include("tierra/config.jl")

include("tierra/instructions/search_directions.jl")

include("tierra/instructions/definitions.jl")

include("tierra/world/free_memory_block.jl")
include("tierra/mutations.jl")

include("tierra/organism/organism.jl")
include("tierra/world/tierra_model.jl")

include("tierra/statistics/statistic_model.jl")

include("tierra/world/memory_utils.jl")
include("tierra/world/memory.jl")
include("tierra/world/division.jl")
include("tierra/world/execution.jl")

include("tierra/instructions/template_search.jl")

include("tierra/instructions/implementation.jl")

include("tierra/small_ancestor.jl")
include("tierra/large_ancestor.jl")

include("tierra/show.jl")
include("tierra/logging.jl")

# Geb

include("geb/config.jl")

include("geb/utils.jl")

include("geb/network/rule.jl")
include("geb/network/node.jl")
include("geb/network/network.jl")
include("geb/network/BFS.jl")

include("geb/network/axiom_network.jl")
include("geb/network/rule_decoding.jl")
include("geb/network/development.jl")

include("geb/organism/organism.jl")
include("geb/model.jl")

include("geb/network/activation_propagation.jl")

include("geb/grid_utils.jl")
include("geb/organism/mutation.jl")
include("geb/organism/actions.jl")
include("geb/organism/input_routing.jl")

include("geb/execution.jl")

include("geb/statistics/statistic_model.jl")

include("geb/show.jl")

end

max_stars_repo_name: tochsner/ALifeBenchmark

max_issues_repo_name: tochsner/ALifeBenchmark

max_forks_repo_name: tochsner/ALifeBenchmark


text:
export Mutagenesis

"""
Mutagenesis

Website: https://relational.fit.cvut.cz/dataset/Mutagenesis
License: CC0

The `Mutagenesis` dataset comprises 188 molecules trialed for mutagenicity on Salmonella typhimurium, available from
 [relational.fit.cvut.cz](https://relational.fit.cvut.cz/dataset/Mutagenesis) and
 [CTUAvastLab/datasets](https://github.com/CTUAvastLab/datasets/tree/main/mutagenesis).

Train, test and validation data can be loaded using following code.
The `withenv("DATADEPS_ALWAYS_ACCEPT"=>"true")` disables the accept download prompt.
```jldoctest
julia> using MLDatasets: Mutagenesis

julia> train_x, train_y = withenv("DATADEPS_ALWAYS_ACCEPT"=>"true") do; Mutagenesis.traindata(); end;

julia> test_x, test_y = Mutagenesis.testdata();

julia> val_x, val_y = Mutagenesis.valdata();

julia> train_x[1]
Dict{Symbol, Any} with 5 entries:
  :lumo  => -1.246
  :inda  => 0
  :logp  => 4.23
  :ind1  => 1
  :atoms => Dict{Symbol, Any}[Dict(:element=>"c", :bonds=>Dict{Symbol, Any}[Dic…

julia> train_y[1]
1
```
"""
module Mutagenesis

using DataDeps, JSON3
using ..MLDatasets: datafile

const DEPNAME = "Mutagenesis"
const DATA = "data.json"
const METADATA = "meta.json"

function __init__()
    ORIGINAL_LINK = "https://relational.fit.cvut.cz/dataset/Mutagenesis"
    DATA_LINK = "https://raw.githubusercontent.com/CTUAvastLab/datasets/main/mutagenesis"

    register(DataDep(
        DEPNAME,
        """
        Dataset: The $DEPNAME dataset.
        Website: $ORIGINAL_LINK
        License: CC0
        """,
        "$DATA_LINK/" .* [DATA, METADATA],
        "80ec1716217135e1f2e0b5a61876c65184e2014e64551103c41e174775ca207c"
    ))
end

traindata(; dir = nothing) = traindata(dir)
testdata(; dir = nothing) = testdata(dir)
valdata(; dir = nothing) = valdata(dir)

function traindata(dir)
    samples, targets, train_idxs, val_idxs, test_idxs = load_data(dir)
    samples[train_idxs], targets[train_idxs]
end

function testdata(dir)
    samples, targets, train_idxs, val_idxs, test_idxs = load_data(dir)
    samples[test_idxs], targets[test_idxs]
end

function valdata(dir)
    samples, targets, train_idxs, val_idxs, test_idxs = load_data(dir)
    samples[val_idxs], targets[val_idxs]
end

function load_data(dir)
    data_path = datafile(DEPNAME, DATA, dir)
    metadata_path = datafile(DEPNAME, METADATA, dir)
    samples = read_data(data_path)
    metadata = read_data(metadata_path)
    labelkey = metadata["label"]
    targets = map(i -> i[labelkey], samples)
    samples_without_label = map(x->delete!(copy(x), Symbol(labelkey)), samples)
    val_num = metadata["val_samples"]
    test_num = metadata["test_samples"]
    train_idxs = 1:length(samples)-val_num-test_num
    val_idxs = length(samples)-val_num-test_num+1:length(samples)-test_num
    test_idxs = length(samples)-test_num+1:length(samples)
    samples_without_label, targets, train_idxs, val_idxs, test_idxs
end

read_data(path) = open(JSON3.read, path)

end # module


text:
# Autogenerated wrapper script for LibVPX_jll for i686-w64-mingw32
export libvpx

JLLWrappers.@generate_wrapper_header("LibVPX")
JLLWrappers.@declare_file_product(libvpx)
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_file_product(
        libvpx,
        "bin\\libvpx.a",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
wrap(content) = """
<?xml version="1.0" encoding="UTF-8"?>
<OME xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:OME="http://www.openmicroscopy.org/Schemas/OME/2016-06"
     xsi:schemaLocation="http://www.openmicroscopy.org/Schemas/OME/2016-06
     http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd">
    $content
</OME>"""

function faketiff()
    io = IOBuffer()
    write(io, OMETIFF.myendian())
    write(io, UInt16(42))
    write(io, UInt32(8))
    OMETIFF.TiffFile(OMETIFF.getstream(io))
end


text:
function RetrieveCapacityMix(systemHistory::SystemHistory)
    date = 1:length(systemHistory.systemCapacityMix)
    yearTechnologyMix = zeros(length(systemHistory.systemCapacityMix)+4,length(technologyType))
    # yearTechnologyCount = zeros(length(systemHistory.systemCapacityMix),length(technologyType))
    # yearTechnologyIncremental = zeros(length(systemHistory.systemCapacityMix),length(technologyType))
    # conventionalPlantsCapacity = Array{Float64,1}(undef,0)
    # renewablePlantsCapacity = Array{Float64,1}(undef,0)
    # conventionalPlantsNumbers = Array{Float64,1}(undef,0)
    # renewablePlantsNumbers = Array{Float64,1}(undef,0)
    for year in date
    # thisConventionalPlantsCapacity = 0
    # thisRenewablePlantsCapacity = 0
    # thisConventionalPlantsNumber = 0
    # thisRenewablePlantsNumber = 0
    # if year >= 2
    #     yearTechnologyIncremental[year,:] = yearTechnologyMix[year,:] - yearTechnologyMix[year-1,:]
    # end
        for generator in systemHistory.systemCapacityMix[year]
            for typeCount in 1:length(technologyType)
                if generator.type == technologyType[typeCount]
                    yearTechnologyMix[year,typeCount] += generator.designProperties.installedCapacity
                    # yearTechnologyCount[year,typeCount] +=1
                end
            end
            # if typeof(generator) == ConventionalPlants
            #     thisConventionalPlantsNumber += 1
            #     thisConventionalPlantsCapacity += generator.designProperties.maximumOutPut
            # end
            #
            # if typeof(generator) == RenewablePlants
            #     thisRenewablePlantsNumber += 1
            #     thisRenewablePlantsCapacity += generator.designProperties.maximumOutPut
            # end
        end
    # push!(conventionalPlantsCapacity,thisConventionalPlantsCapacity)
    # push!(renewablePlantsCapacity,thisRenewablePlantsCapacity)
    # push!(conventionalPlantsNumbers,thisConventionalPlantsNumber)
    # push!(renewablePlantsNumbers,thisRenewablePlantsNumber)
    end
    for finalYears in 1:4
        yearTechnologyMix[end-finalYears+1,:] = yearTechnologyMix[end-4,:]
    end

    return yearTechnologyMix
end
# println(conventionalPlantsNumbers)
# println(renewablePlantsNumbers)

# fig, axis = subplots()
# axis.stackplot(date,conventionalPlantsCapacity,renewablePlantsCapacity, colors = ["orange", "green"])
# axis.xlabel("time[year]", fontsize = 20)
# axis.ylabel("capacity mix [MW]", fontsize = 20)
# axis.xticks(fontsize = 18)
# axis.yticks(fontsize = 18)
#
# axis.plot([],[],color="orange", label="thermal", lineWidth=5)
# axis.plot([],[],color="green",label="renewable", lineWidth=5)
# axis.legend()
allCapacityMix = zeros(17,3)
for GEPcase in 1:4
    yearTechnologyMix = RetrieveCapacityMix(systemHistory_List[1,GEPcase])
    allCapacityMix[GEPcase,:] = sum(yearTechnologyMix[end-15:end-1,:],dims=1)/15
end
for EMLabcase in 1:4
    yearTechnologyMix = RetrieveCapacityMix(systemHistory_List[2,EMLabcase])
    allCapacityMix[4+EMLabcase,:] = sum(yearTechnologyMix[end-15:end-1,:],dims=1)/15
end
for EMCAScase in 1:9
    yearTechnologyMix = RetrieveCapacityMix(systemHistory_List[3,EMCAScase])
    allCapacityMix[8+EMCAScase,:] = sum(yearTechnologyMix[end-15:end-1,:],dims=1)/15
end

fig, axis = subplots()

yearTechnologyMix = RetrieveCapacityMix(systemHistory_List[3,9])

#PyPlot.figure(figure_num)
colorMix = ["#0485d1","#e17701","grey"]
axis.stackplot(collect(1:size(yearTechnologyMix)[1]),yearTechnologyMix' ,colors = colorMix )
#axis.title("System capacity mix", fontsize = 20)
axis.set_xlabel("Time[year]", fontsize = 20)
axis.set_xticks(collect(0:5:size(yearTechnologyMix)[1]))
axis.set_ylabel("Capacity mix [MW]", fontsize = 20)
axis.tick_params(axis="both",  labelsize=18)
for i in 1:length(technologyType)
    axis.plot([],[],color=colorMix[i], label=String(technologyType[i]), lineWidth=8)
end
axis.plot(collect(1:size(yearTechnologyMix)[1]),13670.25 * ones(size(yearTechnologyMix)[1]), label="Maximum load", color = "black",lineWidth=3)
axis.grid(true)
axis.set_xlim([1,size(yearTechnologyMix)[1]])
axis.legend(loc="upper center", bbox_to_anchor=(0.5, 1.12),
          ncol=4, fancybox=true, shadow=false, fontsize = 14)

fig, axis = subplots()
existingUnits = [33 22 11 0;
                33 22 11 0;
                33 22 11 0]
existingCapacities = 100 .* existingUnits
totalCapacityProjection = systemHistory_List[1,4].capacityMixProjection[1]
newInvestments = totalCapacityProjection.-existingCapacities
existingCapacities_repeated = repeat(existingCapacities, inner = (1,5))
newInvestments_repeated = repeat(newInvestments, inner = (1,5))
sourceType = ["Base(existing)","Base(expected)", "Mid(existing)","Mid(expected)", "Peak(existing)", "Peak(expected)", "Supply gap = 100 MW"]
colorMix = ["#0485d1","cyan","#e17701","yellow","grey","darkgray","midnightblue"]
separateCapacityMatrix = zeros(length(sourceType),20)
for j in 1:3
    separateCapacityMatrix[2*j-1,:] = existingCapacities_repeated[j,:]
    separateCapacityMatrix[2*j,:] = newInvestments_repeated[j,:]
end
axis.stackplot(collect(1:size(separateCapacityMatrix)[2]),separateCapacityMatrix,colors = colorMix)
axis.plot(collect(1:20),13670.25 * ones(20), label="Maximum load", color = "orange",lineWidth=3)
axis.set_xlim([1,20])
axis.set_xticks([5,10,15,20])
axis.set_xticklabels([])
axis.tick_params(axis="both",  labelsize=18)
axis.set_ylabel("Capacity mix [MW]",fontsize = 20)
axis.set_xlabel("Time [Year]",fontsize = 20)

# figure_num += 1
# PyPlot.figure(figure_num)
# for genCoCount in 1:systemSettings.GenCoNumbers
#     PyPlot.subplot(systemSettings.GenCoNumbers,1,genCoCount)
#     titleName = string("Investment History of GenCo ",genCoCount)
#     PyPlot.title(titleName)
#     PlotStackBar(allGenCos[genCoCount],systemHistory,technologyType)
#     if genCoCount == 1
#         PyPlot.legend()
#     end
#     if genCoCount == 3
#         PyPlot.ylabel("capacity mix [MW]", fontsize = 20)
#     end
#     PyPlot.xticks(fontsize = 4)
#     PyPlot.yticks(fontsize = 18)
#     if genCoCount == 5
#         PyPlot.xticks(fontsize = 18)
#     end
# end
# PyPlot.xlabel("time[year]", fontsize = 20)

# figure_num += 1
# PyPlot.figure(figure_num)
# PyPlot.plot(systemHistory.systemElectricityPrice)
# PyPlot.title("Electricity price evolution", fontsize = 20)
# PyPlot.xlabel("time[year]", fontsize = 20)
# PyPlot.ylabel("electricity price [EUR/MWh]", fontsize = 20)
# PyPlot.xticks(fontsize = 18)
# PyPlot.yticks(fontsize = 18)
# PyPlot.grid(true)

#figure_num += 1
#PyPlot.figure(figure_num)
#PlotLoadDurationCurve(systemSettings)
#plt.show()

# for genCo in allGenCos
#     global figure_num += 1
#     PyPlot.figure(figure_num)
#     PlotCapacityMix(genCo,technologyType)
# end

# figure_num += 1
# PyPlot.figure(figure_num)
# PlotObservedPrice(systemHistory,  systemLoad, technologyType, 40 ,20)

# println(systemHistory.yearlyInvestmentInformation[1])
# CSV.write("InvestmentHistory.csv", systemHistory.yearlyInvestmentInformation[1])
#figure_num += 1
#PyPlot.figure(figure_num)
#PlotPriceEvolutionInvest(allGenCos, 30)

#figure_num += 1
#PyPlot.figure(figure_num)
#PlotNPVStream(allGenCos, 5)
# figure_num += 1
# caseList = collect(1:9)
# fig, axes = PyPlot.subplots(3, 3, sharex=true)
# #axes.get_xaxis().set_visible(false)
# for caseNum in caseList
#     colorMix = ["#0485d1","#e17701","grey"]
#     row_count = Int(ceil(caseNum/3))
#     if caseNum % 3 == 0
#         column_Count = 3
#     else
#         column_Count = caseNum % 3
#     end
#     axes[row_count,column_Count].stackplot(0:1,sensitivity_EMCAS[caseNum]' ,colors = colorMix )
#     axes[row_count,column_Count].plot(0:1, 13670 *ones(2), lineWidth=3 ,color = "y")
#     axes[row_count,column_Count].get_xaxis().set_visible(false)
#     axes[row_count,column_Count].grid(true)
#
#
#     if column_Count == 2 || column_Count == 3
#         axes[row_count,column_Count].set_yticklabels([])
#     end
#     axes[row_count,column_Count].set_xticklabels(["Case1"])
#
#
#     #axes[column_Count,row_count].bar([1],sensitivity_EMCAS[caseNum][1,1],width = 0.5)
#     #axes[column_Count,row_count].bar([1],sensitivity_EMCAS[caseNum][1,2],width = 0.5,bottom=sensitivity_EMCAS[caseNum][1,1])
#     #axes[column_Count,row_count].bar([1],sensitivity_EMCAS[caseNum][1,3],width = 0.5,bottom=(sensitivity_EMCAS[caseNum][1,2]+sensitivity_EMCAS[caseNum][1,1]))
#     #axes[column_Count,row_count].set_xticks(0:1, ["G1"])
#     #PyPlot.title("System capacity mix", fontsize = 20)
#     #xName = string("Case ",caseNum)
#     #PyPlot.xlabel(xName, fontsize = 20)
#     #PyPlot.ylabel("capacity mix [MW]", fontsize = 20)
#     #PyPlot.xticks(fontsize = 18)
#     axes[row_count,column_Count].tick_params(labelsize = 16)
#     if row_count == 1
#         axes[row_count,column_Count].set_ylim(ymin = 0, ymax = 32000)
#     end
#     if row_count == 2
#         axes[row_count,column_Count].set_ylim(ymin = 0, ymax = 32000)
#     end
#     if row_count == 3
#         axes[row_count,column_Count].set_ylim(ymin = 0, ymax = 32000)
#     end
# end
# #fig.text(0.5, 0.04, "Cases number", ha="center",fontsize = 20)
# fig.text(0.04, 0.5, "Capacity mix [MW]", va="center", rotation="vertical",fontsize = 24)


text:
gf() = begin
    CSV.read("c:/data/AirOnTimeCSV/airOT198710.csv")
end

gf2() = begin
    p = "c:/data/AirOnTimeCSV/"
    f = joinpath.(p, readdir(p))
    sort!(f, by = x -> filesize(x), rev = true)
    reduce(vcat, CSV.read.(f[1:100]))
end

iow() = begin
    open("c:/data/bin.bin", "w")
end

ior() = begin
    open("c:/data/bin.bin", "r")
end

max_stars_repo_name: xiaodaigh/JDF

max_issues_repo_name: xiaodaigh/JDF

max_forks_repo_name: xiaodaigh/JDF


text:
get_layout_pairs(nbit::Int; periodic::Bool) = map(i->(i=>i%nbit+1), 1:(periodic ? nbit : nbit-1))
get_layout_pairs(nx::Int, ny::Int; periodic::Bool) = pair_square(nx, ny; periodic=periodic)

@const_gate ZZ = mat(kron(Z, Z))

rzz_layer(nbit::Int, layout) = chain(nbit, [put(nbit, (i,j)=>rot(ZZ, 0.0)) for (i,j) in layout])
rx_layer(nbit::Int) = chain(nbit, [put(nbit, i=>Rx(0.0)) for i=1:nbit])

function qaoa_circuit(size, nlayer::Int; periodic=false)
    nbit = prod(size)
    layout = get_layout_pairs(size...; periodic=periodic)
    c = chain(nbit)
    for i=1:nlayer
        push!(c, rx_layer(nbit))
        push!(c, rzz_layer(nbit, layout))
    end
    return c
end

function tns_circuit(nbit, nlayer, pairs; entangler=cnot)
    circuit = chain(nbit)
    for i = 1:nlayer
        ent = chain(nbit, entangler(nbit, i, j) for (i, j) in pairs)
        push!(circuit, ent)
    end
    circuit
end


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: iotthingsgraph

using AWS.Compat
using AWS.UUIDs
"""
    AssociateEntityToThing()

Associates a device with a concrete thing that is in the user's registry. A thing can be associated with only one device at a time. If you associate a thing with a new device id, its previous association will be removed.

# Required Parameters
- `entityId`: The ID of the device to be associated with the thing. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:device:DEVICENAME
- `thingName`: The name of the thing to which the entity is to be associated.

# Optional Parameters
- `namespaceVersion`: The version of the user's namespace. Defaults to the latest version of the user's namespace.
"""

associate_entity_to_thing(entityId, thingName; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("AssociateEntityToThing", Dict{String, Any}("entityId"=>entityId, "thingName"=>thingName); aws_config=aws_config)
associate_entity_to_thing(entityId, thingName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("AssociateEntityToThing", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("entityId"=>entityId, "thingName"=>thingName), args)); aws_config=aws_config)

"""
    CreateFlowTemplate()

Creates a workflow template. Workflows can be created only in the user's namespace. (The public namespace contains only entities.) The workflow can contain only entities in the specified namespace. The workflow is validated against the entities in the latest version of the user's namespace unless another namespace version is specified in the request.

# Required Parameters
- `definition`: The workflow DefinitionDocument.

# Optional Parameters
- `compatibleNamespaceVersion`: The namespace version in which the workflow is to be created. If no value is specified, the latest version is used by default.
"""

create_flow_template(definition; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("CreateFlowTemplate", Dict{String, Any}("definition"=>definition); aws_config=aws_config)
create_flow_template(definition, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("CreateFlowTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("definition"=>definition), args)); aws_config=aws_config)

"""
    CreateSystemInstance()

Creates a system instance.  This action validates the system instance, prepares the deployment-related resources. For Greengrass deployments, it updates the Greengrass group that is specified by the greengrassGroupName parameter. It also adds a file to the S3 bucket specified by the s3BucketName parameter. You need to call DeploySystemInstance after running this action. For Greengrass deployments, since this action modifies and adds resources to a Greengrass group and an S3 bucket on the caller's behalf, the calling identity must have write permissions to both the specified Greengrass group and S3 bucket. Otherwise, the call will fail with an authorization error. For cloud deployments, this action requires a flowActionsRoleArn value. This is an IAM role that has permissions to access AWS services, such as AWS Lambda and AWS IoT, that the flow uses when it executes. If the definition document doesn't specify a version of the user's namespace, the latest version will be used by default.

# Required Parameters
- `definition`:
- `target`: The target type of the deployment. Valid values are GREENGRASS and CLOUD.

# Optional Parameters
- `flowActionsRoleArn`: The ARN of the IAM role that AWS IoT Things Graph will assume when it executes the flow. This role must have read and write access to AWS Lambda and AWS IoT and any other AWS services that the flow uses when it executes. This value is required if the value of the target parameter is CLOUD.
- `greengrassGroupName`: The name of the Greengrass group where the system instance will be deployed. This value is required if the value of the target parameter is GREENGRASS.
- `metricsConfiguration`:
- `s3BucketName`: The name of the Amazon Simple Storage Service bucket that will be used to store and deploy the system instance's resource file. This value is required if the value of the target parameter is GREENGRASS.
- `tags`: Metadata, consisting of key-value pairs, that can be used to categorize your system instances.
"""

create_system_instance(definition, target; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("CreateSystemInstance", Dict{String, Any}("definition"=>definition, "target"=>target); aws_config=aws_config)
create_system_instance(definition, target, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("CreateSystemInstance", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("definition"=>definition, "target"=>target), args)); aws_config=aws_config)

"""
    CreateSystemTemplate()

Creates a system. The system is validated against the entities in the latest version of the user's namespace unless another namespace version is specified in the request.

# Required Parameters
- `definition`: The DefinitionDocument used to create the system.

# Optional Parameters
- `compatibleNamespaceVersion`: The namespace version in which the system is to be created. If no value is specified, the latest version is used by default.
"""

create_system_template(definition; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("CreateSystemTemplate", Dict{String, Any}("definition"=>definition); aws_config=aws_config)
create_system_template(definition, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("CreateSystemTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("definition"=>definition), args)); aws_config=aws_config)

"""
    DeleteFlowTemplate()

Deletes a workflow. Any new system or deployment that contains this workflow will fail to update or deploy. Existing deployments that contain the workflow will continue to run (since they use a snapshot of the workflow taken at the time of deployment).

# Required Parameters
- `id`: The ID of the workflow to be deleted. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:workflow:WORKFLOWNAME

"""

delete_flow_template(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteFlowTemplate", Dict{String, Any}("id"=>id); aws_config=aws_config)
delete_flow_template(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteFlowTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    DeleteNamespace()

Deletes the specified namespace. This action deletes all of the entities in the namespace. Delete the systems and flows that use entities in the namespace before performing this action.

"""

delete_namespace(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteNamespace"; aws_config=aws_config)
delete_namespace(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteNamespace", args; aws_config=aws_config)

"""
    DeleteSystemInstance()

Deletes a system instance. Only system instances that have never been deployed, or that have been undeployed can be deleted. Users can create a new system instance that has the same ID as a deleted system instance.

# Optional Parameters
- `id`: The ID of the system instance to be deleted.
"""

delete_system_instance(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteSystemInstance"; aws_config=aws_config)
delete_system_instance(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteSystemInstance", args; aws_config=aws_config)

"""
    DeleteSystemTemplate()

Deletes a system. New deployments can't contain the system after its deletion. Existing deployments that contain the system will continue to work because they use a snapshot of the system that is taken when it is deployed.

# Required Parameters
- `id`: The ID of the system to be deleted. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:system:SYSTEMNAME

"""

delete_system_template(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteSystemTemplate", Dict{String, Any}("id"=>id); aws_config=aws_config)
delete_system_template(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeleteSystemTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    DeploySystemInstance()

 Greengrass and Cloud Deployments  Deploys the system instance to the target specified in CreateSystemInstance.   Greengrass Deployments  If the system or any workflows and entities have been updated before this action is called, then the deployment will create a new Amazon Simple Storage Service resource file and then deploy it. Since this action creates a Greengrass deployment on the caller's behalf, the calling identity must have write permissions to the specified Greengrass group. Otherwise, the call will fail with an authorization error. For information about the artifacts that get added to your Greengrass core device when you use this API, see AWS IoT Things Graph and AWS IoT Greengrass.

# Optional Parameters
- `id`: The ID of the system instance. This value is returned by the CreateSystemInstance action. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:deployment:DEPLOYMENTNAME
"""

deploy_system_instance(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeploySystemInstance"; aws_config=aws_config)
deploy_system_instance(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeploySystemInstance", args; aws_config=aws_config)

"""
    DeprecateFlowTemplate()

Deprecates the specified workflow. This action marks the workflow for deletion. Deprecated flows can't be deployed, but existing deployments will continue to run.

# Required Parameters
- `id`: The ID of the workflow to be deleted. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:workflow:WORKFLOWNAME

"""

deprecate_flow_template(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeprecateFlowTemplate", Dict{String, Any}("id"=>id); aws_config=aws_config)
deprecate_flow_template(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeprecateFlowTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    DeprecateSystemTemplate()

Deprecates the specified system.

# Required Parameters
- `id`: The ID of the system to delete. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:system:SYSTEMNAME

"""

deprecate_system_template(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeprecateSystemTemplate", Dict{String, Any}("id"=>id); aws_config=aws_config)
deprecate_system_template(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DeprecateSystemTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    DescribeNamespace()

Gets the latest version of the user's namespace and the public version that it is tracking.

# Optional Parameters
- `namespaceName`: The name of the user's namespace. Set this to aws to get the public namespace.
"""

describe_namespace(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DescribeNamespace"; aws_config=aws_config)
describe_namespace(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DescribeNamespace", args; aws_config=aws_config)

"""
    DissociateEntityFromThing()

Dissociates a device entity from a concrete thing. The action takes only the type of the entity that you need to dissociate because only one entity of a particular type can be associated with a thing.

# Required Parameters
- `entityType`: The entity type from which to disassociate the thing.
- `thingName`: The name of the thing to disassociate.

"""

dissociate_entity_from_thing(entityType, thingName; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DissociateEntityFromThing", Dict{String, Any}("entityType"=>entityType, "thingName"=>thingName); aws_config=aws_config)
dissociate_entity_from_thing(entityType, thingName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("DissociateEntityFromThing", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("entityType"=>entityType, "thingName"=>thingName), args)); aws_config=aws_config)

"""
    GetEntities()

Gets definitions of the specified entities. Uses the latest version of the user's namespace by default. This API returns the following TDM entities.   Properties   States   Events   Actions   Capabilities   Mappings   Devices   Device Models   Services   This action doesn't return definitions for systems, flows, and deployments.

# Required Parameters
- `ids`: An array of entity IDs. The IDs should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:device:DEVICENAME

# Optional Parameters
- `namespaceVersion`: The version of the user's namespace. Defaults to the latest version of the user's namespace.
"""

get_entities(ids; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetEntities", Dict{String, Any}("ids"=>ids); aws_config=aws_config)
get_entities(ids, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetEntities", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ids"=>ids), args)); aws_config=aws_config)

"""
    GetFlowTemplate()

Gets the latest version of the DefinitionDocument and FlowTemplateSummary for the specified workflow.

# Required Parameters
- `id`: The ID of the workflow. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:workflow:WORKFLOWNAME

# Optional Parameters
- `revisionNumber`: The number of the workflow revision to retrieve.
"""

get_flow_template(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetFlowTemplate", Dict{String, Any}("id"=>id); aws_config=aws_config)
get_flow_template(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetFlowTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    GetFlowTemplateRevisions()

Gets revisions of the specified workflow. Only the last 100 revisions are stored. If the workflow has been deprecated, this action will return revisions that occurred before the deprecation. This action won't work for workflows that have been deleted.

# Required Parameters
- `id`: The ID of the workflow. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:workflow:WORKFLOWNAME

# Optional Parameters
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

get_flow_template_revisions(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetFlowTemplateRevisions", Dict{String, Any}("id"=>id); aws_config=aws_config)
get_flow_template_revisions(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetFlowTemplateRevisions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    GetNamespaceDeletionStatus()

Gets the status of a namespace deletion task.

"""

get_namespace_deletion_status(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetNamespaceDeletionStatus"; aws_config=aws_config)
get_namespace_deletion_status(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetNamespaceDeletionStatus", args; aws_config=aws_config)

"""
    GetSystemInstance()

Gets a system instance.

# Required Parameters
- `id`: The ID of the system deployment instance. This value is returned by CreateSystemInstance. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:deployment:DEPLOYMENTNAME

"""

get_system_instance(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetSystemInstance", Dict{String, Any}("id"=>id); aws_config=aws_config)
get_system_instance(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetSystemInstance", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    GetSystemTemplate()

Gets a system.

# Required Parameters
- `id`: The ID of the system to get. This ID must be in the user's namespace. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:system:SYSTEMNAME

# Optional Parameters
- `revisionNumber`: The number that specifies the revision of the system to get.
"""

get_system_template(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetSystemTemplate", Dict{String, Any}("id"=>id); aws_config=aws_config)
get_system_template(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetSystemTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    GetSystemTemplateRevisions()

Gets revisions made to the specified system template. Only the previous 100 revisions are stored. If the system has been deprecated, this action will return the revisions that occurred before its deprecation. This action won't work with systems that have been deleted.

# Required Parameters
- `id`: The ID of the system template. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:system:SYSTEMNAME

# Optional Parameters
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

get_system_template_revisions(id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetSystemTemplateRevisions", Dict{String, Any}("id"=>id); aws_config=aws_config)
get_system_template_revisions(id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetSystemTemplateRevisions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("id"=>id), args)); aws_config=aws_config)

"""
    GetUploadStatus()

Gets the status of the specified upload.

# Required Parameters
- `uploadId`: The ID of the upload. This value is returned by the UploadEntityDefinitions action.

"""

get_upload_status(uploadId; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetUploadStatus", Dict{String, Any}("uploadId"=>uploadId); aws_config=aws_config)
get_upload_status(uploadId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("GetUploadStatus", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("uploadId"=>uploadId), args)); aws_config=aws_config)

"""
    ListFlowExecutionMessages()

Returns a list of objects that contain information about events in a flow execution.

# Required Parameters
- `flowExecutionId`: The ID of the flow execution.

# Optional Parameters
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

list_flow_execution_messages(flowExecutionId; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("ListFlowExecutionMessages", Dict{String, Any}("flowExecutionId"=>flowExecutionId); aws_config=aws_config)
list_flow_execution_messages(flowExecutionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("ListFlowExecutionMessages", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("flowExecutionId"=>flowExecutionId), args)); aws_config=aws_config)

"""
    ListTagsForResource()

Lists all tags on an AWS IoT Things Graph resource.

# Required Parameters
- `resourceArn`: The Amazon Resource Name (ARN) of the resource whose tags are to be returned.

# Optional Parameters
- `maxResults`: The maximum number of tags to return.
- `nextToken`: The token that specifies the next page of results to return.
"""

list_tags_for_resource(resourceArn; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("ListTagsForResource", Dict{String, Any}("resourceArn"=>resourceArn); aws_config=aws_config)
list_tags_for_resource(resourceArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("ListTagsForResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("resourceArn"=>resourceArn), args)); aws_config=aws_config)

"""
    SearchEntities()

Searches for entities of the specified type. You can search for entities in your namespace and the public namespace that you're tracking.

# Required Parameters
- `entityTypes`: The entity types for which to search.

# Optional Parameters
- `filters`: Optional filter to apply to the search. Valid filters are NAME NAMESPACE, SEMANTIC_TYPE_PATH and REFERENCED_ENTITY_ID. REFERENCED_ENTITY_ID filters on entities that are used by the entity in the result set. For example, you can filter on the ID of a property that is used in a state. Multiple filters function as OR criteria in the query. Multiple values passed inside the filter function as AND criteria.
- `maxResults`: The maximum number of results to return in the response.
- `namespaceVersion`: The version of the user's namespace. Defaults to the latest version of the user's namespace.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

search_entities(entityTypes; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchEntities", Dict{String, Any}("entityTypes"=>entityTypes); aws_config=aws_config)
search_entities(entityTypes, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchEntities", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("entityTypes"=>entityTypes), args)); aws_config=aws_config)

"""
    SearchFlowExecutions()

Searches for AWS IoT Things Graph workflow execution instances.

# Required Parameters
- `systemInstanceId`: The ID of the system instance that contains the flow.

# Optional Parameters
- `endTime`: The date and time of the latest flow execution to return.
- `flowExecutionId`: The ID of a flow execution.
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
- `startTime`: The date and time of the earliest flow execution to return.
"""

search_flow_executions(systemInstanceId; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchFlowExecutions", Dict{String, Any}("systemInstanceId"=>systemInstanceId); aws_config=aws_config)
search_flow_executions(systemInstanceId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchFlowExecutions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("systemInstanceId"=>systemInstanceId), args)); aws_config=aws_config)

"""
    SearchFlowTemplates()

Searches for summary information about workflows.

# Optional Parameters
- `filters`: An array of objects that limit the result set. The only valid filter is DEVICE_MODEL_ID.
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

search_flow_templates(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchFlowTemplates"; aws_config=aws_config)
search_flow_templates(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchFlowTemplates", args; aws_config=aws_config)

"""
    SearchSystemInstances()

Searches for system instances in the user's account.

# Optional Parameters
- `filters`: Optional filter to apply to the search. Valid filters are SYSTEM_TEMPLATE_ID, STATUS, and GREENGRASS_GROUP_NAME. Multiple filters function as OR criteria in the query. Multiple values passed inside the filter function as AND criteria.
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

search_system_instances(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchSystemInstances"; aws_config=aws_config)
search_system_instances(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchSystemInstances", args; aws_config=aws_config)

"""
    SearchSystemTemplates()

Searches for summary information about systems in the user's account. You can filter by the ID of a workflow to return only systems that use the specified workflow.

# Optional Parameters
- `filters`: An array of filters that limit the result set. The only valid filter is FLOW_TEMPLATE_ID.
- `maxResults`: The maximum number of results to return in the response.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

search_system_templates(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchSystemTemplates"; aws_config=aws_config)
search_system_templates(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchSystemTemplates", args; aws_config=aws_config)

"""
    SearchThings()

Searches for things associated with the specified entity. You can search by both device and device model. For example, if two different devices, camera1 and camera2, implement the camera device model, the user can associate thing1 to camera1 and thing2 to camera2. SearchThings(camera2) will return only thing2, but SearchThings(camera) will return both thing1 and thing2. This action searches for exact matches and doesn't perform partial text matching.

# Required Parameters
- `entityId`: The ID of the entity to which the things are associated. The IDs should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:device:DEVICENAME

# Optional Parameters
- `maxResults`: The maximum number of results to return in the response.
- `namespaceVersion`: The version of the user's namespace. Defaults to the latest version of the user's namespace.
- `nextToken`: The string that specifies the next page of results. Use this when you're paginating results.
"""

search_things(entityId; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchThings", Dict{String, Any}("entityId"=>entityId); aws_config=aws_config)
search_things(entityId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("SearchThings", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("entityId"=>entityId), args)); aws_config=aws_config)

"""
    TagResource()

Creates a tag for the specified resource.

# Required Parameters
- `resourceArn`: The Amazon Resource Name (ARN) of the resource whose tags are returned.
- `tags`: A list of tags to add to the resource.>

"""

tag_resource(resourceArn, tags; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("TagResource", Dict{String, Any}("resourceArn"=>resourceArn, "tags"=>tags); aws_config=aws_config)
tag_resource(resourceArn, tags, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("TagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("resourceArn"=>resourceArn, "tags"=>tags), args)); aws_config=aws_config)

"""
    UndeploySystemInstance()

Removes a system instance from its target (Cloud or Greengrass).

# Optional Parameters
- `id`: The ID of the system instance to remove from its target.
"""

undeploy_system_instance(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UndeploySystemInstance"; aws_config=aws_config)
undeploy_system_instance(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UndeploySystemInstance", args; aws_config=aws_config)

"""
    UntagResource()

Removes a tag from the specified resource.

# Required Parameters
- `resourceArn`: The Amazon Resource Name (ARN) of the resource whose tags are to be removed.
- `tagKeys`: A list of tag key names to remove from the resource. You don't specify the value. Both the key and its associated value are removed.  This parameter to the API requires a JSON text string argument. For information on how to format a JSON parameter for the various command line tool environments, see Using JSON for Parameters in the AWS CLI User Guide.

"""

untag_resource(resourceArn, tagKeys; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UntagResource", Dict{String, Any}("resourceArn"=>resourceArn, "tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(resourceArn, tagKeys, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UntagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("resourceArn"=>resourceArn, "tagKeys"=>tagKeys), args)); aws_config=aws_config)

"""
    UpdateFlowTemplate()

Updates the specified workflow. All deployed systems and system instances that use the workflow will see the changes in the flow when it is redeployed. If you don't want this behavior, copy the workflow (creating a new workflow with a different ID), and update the copy. The workflow can contain only entities in the specified namespace.

# Required Parameters
- `definition`: The DefinitionDocument that contains the updated workflow definition.
- `id`: The ID of the workflow to be updated. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:workflow:WORKFLOWNAME

# Optional Parameters
- `compatibleNamespaceVersion`: The version of the user's namespace. If no value is specified, the latest version is used by default. Use the GetFlowTemplateRevisions if you want to find earlier revisions of the flow to update.
"""

update_flow_template(definition, id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UpdateFlowTemplate", Dict{String, Any}("definition"=>definition, "id"=>id); aws_config=aws_config)
update_flow_template(definition, id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UpdateFlowTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("definition"=>definition, "id"=>id), args)); aws_config=aws_config)

"""
    UpdateSystemTemplate()

Updates the specified system. You don't need to run this action after updating a workflow. Any deployment that uses the system will see the changes in the system when it is redeployed.

# Required Parameters
- `definition`: The DefinitionDocument that contains the updated system definition.
- `id`: The ID of the system to be updated. The ID should be in the following format.  urn:tdm:REGION/ACCOUNT ID/default:system:SYSTEMNAME

# Optional Parameters
- `compatibleNamespaceVersion`: The version of the user's namespace. Defaults to the latest version of the user's namespace. If no value is specified, the latest version is used by default.
"""

update_system_template(definition, id; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UpdateSystemTemplate", Dict{String, Any}("definition"=>definition, "id"=>id); aws_config=aws_config)
update_system_template(definition, id, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UpdateSystemTemplate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("definition"=>definition, "id"=>id), args)); aws_config=aws_config)

"""
    UploadEntityDefinitions()

Asynchronously uploads one or more entity definitions to the user's namespace. The document parameter is required if syncWithPublicNamespace and deleteExistingEntites are false. If the syncWithPublicNamespace parameter is set to true, the user's namespace will synchronize with the latest version of the public namespace. If deprecateExistingEntities is set to true, all entities in the latest version will be deleted before the new DefinitionDocument is uploaded. When a user uploads entity definitions for the first time, the service creates a new namespace for the user. The new namespace tracks the public namespace. Currently users can have only one namespace. The namespace version increments whenever a user uploads entity definitions that are backwards-incompatible and whenever a user sets the syncWithPublicNamespace parameter or the deprecateExistingEntities parameter to true. The IDs for all of the entities should be in URN format. Each entity must be in the user's namespace. Users can't create entities in the public namespace, but entity definitions can refer to entities in the public namespace. Valid entities are Device, DeviceModel, Service, Capability, State, Action, Event, Property, Mapping, Enum.

# Optional Parameters
- `deprecateExistingEntities`: A Boolean that specifies whether to deprecate all entities in the latest version before uploading the new DefinitionDocument. If set to true, the upload will create a new namespace version.
- `document`: The DefinitionDocument that defines the updated entities.
- `syncWithPublicNamespace`: A Boolean that specifies whether to synchronize with the latest version of the public namespace. If set to true, the upload will create a new namespace version.
"""

upload_entity_definitions(; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UploadEntityDefinitions"; aws_config=aws_config)
upload_entity_definitions(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = iotthingsgraph("UploadEntityDefinitions", args; aws_config=aws_config)


text:
using ITensorNetworkAD
using AutoHOOT
using ITensors
using Zygote
using ITensorNetworkAD.ITensorAutoHOOT: SubNetwork

const go = AutoHOOT.graphops
const itensorah = ITensorNetworkAD.ITensorAutoHOOT

@testset "test interface" begin
  i = Index(2, "i")
  j = Index(3, "j")
  k = Index(2, "k")
  l = Index(4, "l")
  m = Index(5, "m")

  A = randomITensor(i, j)
  B = randomITensor(j, k)
  C = randomITensor(k, l)
  D = randomITensor(l, m)

  out = A * B * C * D
  network = [A, C, B, D]
  nodes, dict = itensorah.generate_einsum_expr([network])
  network = itensorah.extract_network(nodes[1], dict)
  out2 = contract(network)

  AB = SubNetwork([A, B])
  ABC = SubNetwork([AB, C])
  ABCD = SubNetwork([D, ABC])
  nodes, dict = itensorah.generate_einsum_expr([ABCD])
  network = itensorah.extract_network(nodes[1], dict)
  out3 = contract(network)

  @test isapprox(out, out2)
  @test isapprox(out, out3)
end

@testset "test compute" begin
  i = Index(2, "i")
  j = Index(3, "j")
  k = Index(4, "k")
  l = Index(5, "l")
  m = Index(6, "m")

  A = randomITensor(i, j)
  B = randomITensor(j, k)
  C = randomITensor(k, l)
  D = randomITensor(l, m)
  E = randomITensor(m, i)

  out = A * B * C * D * E
  network = [A, B, C, D, E]

  nodes, dict = itensorah.generate_einsum_expr([network])
  node = go.generate_optimal_tree(nodes[1])
  out_list = itensorah.compute_graph([node], dict)
  out2 = out_list[1]

  @test isapprox(out, out2)
end

@testset "test optimal contraction path" begin
  i = Index(2, "i")
  j = Index(3, "j")
  k = Index(4, "k")
  l = Index(5, "l")
  m = Index(6, "m")

  A = randomITensor(i, j)
  B = randomITensor(j, k)
  C = randomITensor(k, l)
  D = randomITensor(l, m)
  E = randomITensor(m, i)

  out = A * B * C * D * E

  network = itensorah.generate_optimal_tree([A, B, C, D, E])
  out2 = contract(network)

  @test isapprox(out, out2)
end

@testset "test gradient" begin
  i = Index(2, "i")
  j = Index(3, "j")
  k = Index(4, "k")
  l = Index(5, "l")
  m = Index(6, "m")

  A = randomITensor(i, j)
  B = randomITensor(j, k)
  C = randomITensor(k, l)
  D = randomITensor(l, m)
  E = randomITensor(m, i)

  gradA_direct = B * C * D * E
  gradB_direct = A * C * D * E

  networks = itensorah.gradients([A, B, C, D, E], [A, B])
  gradA = contract(networks[1])
  gradB = contract(networks[2])

  @test isapprox(gradA_direct, gradA)
  @test isapprox(gradB_direct, gradB)
end

@testset "test batch_tensor_contraction" begin
  i = Index(2, "i")
  j = Index(3, "j")
  k = Index(2, "k")
  A = randomITensor(i, j)
  B = randomITensor(j, k)
  C = randomITensor(k, i)

  function network(A)
    tensor_network = [A, B, C]
    out = itensorah.batch_tensor_contraction([tensor_network], A)
    return sum(out)[]
  end
  function tree_network(A)
    tensor_network = SubNetwork(SubNetwork(A, B), C)
    out = itensorah.batch_tensor_contraction([tensor_network], A)
    return sum(out)[]
  end
  grad_A = gradient(network, A)
  tree_grad_A = gradient(tree_network, A)
  @test isapprox(grad_A[1], B * C)
  @test isapprox(tree_grad_A[1], B * C)
end

@testset "test zygote interface for inner product" begin
  i = Index(2, "i")
  a = randomITensor(i)
  # build a symmetric H
  H = ITensor(i, i')
  H[i => 1, i' => 1] = 1.0
  H[i => 2, i' => 1] = 2.0
  H[i => 1, i' => 2] = 2.0
  H[i => 2, i' => 2] = 3.0

  function inner(a)
    b = prime(a)
    network = [a, H, b]
    inner = itensorah.batch_tensor_contraction([network], network...)
    return sum(inner)[]
  end
  grad = gradient(inner, a)
  @test isapprox((grad[1])', 2 * H * a)
end

@testset "test zygote interface with sum" begin
  A = ITensor(3.0)
  B = ITensor(2.0)
  function add(A, B)
    return sum([A, B])[]
  end
  grad = gradient(add, A, B)
  @test isapprox(grad[1], ITensor(1.0))
end

@testset "test zygote interface with multiple networks" begin
  i = Index(2, "i")
  j = Index(2, "j")
  k = Index(2, "k")
  l = Index(2, "l")
  A = randomITensor(i, j)
  B = randomITensor(j, i)
  C = randomITensor(j, k)
  D = randomITensor(k, l)
  E = randomITensor(l, i)

  function inner(A)
    networks = [[A, B], [A, C, D, E]]
    contract = itensorah.batch_tensor_contraction(networks, A, B, C, D, E)
    return sum(contract)[]
  end
  grad = gradient(inner, A)
  @test isapprox(grad[1], B + C * D * E)
end

@testset "test simple hvp" begin
  i = Index(2, "i")
  A = randomITensor(i)
  B = randomITensor(i, i')
  B[i => 1, i' => 1] = 1.0
  B[i => 2, i' => 1] = 2.0
  B[i => 1, i' => 2] = 2.0
  B[i => 2, i' => 2] = 3.0
  v = randomITensor(i)

  network(x) = ((x * B) * prime(x))[]
  grad(x) = gradient(network, x)[1]
  inner(x) = (grad(x) * v)[]
  hvp(x) = gradient(inner, x)[1]

  hvp_out = hvp(A)
  hvp_true = noprime(2 * B * v)
  @test isapprox(hvp_out, hvp_true)
end

@testset "test hvp with batch_tensor_contraction" begin
  i = Index(2, "i")
  A = randomITensor(i)
  B = randomITensor(i, i')
  B[i => 1, i' => 1] = 1.0
  B[i => 2, i' => 1] = 2.0
  B[i => 1, i' => 2] = 2.0
  B[i => 2, i' => 2] = 3.0
  v = randomITensor(i)

  function network(x)
    xprime = prime(x)
    inner = sum(itensorah.batch_tensor_contraction([[x, xprime, B]], x, xprime))
    return inner[]
  end
  grad(x) = gradient(network, x)[1]
  inner(x) = (grad(x) * v)[]
  hvp(x) = gradient(inner, x)[1]
  hvp_out = hvp(A)
  hvp_true = noprime(2 * B * v)
  @test isapprox(hvp_out, hvp_true)
end


text:
using Highlights.Format
import Highlights: Lexers
import Highlights

# ------------------------------- highlighting ------------------------------- #
highlight_regexes = Dict(
    :multiline_code => [
        r"\`\`\`[a-zA-Z0-9 \( \) \+ \= \; \. \, \/ \@ \# \s \? \{ \}  \_ \- \: \!\ \" \> \'\s]*\`\`\`",
    ],
    :code => [
        r"\`[a-zA-Z0-9 \( \) \+ \= \; \. \, \/ \@ \#\s \_ \- \: \!\ \? \{ \} \" \> \'\s]*\`",
    ],
    :type => [r"\:\:+[a-zA-Z0-9\.\,]*", r"\{+[a-zA-Z0-9 \,\.. ]*\}"],
    # :number => [
    #     r"[+-]?\d+[\.\,]?\d",
        # r" [0-9] ",
        # r"[ \+-\-][0-9]* ",
    # ]
)

"""
    highlight(text::AbstractString, theme::Theme)

Highlighs a text introducing markup to style semantically
relevant segments, colors specified by a theme object
"""
function highlight(text::AbstractString; theme::Theme=theme)
    for (like, regexes) in highlight_regexes
        markup = getfield(theme, like)

        prev_match = ""
        for regex in regexes
            text = replace(text, regex => s"[markup]\g<0>[/markup]")
            text = replace(text, "[markup]"=> "[$markup]")
            text = replace(text, "[/markup]"=> "[/$markup]")
        end
    end
    return text
end

"""
    highlight(text::AbstractString, theme::Theme, like::Symbol)

Hilights an entire text as if it was a type of semantically
relevant text of type :like.
"""
function highlight(text::AbstractString, like::Symbol; theme::Theme=theme)
    markup = getfield(theme, like)
    return do_by_line(x -> "[$markup]$x[/$markup]", chomp(text))
end

# shorthand to highlight objects based on type

highlight(x::Union{UnionAll, DataType}; theme::Theme=theme) = highlight(string(x), :type; theme=theme)
highlight(x::Number; theme::Theme=theme) = highlight(string(x), :number; theme=theme)
highlight(x::Function; theme::Theme=theme) = highlight(string(x), :func; theme=theme)
highlight(x::Symbol; theme::Theme=theme) = highlight(string(x), :symbol; theme=theme)
highlight(x::Expr; theme::Theme=theme) = highlight(string(x), :expression; theme=theme)
highlight(x::AbstractVector; theme::Theme=theme) = highlight(string(x), :number; theme=theme)
highlight(x; theme=theme) = string(x)  # capture all other cases

# ------------------------------ Highlighters.jl ----------------------------- #

"""
    Format.render(io::IO, ::MIME"text/ansi", tokens::Format.TokenIterator)

custom ANSI lexer for Highlighters.jl
"""
function Format.render(io::IO, ::MIME"text/ansi", tokens::Format.TokenIterator)
    for (str, id, style) in tokens
        fg = style.fg.active ? map(Int, (style.fg.r, style.fg.g, style.fg.b)) : ""
        bg = style.bg.active ? map(Int, (style.bg.r, style.bg.g, style.bg.b)) : nothing

        bold = style.bold ? "bold" : ""
        italic = style.italic ? "italic" : ""
        underline = style.underline ? "underline" : ""
        bg = isnothing(bg) ? "" : "on_$(bg)"
        markup = "$fg $(bg) $(bold) $(italic) $(underline)"
        if length(strip(markup)) > 0
            print(io, "[$markup]$str[/$markup]")
        else
            print(io, str)
        end
    end
end

"""
    highlight_syntax(code::AbstractString; style::Bool=true)

Highlight Julia code syntax in a string.
"""
function highlight_syntax(code::AbstractString; style::Bool = true)
    txt = sprint(
        Highlights.highlight,
        MIME("text/ansi"),
        escape_brackets(code),
        Lexers.JuliaLexer,
        CodeTheme;
        context = stdout,
    )

    if style
        txt = apply_style(txt)
    end

    return txt
end

"""
    load_code_and_highlight(path::AbstractString, lineno::Int; δ::Int=3, width::INt=120)

Load a file, get the code and format it. Return styled text
"""
function load_code_and_highlight(path::AbstractString, lineno::Int; δ::Int = 3)
    η = countlines(path)
    @assert lineno > 0 "lineno must be ≥1"
    @assert lineno ≤ η "lineno $lineno too high for file with $(η) lines"

    lines = read_file_lines(path, lineno - 9, lineno + 10)
    linenos = [ln[1] for ln in lines]
    lines = [ln[2] for ln in lines]
    code = split(highlight_syntax(join(lines); style = false), "\n")

    # clean
    clean(line) = replace(line, "    [/    ]" => "")
    codelines = clean.(code)  # [10-δ:10+δ]
    linenos = linenos  # [10-δ:10+δ]

    # make n lines match
    if lineno ≤ δ
        codelines = clean.(code)[1:(lineno + δ)]
        linenos = linenos[1:(lineno + δ)]
    elseif η - lineno ≤ δ
        codelines = clean.(code)[(end - δ):end]
        linenos = linenos[(end - δ):end]
    else
        codelines = clean.(code)[(10 - δ):(10 + δ)]
        linenos = linenos[(10 - δ):(10 + δ)]
    end

    # format
    _len = textlen ∘ lstrip ∘ remove_markup ∘ remove_markup_open
    dedent = 100
    for ln in codelines
        if _len(ln) > 1
            dedent = min(dedent, textlen(ln) - _len(ln))
        end
    end
    dedent = dedent < 1 ? 1 : dedent

    cleaned_lines = []
    for (n, line) in zip(linenos, codelines)
        # style
        if n == lineno
            symb = "[red bold]❯[/red bold]"
            color = "white"
        else
            symb = " "
            color = "grey39"
        end

        # end
        line = textlen(line) > 1 ? line[dedent:end] : line
        line = symb * " [$color]$n[/$color] " * line
        push!(cleaned_lines, line)
    end

    return join(cleaned_lines, "\n")
end


text:
const PRIMITIV_C_BOOL = Cuint
const PRIMITIV_C_FALSE = 0
const PRIMITIV_C_TRUE = 1
const PRIMITIV_C_STATUS = Cuint
const PRIMITIV_C_OK = 0
const PRIMITIV_C_ERROR = -1

const primitivShape_t = Void
const primitivTensor_t = Void

macro call(fun, args...)
    status = Expr(:call, :ccall, ("primitiv$fun", "libprimitiv_c"), :PRIMITIV_C_STATUS, args...)
    err = gensym()
    esc(:(if ($err = $status) != PRIMITIV_C_OK; error(get_message()); end))
end

function get_message()
    size_ptr = Ref{UInt32}(0)
    status = ccall((:primitivGetMessage, :libprimitiv_c), PRIMITIV_C_STATUS, (Cstring, Ptr{Cuint}), C_NULL, size_ptr)
    @assert status == PRIMITIV_C_OK
    message = "0" ^ size_ptr[]
    status = ccall((:primitivGetMessage, :libprimitiv_c), PRIMITIV_C_STATUS, (Cstring, Ptr{Cuint}), message, size_ptr)
    @assert status == PRIMITIV_C_OK
    message
end


text:
module TestDoctest

using Documenter
using Test
using MicroCollections

@testset "doctest" begin
    if lowercase(get(ENV, "JULIA_PKGEVAL", "false")) == "true"
        @info "Skipping doctests on PkgEval."
    elseif VERSION < v"1.5-"
        @info "Skipping doctests on Julia `$VERSION`."
    else
        doctest(MicroCollections; manual = false)
    end
end

end  # module