text:
# L1 norm (times a constant, or weighted)

immutable NormL1{T <: Union{Real, AbstractArray}} <: ProximableConvex
  lambda::T
  function NormL1(lambda::T)
    if !(eltype(lambda) <: Real)
      error("λ must be real")
    end
    if any(lambda .< 0)
      error("λ must be nonnegative")
    else
      new(lambda)
    end
  end
end

"""
  NormL1(λ::Real=1.0)

Returns the function `g(x) = λ||x||_1`, for a real parameter `λ ⩾ 0`.
"""

NormL1{R <: Real}(lambda::R=1.0) = NormL1{R}(lambda)

"""
  NormL1(λ::Array{Real})

Returns the function `g(x) = sum(λ_i|x_i|, i = 1,...,n)`, for a vector of real
parameters `λ_i ⩾ 0`.
"""

NormL1{A <: AbstractArray}(lambda::A) = NormL1{A}(lambda)

function (f::NormL1{R}){R <: Real}(x::AbstractArray)
  return f.lambda*vecnorm(x,1)
end

function (f::NormL1{A}){A <: AbstractArray}(x::AbstractArray)
  return vecnorm(f.lambda.*x,1)
end

function prox!{A <: AbstractArray, R <: Real}(f::NormL1{A}, x::AbstractArray{R}, y::AbstractArray{R}, gamma::Real=1.0)
  fy = zero(R)
  for i in eachindex(x)
    gl = gamma*f.lambda[i]
    y[i] = x[i] + (x[i] <= -gl ? gl : (x[i] >= gl ? -gl : -x[i]))
  end
  return sum(f.lambda .* abs.(y))
end

function prox!{A <: AbstractArray, R <: Real}(f::NormL1{A}, x::AbstractArray{Complex{R}}, y::AbstractArray{Complex{R}}, gamma::Real=1.0)
  fy = zero(R)
  for i in eachindex(x)
    gl = gamma*f.lambda[i]
    y[i] = sign(x[i])*(abs(x[i]) <= gl ? 0 : abs(x[i]) - gl)
  end
  return sum(f.lambda .* abs.(y))
end

function prox!{T <: Real, R <: Real}(f::NormL1{T}, x::AbstractArray{R}, y::AbstractArray{R}, gamma::Real=1.0)
  n1y = zero(R)
  gl = gamma*f.lambda
  for i in eachindex(x)
    y[i] = x[i] + (x[i] <= -gl ? gl : (x[i] >= gl ? -gl : -x[i]))
    n1y += y[i] > 0 ? y[i] : -y[i]
  end
  return f.lambda*n1y
end

function prox!{T <: Real, R <: Real}(f::NormL1{T}, x::AbstractArray{Complex{R}}, y::AbstractArray{Complex{R}}, gamma::Real=1.0)
  gl = gamma*f.lambda
  n1y = zero(R)
  for i in eachindex(x)
    y[i] = sign(x[i])*(abs(x[i]) <= gl ? 0 : abs(x[i]) - gl)
    n1y += abs(y[i])
  end
  return f.lambda*n1y
end

fun_name(f::NormL1) = "weighted L1 norm"
fun_dom(f::NormL1) = "AbstractArray{Real}, AbstractArray{Complex}"
fun_expr{R <: Real}(f::NormL1{R}) = "x ↦ λ||x||_1"
fun_expr{A <: AbstractArray}(f::NormL1{A}) = "x ↦ sum( λ_i |x_i| )"
fun_params{R <: Real}(f::NormL1{R}) = "λ = $(f.lambda)"
fun_params{A <: AbstractArray}(f::NormL1{A}) = string("λ = ", typeof(f.lambda), " of size ", size(f.lambda))

function prox_naive{T <: RealOrComplex}(f::NormL1, x::AbstractArray{T}, gamma::Real=1.0)
  y = sign.(x).*max.(0.0, abs.(x)-gamma*f.lambda)
  return y, vecnorm(f.lambda.*y,1)
end


text:
#=
spatial:
- Julia version:
- Author: Dan
- Date: 2021-10-05
=#
export ramachandranangles, dihedralangle, phiangle, phiangles, psiangle, psiangles, rmsd, distance

ramachandranangles(kern::KernelInterface) = begin
    phiangles(kern), psiangles(kern)
end

ramachandranangles(kern::DataFrameSystem) = begin
    phiangles(kern), psiangles(kern)
end

"""
    dihedralangle(atom_a, atom_b, atom_c, atom_d)
    dihedralangle(vec_ab, vec_bc, vec_cd)
Calculate the dihedral angle in radians defined by four `AbstractAtom`s or
three vectors.
The angle between the planes defined by atoms (A, B, C) and (B, C, D) is
returned in the range -π to π.
Credit to this function goes to [BioStructures](https://doi.org/10.1093/bioinformatics/btaa502)
"""
dihedralangle(at_a::Union{DataFrameRow,AbstractAtom}, at_b::Union{DataFrameRow,AbstractAtom},
                at_c::Union{DataFrameRow,AbstractAtom}, at_d::Union{DataFrameRow,AbstractAtom}) =
    dihedralangle(
        coords(at_b) - coords(at_a),
        coords(at_c) - coords(at_b),
        coords(at_d) - coords(at_c)
    )

dihedralangle(vec_a::SVector{3,Float64}, vec_b::SVector{3,Float64}, vec_c::SVector{3,Float64}) =
    return atan(
        dot(cross(cross(vec_a, vec_b), cross(vec_b, vec_c)), vec_b / norm(vec_b)),
        dot(cross(vec_a, vec_b), cross(vec_b, vec_c)))

phiangle(prev_res::Union{T, DataFrameRow}, res::Union{T, DataFrameRow}) where T<: AbstractResidue = begin

    if !sequentialResidues(prev_res, res)
        return NaN
    end

    children = getChildren(res)
    child_names_res::Vector{String} = [x.name_ for x in children]
    child_names_prev_res::Vector{String} = [x.name_ for x in getChildren(prev_res)]
    if !in("C", child_names_prev_res) && !all(in(x, child_names_res) for x in ["C", "CA","N"])
        return NaN
    else
        c_atom_prev::Union{AbstractAtom, DataFrameRow} = getChildren(prev_res)[findfirst(x -> x == "C", child_names_prev_res)]
        n_atom::Union{AbstractAtom, DataFrameRow} =  children[findfirst(x -> x == "N", child_names_res)]
        ca_atom::Union{AbstractAtom, DataFrameRow} = children[findfirst(x -> x == "CA", child_names_res)]
        c_atom::Union{AbstractAtom, DataFrameRow} =  children[findfirst(x -> x == "C", child_names_res)]
        return dihedralangle(c_atom_prev, n_atom, ca_atom, c_atom)
    end
end

phiangles(kern::KernelInterface) = begin
    res_list = collectResidues(kern)
    if length(res_list) < 2
        throw(ArgumentError("At least 2 residues required to calculate dihedral angles"))
    end

    phi_angles = Float64[NaN]

    for i in 2:length(res_list)
        res = res_list[i]
        res_prev = res_list[i - 1]
        push!(phi_angles, phiangle(res_prev, res))
    end

    return phi_angles
end

phiangles(kern::DataFrameSystem) = begin
    res_list = eachResidue(kern)
    if length(res_list) < 2
        throw(ArgumentError("At least 2 residues required to calculate dihedral angles"))
    end

    phi_angles = Float64[NaN]

    for i in 2:length(res_list)
        res = res_list[i]
        res_prev = res_list[i - 1]
        push!(phi_angles, phiangle(res_prev, res))
    end

    return phi_angles
end

psiangle(res::Union{T, DataFrameRow}, res_next::Union{T, DataFrameRow}) where T<: AbstractResidue = begin

    if !sequentialResidues(res, res_next)
        return NaN
    end

    children = getChildren(res)
    child_names_res::Vector{String} = [x.name_ for x in children]
    child_names_next_res::Vector{String} = [x.name_ for x in getChildren(res_next)]
    if !in("N", child_names_next_res) && !all(in(x, child_names_res) for x in ["C", "CA","N"])
        return NaN
    else
        n_atom::Union{AbstractAtom, DataFrameRow} = children[findfirst(x -> x == "N", child_names_res)]
        n_atom_next::Union{AbstractAtom, DataFrameRow} = getChildren(res_next)[findfirst(x -> x == "N", child_names_next_res)]
        ca_atom::Union{AbstractAtom, DataFrameRow} = children[findfirst(x -> x == "CA", child_names_res)]
        c_atom::Union{AbstractAtom, DataFrameRow} = children[findfirst(x -> x == "C", child_names_res)]
        return dihedralangle(n_atom, ca_atom, c_atom, n_atom_next)
    end
end

psiangles(kern::KernelInterface) = begin
    res_list = collectResidues(kern)
    if length(res_list) < 2
        throw(ArgumentError("At least 2 residues required to calculate dihedral angles"))
    end

    psi_angles = Float64[]

    for i in 1:length(res_list) -1
        res = res_list[i]
        res_next = res_list[i + 1]
        push!(psi_angles, psiangle(res, res_next))
    end

    push!(psi_angles, NaN)
    return psi_angles
end

rmsd(dfs1::DataFrameSystem, dfs2::DataFrameSystem; calphaonly=true) = begin
    if calphaonly
        atoms1 = filter(x -> getName(x) == "CA", dfs1.atoms, view=true)
        atoms2 = filter(x -> getName(x) == "CA", dfs2.atoms, view=true)
    else
        atoms1 = dfs1.atoms
        atoms2 = dfs2.atoms
    end
    if size(atoms1)[1] != size(atoms2)[1]
        throw(ArgumentError("Inputs are of size $(length(atoms1)) and " *
                            "$(length(atoms2)) but must be the same for RMSD"))
    end
    diff = getPosition(atoms1) - getPosition(atoms2)
    return sqrt.(dot(diff,diff) / length(diff))
end

rmsd(comp1::AbstractComposite, comp2::AbstractComposite,calphaonly=true) = begin
    if calphaonly
        atoms1 = filter(x -> getName(x) == "CA", eachAtom(comp1))
        atoms2 = filter(x -> getName(x) == "CA", eachAtom(comp2))
    else
        atoms1 = eachAtom(comp1)
        atoms2 = eachAtom(comp2)
    end
    if length(atoms1) != length(atoms2)
        throw(ArgumentError("Inputs are of size $(length(atoms1)) and " *
                            "$(length(atoms2)) but must be the same for RMSD"))
    end
    diff = getPosition(atoms1) - getPosition(atoms2)
    return sqrt.(dot(diff,diff) / length(diff))
end

distance(el1::Union{KernelInterface,AbstractDataFrame, DataFrameRow},
    el2::Union{KernelInterface,AbstractDataFrame, DataFrameRow}) = begin
    return sqrt(squaredDistance(el1,el2))
end

squaredDistance(comp1::Union{KernelInterface,AbstractDataFrame, DataFrameRow},
    comp2::Union{KernelInterface,AbstractDataFrame, DataFrameRow}) = begin
    atoms1 = collect(eachAtom(comp1))
    atoms2 = collect(eachAtom(comp2))
    min_dist = Inf
    for i = 1:length(atoms1)
        for j = i:length(atoms2)
            cur_dist::Float64 = sum( ( getPosition(atoms1[i]) - getPosition(atoms2[j]) ).^2)
            min_dist = min(min_dist, cur_dist)
        end
    end
    return min_dist
end

ext: jl


text:
using FlightGNC
using UnPack, ComponentArrays, Transducers
using LinearAlgebra, DifferentialEquations  # for callbacks

function main(p_M_0::Vector, v_M_0::Vector, p_T_0::Vector, v_T_0::Vector, s_guidance::BPNG; A_M_max = Inf)
    @unpack dim = s_guidance
    # Environments
    pursuer = PointMassMissile(dim, A_M_max)
    evador = PointMassMissile(dim, Inf)
    env = PursuerEvadorMissile(pursuer, evador)

    # Initial condition
    x0_pursuer = State(pursuer)(p_M_0, v_M_0)
    x0_evador = State(evador)(p_T_0, v_T_0)
    x0 = State(env)(x0_pursuer, x0_evador)

    # Simulation parameters
    Δt      = 0.01
    t_sim_f = 200

    # callbacks
    function condition_stop(u, t, integrator)
        (p_M, v_M, p_T, v_T) = (u.pursuer.p, u.pursuer.v, u.evador.p , u.evador.v)
        r = norm(p_T-p_M)
        ṙ = dot(p_T-p_M, v_T-v_M) / r
        r < 0.1  || (r < 10 && ṙ >= 0)
    end
    affect!(integrator) = terminate!(integrator)  # See DiffEq.jl documentation
    cb_stop    = DiscreteCallback(condition_stop, affect!)
    cb = CallbackSet(cb_stop)  # useful for multiple callbacks

    # Execute Simulation
    # prob: DE problem, df: DataFrame
    simulator = Simulator(x0,
                        apply_inputs(Dynamics!(env, s_guidance);
                                    u_pursuer = BPNG_cmd(s_guidance),
                                    u_evador = (x, params, t) -> zeros(evador.dim));
                        Problem = :ODE,
                        solver = Tsit5(),
                        tf = t_sim_f)

    # Non-interactive simulation: solve approach (automatically reinitialised)
    @time df = solve(simulator;
                    savestep = Δt,
                    callback = cb,
                    reltol = 1e-8,
                    abstol = 1e-8)

    # # Execute Simulation
    # # prob: DE problem, df: DataFrame
    # @time prob, df = FSimBase.sim(
    #                      x0,  # initial condition
    #                      apply_inputs(Dynamics!(env,s_guidance);
    #                                   u_pursuer = BPNG_cmd(s_guidance),
    #                                   u_evador = (x, params, t) -> zeros(evador.dim));  # dynamics!; apply_inputs is exported from FS and is so useful for systems with inputs
    #                      tf = t_sim_f,
    #                      savestep = Δt,  # savestep is NOT simulation step
    #                      solver = Tsit5(),
    #                      callback = cb,
    #                      reltol = 1e-8,
    #                      abstol = 1e-8
    #                     )  # sim is exported from FS

	return df
end


text:
struct Period
    st::Int
    en::Int
end
overlaps(a::Period, b::Period) = max(a.st, b.st) < min(a.en, b.en)

function process(case, timesltos)
    workers = Dict(:C => nothing, :J => nothing)
    res = []

    for minute in eachindex(periods)
        for k in keys(workers)
            # TODO
        end
    end

    if res
        println("Case #$(case): $(sol)")
    else
        println("Case #$(case): IMPOSSIBLE")
    end
end

function main()
    T = parse(Int, readline(stdin))
    for case in 1:T
        N = parse(Int, readline(stdin))
        timeslots = [Vector() for _ in 1:(24 * 60)]

        for _ in 1:N
            st, en = parse.(Int, stdin |> readline |> split)
            push!(timeslots[st], en)
        end

        process(case, timeslots)
        empty!(periods)
    end
end

isinteractive() || main()

max_stars_repo_name: AnAverageHuman/competitive

max_issues_repo_name: AnAverageHuman/competitive

max_forks_repo_name: AnAverageHuman/competitive


text:
using Base.Broadcast: broadcastable
using BenchmarkTools
using ChainRulesCore
using LinearAlgebra
using LinearAlgebra.BLAS: ger!, gemv!, gemv, scal!
using StaticArrays
using SparseArrays
using Test

@testset "ChainRulesCore" begin
    @testset "differentials" begin
        include("tangent_types/abstract_zero.jl")
        include("tangent_types/thunks.jl")
        include("tangent_types/tangent.jl")
        include("tangent_types/notimplemented.jl")
    end

    include("accumulation.jl")
    include("projection.jl")

    include("rules.jl")
    include("rule_definition_tools.jl")
    include("config.jl")
    include("ignore_derivatives.jl")

    include("deprecated.jl")
end


text:
function testCreateBufferCreateInfo()
    lava.stagingBuffer()
    lava.storageBuffer()
    lava.indexArrayBuffer(Csize_t(2))
    lava.downloadBuffer()
    lava.uniformBuffer()
    lava.raytracingBuffer()
    return true
end

function testBufferFunction(device)
    @scope begin
        eab = lava.createBuffer(device, lava.indexBuffer())
        eab.setDataVRAM([1, 2], UInt32)
        data = [UInt32(3), UInt32(4)]
        eab.pushData(data, Csize_t(sizeof(data)))
        # println(lava.graphicsQueue(device).activeBuffers())
        lava.graphicsQueue(device).catchUp(Int32(0))
        # println(lava.graphicsQueue(device).activeBuffers())
        outData = Vector{UInt32}(undef, 2)
        eab.getData(outData)
        @test outData[1] == 3
        @test outData[2] == 4
    end
    return true
end


text:
import Base: rand,
             ∈

export Ball2,
       sample

"""
    Ball2{N<:AbstractFloat} <: AbstractCentrallySymmetric{N}

Type that represents a ball in the 2-norm.

### Fields

- `center` -- center of the ball as a real vector
- `radius` -- radius of the ball as a real scalar (``≥ 0``)

### Notes

Mathematically, a ball in the 2-norm is defined as the set

```math
\\mathcal{B}_2^n(c, r) = \\{ x ∈ \\mathbb{R}^n : ‖ x - c ‖_2 ≤ r \\},
```
where ``c ∈ \\mathbb{R}^n`` is its center and ``r ∈ \\mathbb{R}_+`` its radius.
Here ``‖ ⋅ ‖_2`` denotes the Euclidean norm (also known as 2-norm), defined as
``‖ x ‖_2 = \\left( \\sum\\limits_{i=1}^n |x_i|^2 \\right)^{1/2}`` for any
``x ∈ \\mathbb{R}^n``.

### Examples

Create a five-dimensional ball `B` in the 2-norm centered at the origin with
radius 0.5:

```jldoctest ball2_label
julia> B = Ball2(zeros(5), 0.5)
Ball2{Float64}([0.0, 0.0, 0.0, 0.0, 0.0], 0.5)
julia> dim(B)
5
```

Evaluate `B`'s support vector in the direction ``[1,2,3,4,5]``:

```jldoctest ball2_label
julia> σ([1.,2.,3.,4.,5.], B)
5-element Array{Float64,1}:
 0.06741998624632421
 0.13483997249264842
 0.20225995873897262
 0.26967994498529685
 0.3370999312316211
```
"""
struct Ball2{N<:AbstractFloat} <: AbstractCentrallySymmetric{N}
    center::Vector{N}
    radius::N

    # default constructor with domain constraint for radius
    function Ball2{N}(center::Vector{N}, radius::N) where {N<:AbstractFloat}
        @assert radius >= zero(N) "radius must not be negative"
        return new{N}(center, radius)
    end
end

# convenience constructor without type parameter
Ball2(center::Vector{N}, radius::N) where {N<:AbstractFloat} =
    Ball2{N}(center, radius)

# --- AbstractCentrallySymmetric interface functions ---

"""
    center(B::Ball2{N})::Vector{N} where {N<:AbstractFloat}

Return the center of a ball in the 2-norm.

### Input

- `B` -- ball in the 2-norm

### Output

The center of the ball in the 2-norm.
"""
function center(B::Ball2{N})::Vector{N} where {N<:AbstractFloat}
    return B.center
end

# --- LazySet interface functions ---

"""
    σ(d::AbstractVector{N}, B::Ball2{N}) where {N<:AbstractFloat}

Return the support vector of a 2-norm ball in a given direction.

### Input

- `d` -- direction
- `B` -- ball in the 2-norm

### Output

The support vector in the given direction.
If the direction has norm zero, the origin is returned.

### Notes

Let ``c`` and ``r`` be the center and radius of a ball ``B`` in the 2-norm,
respectively.
For nonzero direction ``d`` we have

```math
σ_B(d) = c + r \\frac{d}{‖d‖_2}.
```

This function requires computing the 2-norm of the input direction, which is
performed in the given precision of the numeric datatype of both the direction
and the set.
Exact inputs are not supported.
"""
function σ(d::AbstractVector{N}, B::Ball2{N}) where {N<:AbstractFloat}
    dnorm = norm(d, 2)
    if dnorm <= zero(N)
        return zeros(N, length(d))
    else
        return @. B.center + d * (B.radius / dnorm)
    end
end

"""
    ∈(x::AbstractVector{N}, B::Ball2{N})::Bool where {N<:AbstractFloat}

Check whether a given point is contained in a ball in the 2-norm.

### Input

- `x` -- point/vector
- `B` -- ball in the 2-norm

### Output

`true` iff ``x ∈ B``.

### Notes

This implementation is worst-case optimized, i.e., it is optimistic and first
computes (see below) the whole sum before comparing to the radius.
In applications where the point is typically far away from the ball, a fail-fast
implementation with interleaved comparisons could be more efficient.

### Algorithm

Let ``B`` be an ``n``-dimensional ball in the 2-norm with radius ``r`` and let
``c_i`` and ``x_i`` be the ball's center and the vector ``x`` in dimension
``i``, respectively.
Then ``x ∈ B`` iff ``\\left( ∑_{i=1}^n |c_i - x_i|^2 \\right)^{1/2} ≤ r``.

### Examples

```jldoctest
julia> B = Ball2([1., 1.], sqrt(0.5))
Ball2{Float64}([1.0, 1.0], 0.7071067811865476)
julia> ∈([.5, 1.6], B)
false
julia> ∈([.5, 1.5], B)
true
```
"""
function ∈(x::AbstractVector{N}, B::Ball2{N})::Bool where {N<:AbstractFloat}
    @assert length(x) == dim(B)
    sum = zero(N)
    for i in eachindex(x)
        sum += (B.center[i] - x[i])^2
    end
    return sqrt(sum) <= B.radius
end

"""
    rand(::Type{Ball2}; [N]::Type{<:Real}=Float64, [dim]::Int=2,
         [rng]::AbstractRNG=GLOBAL_RNG, [seed]::Union{Int, Nothing}=nothing
        )::Ball2{N}

Create a random ball in the 2-norm.

### Input

- `Ball2` -- type for dispatch
- `N`     -- (optional, default: `Float64`) numeric type
- `dim`   -- (optional, default: 2) dimension
- `rng`   -- (optional, default: `GLOBAL_RNG`) random number generator
- `seed`  -- (optional, default: `nothing`) seed for reseeding

### Output

A random ball in the 2-norm.

### Algorithm

All numbers are normally distributed with mean 0 and standard deviation 1.
Additionally, the radius is nonnegative.
"""
function rand(::Type{Ball2};
              N::Type{<:Real}=Float64,
              dim::Int=2,
              rng::AbstractRNG=GLOBAL_RNG,
              seed::Union{Int, Nothing}=nothing
             )::Ball2{N}
    rng = reseed(rng, seed)
    center = randn(rng, N, dim)
    radius = abs(randn(rng, N))
    return Ball2(center, radius)
end

"""
    translate(B::Ball2{N}, v::AbstractVector{N}) where {N<:AbstractFloat}

Translate (i.e., shift) a ball in the 2-norm by a given vector.

### Input

- `B` -- ball in the 2-norm
- `v` -- translation vector

### Output

A translated ball in the 2-norm.

### Algorithm

We add the vector to the center of the ball.
"""
function translate(B::Ball2{N}, v::AbstractVector{N}) where {N<:AbstractFloat}
    @assert length(v) == dim(B) "cannot translate a $(dim(B))-dimensional " *
                                "set by a $(length(v))-dimensional vector"
    return Ball2(center(B) + v, B.radius)
end

"""
    sample(B::Ball2{N}, nsamples::Int=1;
           [rng]::AbstractRNG=GLOBAL_RNG,
           [seed]::Union{Int, Nothing}=nothing) where {N<:AbstractFloat}

Return samples from a uniform distribution on the given ball in the 2-norm.

### Input

- `B`        -- ball in the 2-norm
- `nsamples` -- (optional, default: `1`) number of samples
- `rng`      -- (optional, default: `GLOBAL_RNG`) random number generator
- `seed`     -- (optional, default: `nothing`) seed for reseeding

### Output

A linear array of `nsamples` elements drawn from a uniform distribution in `B`.

### Algorithm

Random sampling with uniform distribution in `B` is computed using Muller's method
of normalized Gaussians. This function requires the package `Distributions`.
See `_sample_unit_nball_muller!` for implementation details.
"""
function sample(B::Ball2{N}, nsamples::Int=1;
                rng::AbstractRNG=GLOBAL_RNG,
                seed::Union{Int, Nothing}=nothing) where {N<:AbstractFloat}
    require(:Distributions; fun_name="sample")
    n = dim(B)
    D = Vector{Vector{N}}(undef, nsamples) # preallocate output
    _sample_unit_nball_muller!(D, n, nsamples, rng=rng, seed=seed)

    # customize for the given ball
    r, c = B.radius, B.center
    @inbounds for i in 1:nsamples
        axpby!(one(N), c, r, D[i])
    end
    return D
end


text:
include("common.jl")

using FactCheck
using QSpice.Gates, QSpice.State, QSpice.BitOps

const RUNS_PER_STATE = 100
const QUBITS = [QUBIT0, QUBIT1]

function randomtest(fn, minbits, maxbits)
    for i = minbits:maxbits
        for _=1:RUNS_PER_STATE
            teststate = fromstates([rand(QUBITS) for _ in 1:i]...)
            fn(teststate)
        end
    end
end

facts("Quantum state creation sanity tests") do
    context("Single qubit state") do
        @fact QUBIT0 --> roughly([1.0 + 0im, 0.0 + 0im])
        @fact QUBIT1 --> roughly([0.0 + 0im, 1.0 + 0im])
    end

    context("Basis vectors for N qubit states") do
        randomtest(2, 10) do teststate
            @fact countnz(teststate.vector) --> 1
        end
    end
end

facts("Hadamard gate") do
    context("Single qubit mapping") do
        @fact hadamard(QUBIT0, 1) --> roughly((1.0 / sqrt(2)) .* [1.0 + 0im,  1])
        @fact hadamard(QUBIT1, 1) --> roughly((1.0 / sqrt(2)) .* [1.0 + 0im, -1])
    end

    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        isq = 1.0 / sqrt(2)
        @fact hadamard(s00, 1) --> roughly(isq .* [1.0 + 0im,  0,  1,  0])
        @fact hadamard(s00, 2) --> roughly(isq .* [1.0 + 0im,  1,  0,  0])

        @fact hadamard(s01, 1) --> roughly(isq .* [0.0 + 0im,  1,  0,  1])
        @fact hadamard(s01, 2) --> roughly(isq .* [1.0 + 0im, -1,  0,  0])

        @fact hadamard(s10, 1) --> roughly(isq .* [1.0 + 0im,  0, -1,  0])
        @fact hadamard(s10, 2) --> roughly(isq .* [0.0 + 0im,  0,  1,  1])

        @fact hadamard(s11, 1) --> roughly(isq .* [0.0 + 0im,  1,  0, -1])
        @fact hadamard(s11, 2) --> roughly(isq .* [0.0 + 0im,  0,  1, -1])
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits)
            @fact hadamard(hadamard(teststate, x), x) --> roughly(teststate)
        end
    end
end

facts("Not gate") do
    context("Single qubit mapping") do
        @fact Gates.not(QUBIT0, 1) --> roughly(QUBIT1)
        @fact Gates.not(QUBIT1, 1) --> roughly(QUBIT0)
    end

    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact Gates.not(s00, 1) --> roughly(s10)
        @fact Gates.not(s00, 2) --> roughly(s01)

        @fact Gates.not(s01, 1) --> roughly(s11)
        @fact Gates.not(s01, 2) --> roughly(s00)

        @fact Gates.not(s10, 1) --> roughly(s00)
        @fact Gates.not(s10, 2) --> roughly(s11)

        @fact Gates.not(s11, 1) --> roughly(s01)
        @fact Gates.not(s11, 2) --> roughly(s10)
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits)
            @fact Gates.not(Gates.not(teststate, x), x) --> roughly(teststate)
        end
    end
end

facts("CNOT gate") do
    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact cnot(s00, 1, 2) --> roughly(s00)
        @fact cnot(s00, 2, 1) --> roughly(s00)

        @fact cnot(s01, 1, 2) --> roughly(s01)
        @fact cnot(s01, 2, 1) --> roughly(s11)

        @fact cnot(s10, 1, 2) --> roughly(s11)
        @fact cnot(s10, 2, 1) --> roughly(s10)

        @fact cnot(s11, 1, 2) --> roughly(s10)
        @fact cnot(s11, 2, 1) --> roughly(s01)
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits - 1)
            y = rand(x + 1:teststate.bits)
            @fact cnot(cnot(teststate, x, y), x, y) --> roughly(teststate)
        end
    end
end

facts("CCNOT gate") do
    context("Three qubit mapping") do
        s000 = fromstates(QUBIT0, QUBIT0, QUBIT0)
        s001 = fromstates(QUBIT0, QUBIT0, QUBIT1)
        s010 = fromstates(QUBIT0, QUBIT1, QUBIT0)
        s011 = fromstates(QUBIT0, QUBIT1, QUBIT1)

        s100 = fromstates(QUBIT1, QUBIT0, QUBIT0)
        s101 = fromstates(QUBIT1, QUBIT0, QUBIT1)
        s110 = fromstates(QUBIT1, QUBIT1, QUBIT0)
        s111 = fromstates(QUBIT1, QUBIT1, QUBIT1)

        @fact ccnot(s000, 1, 2, 3) --> roughly(s000)
        @fact ccnot(s001, 1, 2, 3) --> roughly(s001)
        @fact ccnot(s010, 1, 2, 3) --> roughly(s010)
        @fact ccnot(s011, 1, 2, 3) --> roughly(s011)
        @fact ccnot(s100, 1, 2, 3) --> roughly(s100)
        @fact ccnot(s101, 1, 2, 3) --> roughly(s101)
        @fact ccnot(s110, 1, 2, 3) --> roughly(s111)
        @fact ccnot(s111, 1, 2, 3) --> roughly(s110)

        @fact ccnot(s011, 2, 3, 1) --> roughly(s111)
        @fact ccnot(s101, 1, 3, 2) --> roughly(s111)
    end

    context("Unitarity on N qubit states") do
        randomtest(3, 10) do teststate
            x = rand(1:teststate.bits - 2)
            y = rand(x + 1:teststate.bits - 1)
            z = rand(y + 1:teststate.bits)
            @fact ccnot(ccnot(teststate, x, y, z), x, y, z) --> roughly(teststate)
        end
    end
end

facts("Swap gate") do
    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact swap(s00, 1, 2) --> roughly(s00)
        @fact swap(s00, 2, 1) --> roughly(s00)

        @fact swap(s01, 1, 2) --> roughly(s10)
        @fact swap(s01, 2, 1) --> roughly(s10)

        @fact swap(s10, 1, 2) --> roughly(s01)
        @fact swap(s10, 2, 1) --> roughly(s01)

        @fact swap(s11, 1, 2) --> roughly(s11)
        @fact swap(s11, 2, 1) --> roughly(s11)
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits)
            y = rand(1:teststate.bits)
            @fact swap(swap(teststate, x, y), y, x) --> roughly(teststate)
        end
    end
end

facts("CSWAP gate") do
    context("Three qubit mapping") do
        s000 = fromstates(QUBIT0, QUBIT0, QUBIT0)
        s001 = fromstates(QUBIT0, QUBIT0, QUBIT1)
        s010 = fromstates(QUBIT0, QUBIT1, QUBIT0)
        s011 = fromstates(QUBIT0, QUBIT1, QUBIT1)

        s100 = fromstates(QUBIT1, QUBIT0, QUBIT0)
        s101 = fromstates(QUBIT1, QUBIT0, QUBIT1)
        s110 = fromstates(QUBIT1, QUBIT1, QUBIT0)
        s111 = fromstates(QUBIT1, QUBIT1, QUBIT1)

        @fact cswap(s000, 1, 2, 3) --> roughly(s000)
        @fact cswap(s001, 1, 2, 3) --> roughly(s001)
        @fact cswap(s010, 1, 2, 3) --> roughly(s010)
        @fact cswap(s011, 1, 2, 3) --> roughly(s011)
        @fact cswap(s100, 1, 2, 3) --> roughly(s100)
        @fact cswap(s101, 1, 2, 3) --> roughly(s110)
        @fact cswap(s110, 1, 2, 3) --> roughly(s101)
        @fact cswap(s111, 1, 2, 3) --> roughly(s111)

        @fact cswap(s011, 2, 1, 3) --> roughly(s110)
        @fact cswap(s101, 3, 1, 2) --> roughly(s011)
    end

    context("Unitarity on N qubit states") do
        randomtest(3, 10) do teststate
            x = rand(1:teststate.bits - 2)
            y = rand(x + 1:teststate.bits - 1)
            z = rand(y + 1:teststate.bits)
            @fact cswap(cswap(teststate, x, y, z), x, z, y) --> roughly(teststate)
        end
    end
end

facts("Square root swap gate") do
    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact sqrtswap(s00, 1, 2) --> roughly(s00)
        @fact sqrtswap(s00, 2, 1) --> roughly(s00)

        @fact sqrtswap(s01, 1, 2) --> roughly([0, 0.5 + 0.5im, 0.5 - 0.5im, 0])
        @fact sqrtswap(s01, 2, 1) --> roughly([0, 0.5 + 0.5im, 0.5 - 0.5im, 0])

        @fact sqrtswap(s10, 1, 2) --> roughly([0, 0.5 - 0.5im, 0.5 + 0.5im, 0])
        @fact sqrtswap(s10, 2, 1) --> roughly([0, 0.5 - 0.5im, 0.5 + 0.5im, 0])

        @fact sqrtswap(s11, 1, 2) --> roughly(s11)
        @fact sqrtswap(s11, 2, 1) --> roughly(s11)
    end

    context("Three qubit mapping") do
        s000 = fromstates(QUBIT0, QUBIT0, QUBIT0)
        s001 = fromstates(QUBIT0, QUBIT0, QUBIT1)
        s010 = fromstates(QUBIT0, QUBIT1, QUBIT0)
        s011 = fromstates(QUBIT0, QUBIT1, QUBIT1)

        s100 = fromstates(QUBIT1, QUBIT0, QUBIT0)
        s101 = fromstates(QUBIT1, QUBIT0, QUBIT1)
        s110 = fromstates(QUBIT1, QUBIT1, QUBIT0)
        s111 = fromstates(QUBIT1, QUBIT1, QUBIT1)
        @fact sqrtswap(s000, 1, 2) --> roughly(s000)
        @fact sqrtswap(s000, 2, 1) --> roughly(s000)
        @fact sqrtswap(s000, 1, 3) --> roughly(s000)
        @fact sqrtswap(s000, 3, 1) --> roughly(s000)
        @fact sqrtswap(s000, 2, 3) --> roughly(s000)
        @fact sqrtswap(s000, 3, 2) --> roughly(s000)

        @fact sqrtswap(s001, 1, 2) --> roughly(s001)
        @fact sqrtswap(s001, 2, 1) --> roughly(s001)
        @fact sqrtswap(s001, 1, 3) --> roughly([0, 0.5 + 0.5im, 0, 0, 0.5 - 0.5im, 0, 0, 0])
        @fact sqrtswap(s001, 3, 1) --> roughly([0, 0.5 + 0.5im, 0, 0, 0.5 - 0.5im, 0, 0, 0])
        @fact sqrtswap(s001, 2, 3) --> roughly([0, 0.5 + 0.5im, 0.5 - 0.5im, 0, 0, 0, 0, 0])
        @fact sqrtswap(s001, 3, 2) --> roughly([0, 0.5 + 0.5im, 0.5 - 0.5im, 0, 0, 0, 0, 0])

        @fact sqrtswap(s010, 1, 2) --> roughly([0, 0, 0.5 + 0.5im, 0, 0.5 - 0.5im, 0, 0, 0])
        @fact sqrtswap(s010, 2, 1) --> roughly([0, 0, 0.5 + 0.5im, 0, 0.5 - 0.5im, 0, 0, 0])
        @fact sqrtswap(s010, 1, 3) --> roughly(s010)
        @fact sqrtswap(s010, 3, 1) --> roughly(s010)
        @fact sqrtswap(s010, 2, 3) --> roughly([0, 0.5 - 0.5im, 0.5 + 0.5im, 0, 0, 0, 0, 0])
        @fact sqrtswap(s010, 3, 2) --> roughly([0, 0.5 - 0.5im, 0.5 + 0.5im, 0, 0, 0, 0, 0])

        @fact sqrtswap(s011, 1, 2) --> roughly([0, 0, 0, 0.5 + 0.5im, 0, 0.5 - 0.5im, 0, 0])
        @fact sqrtswap(s011, 2, 1) --> roughly([0, 0, 0, 0.5 + 0.5im, 0, 0.5 - 0.5im, 0, 0])
        @fact sqrtswap(s011, 1, 3) --> roughly([0, 0, 0, 0.5 + 0.5im, 0, 0, 0.5 - 0.5im, 0])
        @fact sqrtswap(s011, 3, 1) --> roughly([0, 0, 0, 0.5 + 0.5im, 0, 0, 0.5 - 0.5im, 0])
        @fact sqrtswap(s011, 2, 3) --> roughly(s011)
        @fact sqrtswap(s011, 3, 2) --> roughly(s011)

        @fact sqrtswap(s100, 1, 2) --> roughly([0, 0, 0.5 - 0.5im, 0, 0.5 + 0.5im, 0, 0, 0])
        @fact sqrtswap(s100, 2, 1) --> roughly([0, 0, 0.5 - 0.5im, 0, 0.5 + 0.5im, 0, 0, 0])
        @fact sqrtswap(s100, 1, 3) --> roughly([0, 0.5 - 0.5im, 0, 0, 0.5 + 0.5im, 0, 0, 0])
        @fact sqrtswap(s100, 3, 1) --> roughly([0, 0.5 - 0.5im, 0, 0, 0.5 + 0.5im, 0, 0, 0])
        @fact sqrtswap(s100, 2, 3) --> roughly(s100)
        @fact sqrtswap(s100, 3, 2) --> roughly(s100)

        @fact sqrtswap(s101, 1, 2) --> roughly([0, 0, 0, 0.5 - 0.5im, 0, 0.5 + 0.5im, 0, 0])
        @fact sqrtswap(s101, 2, 1) --> roughly([0, 0, 0, 0.5 - 0.5im, 0, 0.5 + 0.5im, 0, 0])
        @fact sqrtswap(s101, 1, 3) --> roughly(s101)
        @fact sqrtswap(s101, 3, 1) --> roughly(s101)
        @fact sqrtswap(s101, 2, 3) --> roughly([0, 0, 0, 0, 0, 0.5 + 0.5im, 0.5 - 0.5im, 0])
        @fact sqrtswap(s101, 3, 2) --> roughly([0, 0, 0, 0, 0, 0.5 + 0.5im, 0.5 - 0.5im, 0])

        @fact sqrtswap(s110, 1, 2) --> roughly(s110)
        @fact sqrtswap(s110, 2, 1) --> roughly(s110)
        @fact sqrtswap(s110, 1, 3) --> roughly([0, 0, 0, 0.5 - 0.5im, 0, 0, 0.5 + 0.5im, 0])
        @fact sqrtswap(s110, 3, 1) --> roughly([0, 0, 0, 0.5 - 0.5im, 0, 0, 0.5 + 0.5im, 0])
        @fact sqrtswap(s110, 2, 3) --> roughly([0, 0, 0, 0, 0, 0.5 - 0.5im, 0.5 + 0.5im, 0])
        @fact sqrtswap(s110, 3, 2) --> roughly([0, 0, 0, 0, 0, 0.5 - 0.5im, 0.5 + 0.5im, 0])

        @fact sqrtswap(s111, 1, 2) --> roughly(s111)
        @fact sqrtswap(s111, 2, 1) --> roughly(s111)
        @fact sqrtswap(s111, 1, 3) --> roughly(s111)
        @fact sqrtswap(s111, 3, 1) --> roughly(s111)
        @fact sqrtswap(s111, 2, 3) --> roughly(s111)
        @fact sqrtswap(s111, 3, 2) --> roughly(s111)
    end
end

facts("Phase shift gate") do
    context("Single qubit mapping") do
        @fact phaseshift(QUBIT0, 1, pi / 8) --> roughly(QUBIT0)
        @fact phaseshift(QUBIT1, 1, pi / 8) --> roughly([0.0 + 0im, exp(im * pi / 8)])
    end

    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)

        @fact phaseshift(s00, 1, pi / 8) --> roughly(s00)
        @fact phaseshift(s00, 2, pi / 8) --> roughly(s00)

        @fact phaseshift(s01, 1, pi / 8) --> roughly(s01)
        @fact phaseshift(s01, 2, pi / 8) --> roughly([0, exp(im * pi / 8), 0, 0])

        @fact phaseshift(s10, 1, pi / 8) --> roughly([0, 0, exp(im * pi / 8), 0])
        @fact phaseshift(s10, 2, pi / 8) --> roughly(s10)

        @fact phaseshift(s11, 1, pi / 8) --> roughly([0, 0, 0, exp(im * pi / 8)])
        @fact phaseshift(s11, 2, pi / 8) --> roughly([0, 0, 0, exp(im * pi / 8)])
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits)
            theta = rand(1.0:pi)
            @fact phaseshift(phaseshift(teststate, x, theta), x, -theta) --> roughly(teststate)
        end
    end
end

facts("Pauli-X gate") do
    context("Single qubit mapping") do
        @fact paulix(QUBIT0, 1) --> roughly(QUBIT1)
        @fact paulix(QUBIT1, 1) --> roughly(QUBIT0)
    end

    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact paulix(s00, 1) --> roughly(s10)
        @fact paulix(s00, 2) --> roughly(s01)

        @fact paulix(s01, 1) --> roughly(s11)
        @fact paulix(s01, 2) --> roughly(s00)

        @fact paulix(s10, 1) --> roughly(s00)
        @fact paulix(s10, 2) --> roughly(s11)

        @fact paulix(s11, 1) --> roughly(s01)
        @fact paulix(s11, 2) --> roughly(s10)
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits)
            @fact paulix(paulix(teststate, x), x) --> roughly(teststate)
        end
    end
end

facts("Pauli-Y gate") do
    context("Single qubit mapping") do
        @fact pauliy(QUBIT0, 1) --> roughly([0.0, -im])
        @fact pauliy(QUBIT1, 1) --> roughly([im, 0.0])
    end

    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact pauliy(s00, 1) --> roughly([0.0, 0, -im, 0])
        @fact pauliy(s00, 2) --> roughly([0.0, -im, 0, 0])

        @fact pauliy(s01, 1) --> roughly([0.0, 0, 0, -im])
        @fact pauliy(s01, 2) --> roughly([ im, 0, 0, 0.0])

        @fact pauliy(s10, 1) --> roughly([ im, 0, 0, 0.0])
        @fact pauliy(s10, 2) --> roughly([0.0, 0, 0, -im])

        @fact pauliy(s11, 1) --> roughly([0.0, im, 0, 0])
        @fact pauliy(s11, 2) --> roughly([0.0, 0, im, 0])
    end
end

facts("Pauli-Z gate") do
    context("Single qubit mapping") do
        @fact pauliz(QUBIT0, 1) --> roughly(QUBIT0)
        @fact pauliz(QUBIT1, 1) --> roughly([0.0 + 0im, -1])
    end

    context("Two qubit mapping") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)
        @fact pauliz(s00, 1) --> roughly(s00)
        @fact pauliz(s00, 2) --> roughly(s00)

        @fact pauliz(s01, 1) --> roughly(s01)
        @fact pauliz(s01, 2) --> roughly([0.0 + 0im, -1, 0, 0])

        @fact pauliz(s10, 1) --> roughly([0.0 + 0im, 0, -1, 0])
        @fact pauliz(s10, 2) --> roughly(s10)

        @fact pauliz(s11, 1) --> roughly([0.0 + 0im, 0, 0, -1])
        @fact pauliz(s11, 2) --> roughly([0.0 + 0im, 0, 0, -1])
    end

    context("Unitarity on N qubit states") do
        randomtest(2, 10) do teststate
            x = rand(1:teststate.bits)
            @fact pauliz(pauliz(teststate, x), x) --> roughly(teststate)
        end
    end
end

facts("Arbitrary unitary gates") do
    context("N-qubit Hadamard") do
        h = 1.0 / sqrt(2.0) * [1 1; 1 -1]
        randomtest(1, 5) do teststate
            x = rand(1:teststate.bits)
            @fact hadamard(teststate, x) --> roughly(unitary(teststate, h, x))
        end
    end

    context("N-qubit Not") do
        n = [0 1; 1 0]
        randomtest(1, 5) do teststate
            x = rand(1:teststate.bits)
            @fact Gates.not(teststate, x) --> roughly(unitary(teststate, n, x))
        end
    end

    context("N-qubit CNOT") do
        cn = [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0]
        randomtest(2, 5) do teststate
            x = rand(1:teststate.bits - 1)
            y = rand(x + 1:teststate.bits)
            @fact cnot(teststate, x, y) --> roughly(unitary(teststate, cn, x, y))
        end
    end

    context("N-qubit CCNOT") do
        ccn = [1 0 0 0 0 0 0 0;
               0 1 0 0 0 0 0 0;
               0 0 1 0 0 0 0 0;
               0 0 0 1 0 0 0 0;
               0 0 0 0 1 0 0 0;
               0 0 0 0 0 1 0 0;
               0 0 0 0 0 0 0 1;
               0 0 0 0 0 0 1 0]
        randomtest(3, 6) do teststate
            x = rand(1:teststate.bits - 2)
            y = rand(x + 1:teststate.bits - 1)
            z = rand(y + 1:teststate.bits)
            @fact ccnot(teststate, z, x, y) --> roughly(unitary(teststate, ccn, z, x, y))
        end
    end

    context("N-qubit SWAP") do
        s = [1 0 0 0; 0 0 1 0; 0 1 0 0; 0 0 0 1]
        randomtest(2, 5) do teststate
            x = rand(1:teststate.bits - 1)
            y = rand(x + 1:teststate.bits)
            @fact swap(teststate, x, y) --> roughly(unitary(teststate, s, x, y))
        end
    end
    context("N-qubit CSWAP") do
        cs = [1 0 0 0 0 0 0 0;
              0 1 0 0 0 0 0 0;
              0 0 1 0 0 0 0 0;
              0 0 0 1 0 0 0 0;
              0 0 0 0 1 0 0 0;
              0 0 0 0 0 0 1 0;
              0 0 0 0 0 1 0 0;
              0 0 0 0 0 0 0 1]
        randomtest(3, 6) do teststate
            x = rand(1:teststate.bits - 2)
            y = rand(x + 1:teststate.bits - 1)
            z = rand(y + 1:teststate.bits)
            @fact cswap(teststate, x, y, z) --> roughly(unitary(teststate, cs, x, y, z))
        end
    end
end

facts("Measurement") do
    context("Single qubit measurement") do
        posterior, measurement = measure(QUBIT0)
        @fact posterior --> roughly(QUBIT0)
        @fact measurement --> [0]

        posterior, measurement = measure(QUBIT1)
        @fact posterior --> roughly(QUBIT1)
        @fact measurement --> [1]
    end

    context("Equal probability statistical test") do
        s0 = fromvector([0.5, -0.5, 0 - 0.5im, 0 + 0.5im])

        nruns = 10000
        stats = [0, 0, 0, 0]
        for i = 1:nruns
            posterior, measurement = measure(s0)
            n = todecimal(measurement)
            @fact posterior[n + 1] --> 1.0 + 0.0im
            @fact countnz(posterior.vector) --> 1
            stats[n + 1] += 1
        end
        @fact stats[1] / nruns --> roughly(0.25, atol=0.1)
        @fact stats[2] / nruns --> roughly(0.25, atol=0.1)
        @fact stats[3] / nruns --> roughly(0.25, atol=0.1)
        @fact stats[4] / nruns --> roughly(0.25, atol=0.1)
    end

    context("Null probability statistical test") do
        rsqrt8 = 1.0 / sqrt(8)
        s0 = fromvector([rsqrt8 + rsqrt8 * im, rsqrt8 - rsqrt8 * im, 0.0,  1.0 / sqrt(2)])

        nruns = 10000
        stats = [0, 0, 0, 0]
        for i=1:nruns
            posterior, measurement = measure(s0)
            n = todecimal(measurement)
            @fact posterior[n + 1] --> 1.0 + 0.0im
            @fact countnz(posterior.vector) --> 1
            stats[n + 1] += 1
        end
        @fact stats[1] / nruns --> roughly(0.25, atol=0.1)
        @fact stats[2] / nruns --> roughly(0.25, atol=0.1)
        @fact stats[3] / nruns --> 0
        @fact stats[4] / nruns --> roughly(0.5, atol=0.1)
    end
end

# These tests are not that comprehensive, but they are hard to automate
facts("Partial Measurement") do
    context("Single qubit measurement") do
        posterior, measurement = partialmeasure(QUBIT0, 1)
        @fact posterior --> roughly(QUBIT0)
        @fact measurement --> [0]

        posterior, measurement = partialmeasure(QUBIT1, 1)
        @fact posterior --> roughly(QUBIT1)
        @fact measurement --> [1]
    end

    context("Two qubit measurement") do
        s00 = fromstates(QUBIT0, QUBIT0)
        s01 = fromstates(QUBIT0, QUBIT1)
        s10 = fromstates(QUBIT1, QUBIT0)
        s11 = fromstates(QUBIT1, QUBIT1)

        # State |00>
        posterior, measurement = partialmeasure(s00, 1)
        @fact measurement --> [0]
        @fact posterior --> roughly(QUBIT0)

        posterior, measurement = partialmeasure(s00, 2)
        @fact measurement --> [0]
        @fact posterior --> roughly(QUBIT0)

        posterior, measurement = partialmeasure(s00, 1, 2)
        @fact measurement --> [0, 0]
        @fact posterior --> roughly(s00)

        # State |01>
        posterior, measurement = partialmeasure(s01, 1)
        @fact measurement --> [0]
        @fact posterior --> roughly(QUBIT1)

        posterior, measurement = partialmeasure(s01, 2)
        @fact measurement --> [1]
        @fact posterior --> roughly(QUBIT0)

        posterior, measurement = partialmeasure(s01, 1, 2)
        @fact measurement --> [0, 1]
        @fact posterior --> roughly(s01)

        # State |10>
        posterior, measurement = partialmeasure(s10, 1)
        @fact measurement --> [1]
        @fact posterior --> roughly(QUBIT0)

        posterior, measurement = partialmeasure(s10, 2)
        @fact measurement --> [0]
        @fact posterior --> roughly(QUBIT1)

        posterior, measurement = partialmeasure(s10, 1, 2)
        @fact measurement --> [1, 0]
        @fact posterior --> roughly(s10)

        # State |11>
        posterior, measurement = partialmeasure(s11, 1)
        @fact measurement --> [1]
        @fact posterior --> roughly(QUBIT1)

        posterior, measurement = partialmeasure(s11, 2)
        @fact measurement --> [1]
        @fact posterior --> roughly(QUBIT1)

        posterior, measurement = partialmeasure(s11, 1, 2)
        @fact measurement --> [1, 1]
        @fact posterior --> roughly(s11)

        # Try states with different probabilities
        h = hadamard(s00, 1)
        for i=1:100
            posterior, measurement = partialmeasure(h, 1)
            @fact posterior --> roughly(QUBIT0)
            posterior, measurement = partialmeasure(h, 2)
            @fact measurement --> [0]
            @fact posterior --> roughly(1.0 / sqrt(2) .* [1.0 + 0im, 1])
        end

        # Partially measuring the same bit multiple time should have the same effect
        # as measuring it only once
        p1, m1 = partialmeasure(s11, 1, 1, 1)
        p2, m2 = partialmeasure(s11, 1)
        @fact p1 --> roughly(p2)
        @fact m2 --> m2

        p1, m1 = partialmeasure(s11, 1, 1, 2, 2, 1, 2)
        p2, m2 = partialmeasure(s11, 1, 2)
        @fact p1 --> roughly(p2)
        @fact m2 --> m2
    end
end

max_stars_repo_name: leios/quantum

max_issues_repo_name: leios/quantum

max_forks_repo_name: leios/quantum


text:
function PegasusSolver()
    return PegasusSolver(())
end

function PegasusSolver(arg0::jdouble)
    return PegasusSolver((jdouble,), arg0)
end

function PegasusSolver(arg0::jdouble, arg1::jdouble)
    return PegasusSolver((jdouble, jdouble), arg0, arg1)
end

function PegasusSolver(arg0::jdouble, arg1::jdouble, arg2::jdouble)
    return PegasusSolver((jdouble, jdouble, jdouble), arg0, arg1, arg2)
end


text:
function test_threshold_percentage()
    println("-- Testing ThresholdPercentage recommender")

    data = DataAccessor(sparse([1 2 3; 4 5 6]))
    recommender = ThresholdPercentage(data, 2.0)
    build!(recommender)
    @test ranking(recommender, 1, 1) == 50.0
    @test ranking(recommender, 1, 2) == 100.0
end

test_threshold_percentage()


text:
# RK4 is a function which performs one step of the Runge-Kutta 4 ray tracing
#
# EndPoint = RK4(StartPoint, GradientVolume, StepSize);
#
# inputs :
#      StartPoint: 2D or 3D location in vectorfield
#      GradientVolume: Vectorfield
#      Stepsize : The stepsize
#
# outputs :
#      EndPoint : The new location (zero if outside image)
#
# Function is written by D.Kroon University of Twente (July 2008)
# Port from C to Julia by Lars Hellemo (May 2014)

function checkBounds2d(point, Isize)
    if (point[1] < 1) ||
       (point[2] < 1) ||
       (point[1] > (Isize[1])) ||
       (point[2] > (Isize[2]))
        return false
    end
    return true
end

function interpgrad2d!(Ireturn::AbstractArray{T}, I, Isize, point) where {T}
    #  Linear interpolation variables
    perc = zeros(T, 4)
    index = fill(4, 4, 1)
    fTlocalx = floor(point[1])
    fTlocaly = floor(point[2])
    xBas0 = Int(fTlocalx)
    yBas0 = Int(fTlocaly)
    xBas1 = xBas0 + 1
    yBas1 = yBas0 + 1

    # Linear interpolation constants (percentages)
    xCom = point[1] - fTlocalx
    yCom = point[2] - fTlocaly
    xComi = (1 - xCom)
    yComi = (1 - yCom)
    perc[1] = xComi * yComi
    perc[2] = xComi * yCom
    perc[3] = xCom * yComi
    perc[4] = xCom * yCom

    # Stick to boundary
    if xBas0 < 1
        xBas0 = 1
        if xBas1 < 1
            xBas1 = 1
        end
    end

    if yBas0 < 1
        yBas0 = 1
        if yBas1 < 1
            yBas1 = 1
        end
    end
    if xBas1 > Isize[1]
        xBas1 = Isize[1]
        if xBas0 > Isize[1]
            xBas0 = Isize[1]
        end
    end

    if yBas1 > Isize[2]
        yBas1 = Isize[2]
        if yBas0 > Isize[2]
            yBas0 = Isize[2]
        end
    end

    # Get the neighbour intensities
    index[1] = LinearIndices(size(I))[xBas0, yBas0, 1] #TODO Why is this dim 3,3,2?
    index[2] = LinearIndices(size(I))[xBas0, yBas1, 1]
    index[3] = LinearIndices(size(I))[xBas1, yBas0, 1]
    index[4] = LinearIndices(size(I))[xBas1, yBas1, 1]

    f = Isize[1] * Isize[2]

    # the interpolated color
    Ireturn[1] = sum(I[index[i]] * perc[i] for i in 1:4)
    Ireturn[2] = sum(I[index[i] + f] * perc[i] for i in 1:4)
    return Ireturn
end

"""
    Perform the RK4 raytracing step
"""
function rk4(
    startpoint::AbstractArray{T}, gradientvolume::AbstractArray{T}, stepsize::Number
) where {T}
    gradientarray = gradientvolume # TODO Test if this works (without copy)
    gradientarraysize = collect(size(gradientarray))
    nextpoint = startpoint
    nextPoint = rk4step2d(gradientarray, startpoint, ones(T, 2), stepsize)
    return nextPoint
end

"""
Deprecated name for rk4step2d
    """
function RK4STEP_2D(
    gradientArray::AbstractArray{T}, startPoint::AbstractArray{T}, nextPoint, stepSize
) where {T}
    return rk4step2d(gradientArray, startPoint, nextPoint, stepSize)
end
@deprecate RK4STEP_2D(gradientArray, startPoint, nextPoint, stepSize) rk4step2d(
    gradientarray, startpoint, nextpoint, stepsize
)
"""
    Perform one step of the RK4 algorithm
"""
function rk4step2d(
    gradientarray::AbstractArray{T}, startpoint::AbstractArray{T}, nextpoint, stepsize
) where {T}
    k = ones(T, 4, 2)
    temppoint = ones(T, 2)
    gradientarraysize = collect(size(gradientarray))

    # Calculate k1:k4
    for ki in 1:4
        @views interpgrad2d!(k[ki, :], gradientarray, gradientarraysize, startpoint)
        @views tempnorm = max(norm(k[ki, :]), 1e-6)
        for p in 1:2
            k[ki, p] *= stepsize / tempnorm
            temppoint[p] = startpoint[p] - k[ki, p] * T(0.5)
        end
        # Check if still inside the domain
        if !checkBounds2d(temppoint, gradientarraysize)
            return ones(T, 2)
        end
    end

    # Calculate final point
    for p in 1:2
        nextpoint[p] =
            startpoint[p] -
            (k[1, p] + k[2, p] * T(2.0) + k[3, p] * T(2.0) + k[4, p]) / T(6.0)
    end

    # Set step to step size
    # D1 = nextPoint[1] - startPoint[1]
    # D2 = nextPoint[2] - startPoint[2]
    # dl = stepSize/(sqrt(D1 * D1 + D2 * D2)+1e-15)
    # D1 *= dl
    # D2 *= dl
    # nextPoint[1] = startPoint[1] + D1
    # nextPoint[2] = startPoint[2] + D2

    # Check the if are still inside the domain
    if !checkBounds2d(nextpoint, gradientarraysize)
        return ones(T, 2)
    end
    return nextpoint
end


text:
# This file was generated, do not modify it. # hide
ẑ.machine.model.lambda = 5.0;


text:
@testset "Macros" begin

s = """
macro rr_str(pattern, flags...) Regex(pattern, flags...) end
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
localize_vars(foo, bar) = (foo, bar)
macro myschedule(expr)
    expr = localize_vars(:(()->(\$expr)), false)
    :(enq_work(Task(\$(esc(expr)))))
end
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
@deprecate put put!
@deprecate readsfrom(cmd, args...)      open(cmd, "r", args...)
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
function f()
    @gensym x y z
    println(x)
    println(y)
    println(z)
end
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
macro mymacro(expr::Expr)
    expr
end
"""
msgs = lintstr(s)
@test isempty(msgs)

#= (issues #140)
s = """
macro mymacro(expr::Int)
    expr
end
"""
msgs = lintstr(s)
@test msgs[1].code == :E522
@test occursin("macro arguments can only be Symbol/Expr", msgs[1].message)
=#

s = """
# using PyCall # we don't want to uncomment this
@pyimport seaborn as sns
@pyimport seaborn
"""
msgs = lintstr(s)
@test isempty(msgs)

s = """
@enum Z3_lbool Z3_L_FALSE = -1 Z3_L_UNDEF Z3_L_TRUE
"""
msgs = lintstr(s)
@test isempty(msgs)

@testset "E141" begin
    s = """
    macro ()
    end
    """
    msgs = lintstr(s)
    @test_broken msgs[1].code == :E141
    @test_broken occursin("invalid macro syntax", msgs[1].message)
end

s = """
macro foo()
    println("foo")
end
"""
msgs = lintstr(s)
@test isempty(msgs)

@testset "E437" begin
    @test messageset(lintstr("@compat()")) == Set([:E437])
    @test messageset(lintstr("@compat(1, 2)")) == Set([:E437])
end

end


text:
using IterativeSolvers
using FactCheck
using Base.Test

# Type used in SOL test
type Wrapper
    m::Int
    n::Int
end
Base.size(op::Wrapper, dim::Integer) = (dim == 1) ? op.m :
                                            (dim == 2) ? op.n : 1
Base.size(op::Wrapper) = (op.m, op.n)
Base.eltype(op::Wrapper) = Int

function Base.A_mul_B!(α, A::Wrapper, x, β, y)
    m, n = size(A)
    scale!(y, β)
    y[1] = y[1] + α * x[1]
    for i = 2:n
        y[i] = y[i] + i * α * x[i] + (i-1) * α * x[i-1]
    end
    for i = n+1:m
        y[i] = y[i]
    end
    return y
end

function Base.Ac_mul_B!(α, A::Wrapper, x, β, y)
    m, n = size(A)
    mn = min(m, n)
    scale!(y, β)
    for i = 1:mn-1
        y[i] = y[i] + α * i* (x[i]+x[i+1])
    end
    y[mn] = y[mn] + α * mn * x[mn]
    for i = m+1:n
        y[i] = y[i]
    end
    return y
end

# Type used in Dampenedtest
# solve (A'A + diag(v).^2 ) x = b
# using LSMR in the augmented space A' = [A ; diag(v)] b' = [b; zeros(size(A, 2)]
type DampenedVector{Ty, Tx}
    y::Ty
    x::Tx
end
Base.eltype(a::DampenedVector) =  promote_type(eltype(a.y), eltype(a.x))

function Base.norm(a::DampenedVector)
    return sqrt(norm(a.y)^2 + norm(a.x)^2)
end

function Base.copy!{Ty, Tx}(a::DampenedVector{Ty, Tx}, b::DampenedVector{Ty, Tx})
    copy!(a.y, b.y)
    copy!(a.x, b.x)
    return a
end

function Base.fill!(a::DampenedVector, α::Number)
    fill!(a.y, α)
    fill!(a.x, α)
    return a
end

function Base.scale!(a::DampenedVector, α::Number)
    scale!(a.y, α)
    scale!(a.x, α)
    return a
end

function Base.similar(a::DampenedVector, T)
    return DampenedVector(similar(a.y, T), similar(a.x, T))
end

function Base.length(a::DampenedVector)
    length(a.y) + length(a.x)
end

type DampenedMatrix{TA, Tx}
    A::TA
    diagonal::Tx
end

Base.eltype(A::DampenedMatrix) = promote_type(eltype(A.A), eltype(A.diagonal))

function Base.size(A::DampenedMatrix, dim::Integer)
    m, n = size(A.A)
    l = length(A.diagonal)
    dim == 1 ? (m + l) :
    dim == 2 ? n : 1
end

function Base.A_mul_B!{TA, Tx, Ty}(α::Number, mw::DampenedMatrix{TA, Tx}, a::Tx,
                β::Number, b::DampenedVector{Ty, Tx})
    if β != 1.
        if β == 0.
            fill!(b, 0.)
        else
            scale!(b, β)
        end
    end
    A_mul_B!(α, mw.A, a, 1.0, b.y)
    map!((z, x, y)-> z + α * x * y, b.x, b.x, a, mw.diagonal)
    return b
end

function Base.Ac_mul_B!{TA, Tx, Ty}(α::Number, mw::DampenedMatrix{TA, Tx}, a::DampenedVector{Ty, Tx},
                β::Number, b::Tx)
    if β != 1.
        if β == 0.
            fill!(b, 0.)
        else
            scale!(b, β)
        end
    end
    Ac_mul_B!(α, mw.A, a.y, 1.0, b)
    map!((z, x, y)-> z + α * x * y, b, b, a.x, mw.diagonal)
    return b
end

facts(string("lsmr")) do

    context("Small dense matrix") do
        A = rand(10, 5)
        b = rand(10)
        x, = lsmr(A, b)
        @fact norm(x - A\b) --> less_than(√eps())
    end

    context("SOL test") do
        # Test adapted from the BSD-licensed Matlab implementation at
        #    http://www.stanford.edu/group/SOL/software/lsqr.html
        #              Michael Saunders, Systems Optimization Laboratory,
        #              Dept of MS&E, Stanford University.
        #-----------------------------------------------------------------------

        # This is a simple example for testing  LSMR.
        # It uses the leading m*n submatrix from
        # A = [ 1
        #       1 2
        #         2 3
        #           3 4
        #             ...
        #               n ]
        # suitably padded by zeros.
        #
        # 11 Apr 1996: First version for distribution with lsqr.m.
        #              Michael Saunders, Dept of EESOR, Stanford University.

        function SOLtest(m, n, damp)
            A = Wrapper(m, n)
            xtrue = n:-1:1
            b = Array(Float64, m)
            b = float(A_mul_B!(1.0, A, xtrue, 0.0, b))
            x, = lsmr(A, b, atol = 1e-7, btol = 1e-7, conlim = 1e10, maxiter = 10n)
            r = A_mul_B!(-1, A, x, 1, b)
            @fact norm(r) --> less_than_or_equal(1e-4)
        end
        SOLtest(10, 10, 0)
        SOLtest(20, 10, 0)
        SOLtest(20, 10, 0.1)
    end

    context("dampened test") do
        # Test used to make sure A, b can be generic matrix / vector
        srand(1234)
        function DampenedTest(m, n)
            b = rand(m)
            A = rand(m, n)
            v = rand(n)
            Adampened = DampenedMatrix(A, v)
            bdampened = DampenedVector(b, zeros(n))
            x, ch = lsmr(Adampened, bdampened)
            @fact norm((A'A + diagm(v).^2)x - A'b) --> less_than(1e-3)
        end
        DampenedTest(10, 10)
        DampenedTest(20, 10)
    end
end


text:
module Neo4jBolt

using OrderedCollections

include("julia_bolt/JuliaBolt.jl")

using .JuliaBolt

export fix_parameters, record_data, record_values, record_items, index, value, consume, last_bookmark,
    custom_auth, basic_auth, kerberos_auth, session, single, begin_transaction, run_transaction, commit,
    rollback, write_transaction, read_transaction, summary, start_node, end_node, put_node, put_relationship,
    READ_ACCESS, WRITE_ACCESS
export Statement, Graph, Node, Relationship, PackStreamHydrator, Neo4jBoltDriver, UnitOfWork, Path, CartesianPoint,
    WGS84Point, DateWrapper, TimeWrapper, DateTimeWrapper, DurationWrapper

include("errors.jl")
include("config.jl")

const READ_ACCESS = "READ"
const WRITE_ACCESS = "WRITE"

const INITIAL_RETRY_DELAY = 1.0
const RETRY_DELAY_MULTIPLIER = 2.0
const RETRY_DELAY_JITTER_FACTOR = 0.2

const STATEMENT_TYPE_READ_ONLY = "r"
const STATEMENT_TYPE_READ_WRITE = "rw"
const STATEMENT_TYPE_WRITE_ONLY = "w"
const STATEMENT_TYPE_SCHEMA_WRITE = "s"

function custom_auth(scheme, principal, credentials, realm=nothing; parameters...)
    d = Dict{String, Any}(
        "scheme" => scheme,
        "principal" => principal,
        "credentials" => credentials,
    )
    if realm != nothing
        d["realm"] = realm
    end
    p = Dict()
    for (k,v) in parameters
        p[String(k)] = v
    end
    if length(p) > 0
        d["parameters"] = p
    end
    return d
end

function basic_auth(user, password, realm=nothing)
    return custom_auth("basic", user, password, realm)
end

function kerberos_auth(base64_encoded_ticket)
    return custom_auth("kerberos", "", base64_encoded_ticket)
end

#####################################
#         Neo4jBoltDriver           #
#####################################
using Sockets

mutable struct Neo4jBoltDriver
    uri_sceme::String
    pool::ConnectionPool
    closed::Bool
    address
    max_retry_time
    security_plan
    encrypted

    function Neo4jBoltDriver(uri; config...)

        address = JuliaBolt.from_uri(uri, JuliaBolt.DEFAULT_PORT)

        if address.scheme != "bolt"
            throw(ErrorException("Neo4jBolt driver requires bolt URI scheme, $(address.scheme) given"))
        end

        if haskey(config, "encrypted")
            #
        end

        function connector(address; kwargs...)
            return bolt_connect(address; (config..., kwargs...)...)
        end

        pool = ConnectionPool(connector, address; config...)

        # acquire and release a connection
        release(pool, acquire(pool))

        if haskey(config, "max_retry_time")
            max_retry_time = config["max_retry_time"]
        else
            max_retry_time = default_config["max_retry_time"]
        end

        return new("bolt", pool, false, address, max_retry_time, nothing, false)

    end
end

# Driver -------------------------- #

function Base.show(io::IO, driver::Neo4jBoltDriver)
    print(io, "Neo4jBoltDriver(address: $(driver.address))")
end

# Driver -------------------------- #

function Base.close(driver::Neo4jBoltDriver)
    if !driver.closed
        driver.closed = true
        if driver.pool != nothing
            JuliaBolt.close(driver.pool)
        end
    end
end

# Driver -------------------------- #

function session(driver::Neo4jBoltDriver, access_mode=nothing; parameters...)
    aquierer = function(access_mode)
        return JuliaBolt.acquire(driver.pool, access_mode)
    end
    if !in(:max_retry_time, Base.keys(parameters))
        return Session(aquierer, access_mode; max_retry_time=driver.max_retry_time, parameters...)
    else
        return Session((access_mode)->JuliaBolt.acquire(driver.pool, access_mode), access_mode; parameters...)
    end
end

function session(f::Function, args...; kwargs...)
    sess = session(args...; kwargs...)
    try
        f(sess)
    finally
        close(sess)
    end
end

# Driver -------------------------- #
# # # # # # # # # # # # # # # # # # #

#####################################
#             Statement             #
#####################################

mutable struct Statement
    text::String
    metadata::Union{Dict, Nothing}
    timeout::Union{Number, Nothing}

    function Statement(text; metadata::Union{Dict, Nothing}=nothing, timeout::Union{Number, Nothing}=nothing)
        return new(text, metadata, timeout)
    end
end

function Base.show(io::IO, statement::Statement)
    print(io, "Statement(text: $(statement.text))")
end

# Statement ----------------------- #
# # # # # # # # # # # # # # # # # # #

#####################################
#              Session              #
#####################################

mutable struct Session
    acquirer
    default_access_mode
    bookmarks_in
    bookmark_out
    max_retry_time
    closed
    connection
    connection_access_mode
    transaction
    last_result

    function Session(acquirer, access_mode; parameters...)
        bookmarks_in = nothing
        if haskey(parameters, :bookmark)
            bookmarks_in = [parameters[:bookmark]]
        end
        if haskey(parameters, :bookmarks)
            bookmarks_in = parameters[:bookmarks]
        end
        if haskey(parameters, :max_retry_time)
            max_retry_time = parameters[:max_retry_time]
        end
        return new(acquirer, access_mode, bookmarks_in, nothing, max_retry_time, false, nothing, nothing, nothing, nothing)
    end
end

function Base.show(io::IO, session::Session)
    print(io,  "Session()")
end

# Session ------------------------- #

function connect(session::Session, access_mode=nothing)
    if access_mode == nothing
        access_mode = session.default_access_mode
    end
    if session.connection != nothing
        if access_mode == session.connection_access_mode
            return
        end
        disconnect(session, true)
    end
    session.connection = session.acquirer(access_mode)
    session.connection_access_mode = access_mode

end

# Session ------------------------- #

function disconnect(session::Session, do_sync::Bool)
    if session.connection != nothing
        if do_sync
            try
                bolt_sync(session.connection)
            catch ex
                println("disconnect sync error")
            end
        end
        if session.connection != nothing
            session.connection.in_use = false
            session.connection = nothing
        end
        session.connection_access_mode = nothing
    end
end

# Session ------------------------- #

function Base.close(session::Session)
    try
        if has_transaction(session)
            try
                rollback_transaction(session)
            catch e
                println("error close rollback_tranaction")
            end
        end
    finally
        session.closed = true
    end
    disconnect(session, true)
end

# Session ------------------------- #

function Base.run(session::Session, statement::Statement, parameters::Dict=Dict(); use_julia_dates=true, kwparameters...)
    assert_open(session)
    if length(strip(statement.text)) == 0
        throw(ErrorException("Cannot run an empty statement"))
    end
    if session.connection == nothing
        connect(session)
    end
    cx = session.connection
    protocol_version = cx.protocol_version
    server = cx.server

    has_trans = has_transaction(session)

    statement_text = statement.text
    statement_metadata = statement.metadata
    statement_timeout = statement.timeout

    for (k,v) in kwparameters
        parameters[String(k)] = v
    end
    parameters = fix_parameters(parameters, protocol_version, supports_bytes=JuliaBolt.supports(server, "bytes"))

    fail = function(_,_)
        close_transaction(session)
    end

    hydrant = PackStreamHydrator(protocol_version, use_julia_dates)
    result_metadata = Dict(
        "statement"=> statement_text,
        "parameters"=> parameters,
        "server"=> server,
        "protocol_version"=> protocol_version
    )
    run_metadata = Dict(
        :statement=> statement_metadata,
        :timeout=> statement_timeout,
        :on_success=> (_, r)->merge!(result_metadata, r),
        :on_failure=> fail
    )

    function done(_, summary_metadata)
        merge!(result_metadata, summary_metadata)
        bookmark = if haskey(result_metadata, "bookmark") result_metadata["bookmark"] else nothing end
        if bookmark != nothing
            session.bookmarks_in = [bookmark]
            session.bookmark_out = bookmark
        end
    end

    session.last_result = result = StatementResult(session, hydrant, result_metadata)

    if has_trans
        if statement_metadata != nothing
            throw(ErrorException("Metadata can only be attached at transaction level"))
        end
        if statement_timeout != nothing
            throw(ErrorException("Timeouts only apply at transaction level"))
        end
    else
        run_metadata[:bookmarks] = session.bookmarks_in
    end

    bolt_run(cx, statement_text, parameters; run_metadata...)
    bolt_pull_all(
        cx,
        on_records = function(_, r)
            append!(result.records, hydrate_records(hydrant, keys(result), r))
        end,
        on_success=done,
        on_failure=fail,
        on_summary=function(_, r)
            detach(result, false)
        end
    )

    if !has_trans
        try
            JuliaBolt.send(session.connection)
            JuliaBolt.fetch(session.connection)
        catch ex
            if isa(ex, JuliaBolt.ConnectionExpired)
                throw(ErrorException(error))
            else
                rethrow()
            end
        end
    end

    return result
end

Base.run(session::Session, statement::String, parameters::Dict=Dict(); kwparameters...) =
    Base.run(session, Statement(statement), parameters; kwparameters...)

Base.run(session::Session, statement::AbstractArray{UInt8}, parameters::Dict=Dict(); kwparameters...) =
    Base.run(session, Statement(String(statement)), parameters; kwparameters...)

# Session ------------------------- #

function send(session::Session)
    if session.connection != nothing
        try
            JuliaBolt.send(session.connection)
        catch ex
            if isa(ex, JuliaBolt.ConnectionExpired)
                throw(ErrorException(error))
            else
                rethrow()
            end
        end
    end
end

# Session ------------------------- #

function fetch(session::Session)
    if session.connection != nothing
        try
            detail_count, _ = JuliaBolt.fetch(session.connection)
            return detail_count
        catch ex
            if isa(ex, JuliaBolt.ConnectionExpired)
                throw(ErrorException(error))
            else
                rethrow()
            end
        end
    else
        return 0
    end
end

# Session ------------------------- #

function sync(session::Session)
    if session.connection != nothing
        try
            detail_count, _ = bolt_sync(session.connection)
            return detail_count
        catch ex
            if isa(ex, JuliaBolt.ConnectionExpired)
                throw(ErrorException(error))
            else
                rethrow()
            end
        end
    else
        return 0
    end
end

# Session ------------------------- #

function detach(session::Session, result, do_sync=true)
    count = 0

    if do_sync && attached(result)
        send(session)
        while attached(result)
            count += fetch(session)
        end
    end

    if session.last_result === result
        session.last_result = nothing
        if !has_transaction(session)
            disconnect(session, false)
        end
    end

    result.session = nothing
    return count
end

# Session ------------------------- #

function next_bookmarks(session::Session)
    return session.bookmarks_in
end

# Session ------------------------- #

function last_bookmark(session::Session)
    return session.bookmark_out
end

# Session ------------------------- #

function has_transaction(session::Session)
    return session.transaction != nothing
end

# Session ------------------------- #

function close_transaction(session::Session)
    session.transaction = nothing
end

# Session ------------------------- #

function begin_transaction(session::Session; bookmark=nothing, metadata=nothing, timeout=nothing)
    assert_open(session)
    if has_transaction(session)
        throw(ErrorException("Explicit transaction already open"))
    end

    if bookmark != nothing
        throw(ErrorException("Passing bookmarks at transaction level is deprecated"))
    end

    open_transaction(session, metadata=metadata, timeout=timeout)
    return session.transaction
end

function begin_transaction(f::Function, args...)
    tx = begin_transaction(args...)
    ex_thrown = false
    try
        f(tx)
    catch ex
        ex_thrown = true
        rethrow()
    finally

        if tx.closed
            return
        elseif tx.success == nothing
            tx.success = !ex_thrown
        end
        close(tx)
    end
end

# Session ------------------------- #

function open_transaction(session::Session; access_mode=nothing, metadata=nothing, timeout=nothing)
    session.transaction = Transaction(session, on_close=()->close_transaction(session))
    connect(session, access_mode)
    bolt_begin(session.connection, bookmarks=session.bookmarks_in, metadata=metadata, timeout=timeout)
end

# Session ------------------------- #

function commit_transaction(session::Session)
    assert_open(session)
    if session.transaction == nothing
        throw(ErrorException("No transaction to commit"))
    end
    metadata = Dict()
    try
        bolt_commit(session.connection, on_success=(_, r)->merge!(metadata, r))
    finally
        disconnect(session, true)
        session.transaction = nothing
    end
    bookmark = metadata["bookmark"]
    session.bookmarks_in = [bookmark]
    session.bookmark_out = bookmark
    return bookmark
end

# Session ------------------------- #

function rollback_transaction(session::Session)
    assert_open(session)
    if session.transaction == nothing
        throw(ErrorException("No transaction to rollback"))
    end
    cx = session.connection
    if cx != nothing
        metadata = Dict()
        try
            bolt_rollback(cx, on_success=(_, r)->merge!(metadata, r))
        finally
            disconnect(session, true)
            session.transaction = nothing
        end
    end
end

# Session ------------------------- #

function retry_delay_generator(last_delay)
    delay = if last_delay == 0 INITIAL_RETRY_DELAY else last_delay * RETRY_DELAY_MULTIPLIER end
    jitter = RETRY_DELAY_JITTER_FACTOR * delay
    return delay - jitter + (2 * jitter * rand())
end

function is_retriable_transient_error(error)
    return in(error.code, ("Neo.TransientError.Transaction.Terminated", "Neo.TransientError.Transaction.LockClientStopped"))
end

# function kwargs_to_dict(kwargs)
#     d = Dict()
#     for (k,v) in kwargs
#         d[String(k)] = v
#     end
#     return d
# end

struct UnitOfWork
    f::Function
    metadata::Union{Dict, Nothing}
    timeout::Union{Number, Nothing}
end

function Base.show(io::IO, unit_of_work::UnitOfWork)
    print(io,  "UnitOfWork()")
end

function run_transaction(session::Session, access_mode::String, f::Function, args...; kwargs...)
    return run_transaction(session, access_mode, UnitOfWork(f, nothing, nothing), args...; kwargs...)
end

function run_transaction(session::Session, access_mode::String, unit_of_work::UnitOfWork, args...; kwargs...)
    retry_delay = 0
    errors = Exception[]
    t0 = time()

    f = unit_of_work.f
    metadata = unit_of_work.metadata
    timeout = unit_of_work.timeout

    while true
        try
            open_transaction(session, access_mode=access_mode, metadata=metadata, timeout=timeout)
            tx = session.transaction
            try
                result = f(tx, args, Dict(kwargs))
                if tx.success == nothing
                    tx.success = true
                end

                return result
            catch
                if tx.success == nothing
                    tx.success = false
                end
                rethrow()
            finally
                close(tx)
            end
        catch ex
            if isa(ex, JuliaBolt.ServiceUnavailable) || isa(ex, SessionExpired) || isa(ex, JuliaBolt.ConnectionExpired)
                print("tx error")
                push!(errors, ex)
            elseif isa(ex, TransientError)
                if is_retriable_transient_error(error)
                    print("tx is_retriable_transient_error error")
                    push!(errors, ex)
                else
                    rethrow()
                end
            else
                rethrow()
            end
        end

        # an error occored, it was appended to errors, and the tx should be retried
        t1 = time()
        if t1 - t0 > session.max_retry_time
            break
        end
        retry_delay = retry_delay_generator(retry_delay)
        sleep(retry_delay)
    end

    # tx was retried and failed the maximum number of times
    if length(errors) > 0
        throw(errors[length(errors)])
    else
        throw(ErrorException("Transaction failed"))
    end
end

# Session ------------------------- #

function read_transaction(session::Session, unit_of_work, args...; kwargs...)
    assert_open(session)
    return run_transaction(session, READ_ACCESS, unit_of_work, args...; kwargs...)
end

# Session ------------------------- #

function write_transaction(session::Session, unit_of_work, args...; kwargs...)
    assert_open(session)
    return run_transaction(session, WRITE_ACCESS, unit_of_work, args...; kwargs...)
end

# Session ------------------------- #

function assert_open(session::Session)
    if session.closed
        throw(ErrorException("Session Closed"))
    end
end

# Session ------------------------- #
# # # # # # # # # # # # # # # # # # #

#####################################
#            Transaction            #
#####################################

mutable struct Transaction
    session
    on_close
    success::Union{Bool, Nothing}
    closed::Bool

    function Transaction(session; on_close=nothing)
        return new(session, on_close, nothing, false)
    end
end

function Base.show(io::IO, transaction::Transaction)
    print(io,  "Transaction()")
end

# Transaction --------------------- #

function Base.run(transaction::Transaction, statement, parameters=Dict(); kwparameters...)
    assert_open(transaction)
    return Base.run(transaction.session, statement, parameters; kwparameters...)
end

# Transaction --------------------- #

function sync(transaction::Transaction)
    assert_open(transaction)
    sync(transaction.session)
end

# Transaction --------------------- #

function commit(transaction::Transaction)
    transaction.success = true
    close(transaction)
end

# Transaction --------------------- #

function rollback(transaction::Transaction)
    transaction.success = false
    close(transaction)
end

# Transaction --------------------- #

function Base.close(transaction::Transaction)
    assert_open(transaction)
    try
        sync(transaction)
    catch ex
        if isa(ex, JuliaBolt.CypherError)
            transaction.success = false
        else
            rethrow()
        end
    finally
        if has_transaction(transaction.session)
            if transaction.success == true
                commit_transaction(transaction.session)
            else
                rollback_transaction(transaction.session)
            end
        end
        transaction.closed = true
        transaction.on_close()
    end
end

# Transaction --------------------- #

function assert_open(transaction::Transaction)
    if transaction.closed
        throw(ErrorException("Transaction closed"))
    end
end

# Transaction --------------------- #
# # # # # # # # # # # # # # # # # # #

include("types/packstream.jl")

function fix_parameters(parameters, protocol_version; kwargs...)
    if parameters == nothing
        return Dict()
    end

    dehydrator = PackStreamDehydrator(protocol_version; kwargs...)

    try
        return dehydrate(dehydrator, [parameters])[1]
    catch ex
        if isa(ex, TypeError)
            value = error.args[1]
            throw(TypeError("Parameters of type $(typeof(value)) are not supported"))
        else
            rethrow()
        end
    end
end

#####################################
#          Statement Result         #
#####################################

mutable struct StatementResult
    session
    hydrant
    metadata
    records
    summary

    StatementResult(session, hydrant, metadata) = new(session, hydrant, metadata, [], nothing)
end

function Base.show(io::IO, statement_restult::StatementResult)
    print(io,  "StatementResult()")
end

# Statement Result ---------------- #

function attached(sr::StatementResult)
    return sr.session != nothing && !sr.session.closed
end

# Statement Result ---------------- #

function detach(sr::StatementResult, do_sync=true)
    if attached(sr)
        return detach(sr.session, sr, do_sync)
    else
        return 0
    end
end

# Statement Result ---------------- #

function keys(sr::StatementResult)
    if sr.metadata != nothing && haskey(sr.metadata, "fields")
        return sr.metadata["fields"]
    else
        if attached(sr)
            send(sr.session)
        end
        while attached(sr) && !haskey(sr.metadata, "fields")
            fetch(sr.session)
        end
        return sr.metadata["fields"]
    end
end

# Statement Result ---------------- #

function Base.iterate(sr::StatementResult, sent=false)
    while length(sr.records) == 0 && attached(sr)
        if !sent
            send(sr.session)
            sent = true
        end
        fetch(sr.session)
    end

    if length(sr.records) > 0
        return (popfirst!(sr.records), sent)
    else
        return nothing
    end
end

# Statement Result ---------------- #

function Base.IteratorSize(sr::StatementResult)
    return Base.SizeUnknown()
end

# Statement Result ---------------- #

function Base.IteratorEltype(sr::StatementResult)
    return Base.HasEltype()
end

# Statement Result ---------------- #

function Base.eltype(sr::StatementResult)
    return OrderedDict{String, Any}
end

# Statement Result ---------------- #

function summary(sr::StatementResult)
    detach(sr)
    if sr.summary == nothing
        sr.summary = BoltStatementResultSummary(sr.metadata)
    end
    return sr.summary
end

# Statement Result ---------------- #

function consume(sr::StatementResult)
    if attached(sr)
        for _ in sr
            # pass
        end
    end
    return summary(sr)
end

# Statement Result ---------------- #

function single(sr::StatementResult)
    records = [record for record in sr]
    size = length(records)
    if size == 0
        return nothing
    end
    return records[1]
end

# Statement Result ---------------- #

function peek(sr::StatementResult)
    if length(sr.records) > 0
        return sr.records[1]
    elseif !attached(sr)
        return nothing
    else
        send(sr.session)
    end

    while attached(sr)
        fetch(sr.session)
        if length(sr.records) > 0
            return sr.records[1]
        end
    end

    return nothing
end

# Statement Result ---------------- #

function graph(sr::StatementResult)
    detach(sr)
    return sr.hydrant.graph
end

# Bolt Statement Result ----------- #

function value(sr::StatementResult, item=0, default=nothing)
    return [value(record, item, default) for record in sr]
end

# Bolt Statement Result ----------- #

function values(sr::StatementResult, items)
    return [values(record, items) for record in sr]
end

# Bolt Statement Result ----------- #

function data(sr::StatementResult, items)
    return [data(record, items) for record in sr]
end

# Bolt Statement Result ----------- #
# # # # # # # # # # # # # # # # # # #

#####################################
#   Bolt Statement Result Summary   #
#####################################

mutable struct BoltStatementResultSummary
    metadata
    protocol_version
    server
    statement
    parameters
    statement_type
    counters
    result_available_after
    result_consumed_after
    t_first
    t_last
    plan
    profile
    notifications

    function BoltStatementResultSummary(metadata::Dict)
        protocol_version = if haskey(metadata, "protocol_version") metadata["protocol_version"] else nothing end
        server = if haskey(metadata, "server") metadata["server"] else nothing end
        statement = if haskey(metadata, "statement") metadata["statement"] else nothing end
        parameters = if haskey(metadata, "parameters") metadata["parameters"] else nothing end
        statement_type = if haskey(metadata, "type") metadata["type"] else nothing end
        counters = SummaryCounters(if haskey(metadata, "stats") metadata["stats"] else Dict() end)
        result_available_after = if haskey(metadata, "result_available_after") metadata["result_available_after"] else nothing end
        result_consumed_after = if haskey(metadata, "result_consumed_after") metadata["result_consumed_after"] else nothing end
        t_first = if haskey(metadata, "t_first") metadata["t_first"] else nothing end
        t_last = if haskey(metadata, "t_last") metadata["t_last"] else nothing end

        plan = nothing
        profile = nothing
        if haskey(metadata, "plan")
            plan = make_plan(metadata["plan"] )
        end
        if haskey(metadata, "profile")
            profile = make_plan(metadata["profile"])
            plan = profile
        end
        notifications = []
        if haskey(metadata, "notifications")
            for notification in metadata["notifications"]
                p = nothing
                if haskey(notification, "position")
                    position = notification["position"]
                    if position != nothing
                        p = Position(
                            if haskey(position, "offset") position["offset"] else nothing end,
                            if haskey(position, "line") position["line"] else nothing end,
                            if haskey(position, "column") position["column"] else nothing end
                        )
                    end
                end
                n = Notification(
                    if haskey(notification, "code") notification["code"] else nothing end,
                    if haskey(notification, "title") notification["title"] else nothing end,
                    if haskey(notification, "description") notification["description"] else nothing end,
                    if haskey(notification, "severity") notification["severity"] else nothing end,
                    p
                )
                push!(notifications, n)
            end
        end

        return new(metadata, protocol_version, server, statement, parameters, statement_type, counters,
            result_available_after, result_consumed_after, t_first, t_last, plan, profile, notifications)
    end
end

function Base.show(io::IO, statement_restult::BoltStatementResultSummary)
    print(io,  "BoltStatementResultSummary()")
end

struct Plan
    operator_type
    identifiers
    arguments
    children
end

# Struct Types -------------------- #

struct ProfiledPlan
    operator_type
    identifiers
    arguments
    children
    db_hits
    rows
end

# Struct Types -------------------- #

struct Notification
    code
    title
    description
    severity
    position
end

# Struct Types -------------------- #

struct Position
    offset
    line
    column
end

function make_plan(plan_dict::Dict)
    operator_type = if haskey(plan_dict, "operatorType") plan_dict["operatorType"] else nothing end
    identifiers = if haskey(plan_dict, "identifiers") plan_dict["identifiers"] else [] end
    arguments = if haskey(plan_dict, "arguments") plan_dict["arguments"] else [] end

    children = if haskey(plan_dict, "children") Vector([make_plan(child) for child in plan_dict["children"]]) else [] end

    if haskey(plan_dict, "dbHits") || haskey(plan_dict, "rows")
        db_hits = if haskey(plan_dict, "db_hits") plan_dict["db_hits"] else 0 end
        rows = if haskey(plan_dict, "rows") plan_dict["rows"] else 0 end
        return ProfiledPlan(operator_type, identifiers, arguments, children, db_hits, rows)
    else
        return Plan(operator_type, identifiers, arguments, children)
    end
end

# Bolt Statement Result Summary --- #
# # # # # # # # # # # # # # # # # # #

#####################################
#          Summary Counters         #
#####################################

mutable struct SummaryCounters
    nodes_created
    nodes_deleted
    relationships_created
    relationships_deleted
    properties_set
    labels_added
    labels_removed
    indexes_added
    indexes_removed
    constraints_added
    constraints_removed

    function SummaryCounters(stats::Dict)
        nodes_created = if haskey(stats, "nodes-created") stats["nodes-created"] else nothing end
        nodes_deleted = if haskey(stats, "nodes-deleted") stats["nodes-deleted"] else nothing end
        relationships_created = if haskey(stats, "relationships-created") stats["relationships-created"] else nothing end
        relationships_deleted = if haskey(stats, "relationships-deleted") stats["relationships-deleted"] else nothing end
        properties_set = if haskey(stats, "properties-set") stats["properties-set"] else nothing end
        labels_added = if haskey(stats, "labels-added") stats["labels-added"] else nothing end
        labels_removed = if haskey(stats, "labels-removed") stats["labels-removed"] else nothing end
        indexes_added = if haskey(stats, "indexes-added") stats["indexes-added"] else nothing end
        indexes_removed = if haskey(stats, "indexes-removed") stats["indexes-removed"] else nothing end
        constraints_added = if haskey(stats, "constraints-added") stats["constraints-added"] else nothing end
        constraints_removed = if haskey(stats, "constraints-removed") stats["constraints-removed"] else nothing end

        return new(nodes_created, nodes_deleted, relationships_created, relationships_deleted, properties_set, labels_added,
            labels_removed, indexes_added, indexes_removed, constraints_added, constraints_removed)
    end
end

# Summary Counters ---------------- #
# # # # # # # # # # # # # # # # # # #

#####################################
#               Record              #
#####################################

function record_data(dict::OrderedDict{String, Any}, keys::Union{Vector, Nothing}=nothing)
    if keys != nothing
        d = OrderedDict{String, Any}()
        for key in keys
            if isa(key, Integer)
                if key > 0 && key <= length(dict)
                    k = dict.keys[key]
                    d[k] = dict[k]
                else
                    throw(ErrorException("Invalid Index"))
                end
            else
                if haskey(dict, key)
                    d[key] = dict[key]
                else
                    d[key] = nothing
                end
            end
        end
        return d
    end
    return copy(dict)
end

function record_values(dict::OrderedDict{String, Any}, keys::Union{Vector, Nothing}=nothing)
    if keys != nothing
        d = []
        for key in keys
            if isa(key, Integer)
                if key > 0 && key <= length(dict)
                    push!(d, dict.vals[key])
                else
                    throw(ErrorException("Invalid Index"))
                end
            else
                if haskey(dict, key)
                    push!(d, dict[key])
                else
                    push!(d, nothing)
                end
            end
        end
        return d
    end

    return collect(Base.values(dict))
end

function record_items(dict::OrderedDict{String, Any}, keys::Union{Vector, Nothing}=nothing)
    if keys != nothing
        d = []
        for key in keys
            if isa(key, Integer)
                if key > 0 && key <= length(dict)
                    push!(d, (dict.keys[key], dict.vals[key]))
                else
                    throw(ErrorException("Invalid Index"))
                end
            else
                if haskey(dict, key)
                    push!(d, (key, dict[key]))
                else
                    push!(d, (key, nothing))
                end
            end
        end
        return d
    end

    return [(k,v) for (k,v) in dict]
end

function index(dict::OrderedDict{String, Any}, key::Integer)
    if key > 0 && key <= length(dict)
        return key
    else
        throw(ErrorException("Invalid Index"))
    end
end

function index(dict::OrderedDict{String, Any}, key::String)
    i = OrderedCollections.ht_keyindex(dict, key, true)
    if i > 0
        return i
    else
        throw(ErrorException("Invalid Key"))
    end
end

function value(dict::OrderedDict{String, Any}, key::Union{String, Integer}=1, default=nothing)
    if isa(key, Integer)
        if key > 0 && key <= length(dict)
            return dict.vals[key]
        else
            return default
        end
    else
        if haskey(dict, key)
            return dict[key]
        else
            return default
        end
    end
end

# Record -------------------------- #
# # # # # # # # # # # # # # # # # # #

end # module


text:
using Lazy
using BenchmarkTools

function fib(n)
  a,b = 0,1
  for i in 1:(n-1)
    a,b = b,a+b
  end
  return a
end

fibs = @lazy 0:1:(fibs + drop(1, fibs));

println(@benchmark fib(20))
println(@benchmark take(20,fibs))

max_stars_repo_name: vmchale/julia-problem-solving

max_issues_repo_name: vmchale/julia-problem-solving

max_forks_repo_name: vmchale/julia-problem-solving


text:
using PyCall
using BenchmarkTools

py"""
import cv2
import numpy as np

cap = cv2.VideoCapture(0)
if cap.isOpened() is False:
    raise Exception("Error opening video stream of file")
goma=None

def capture(canvas):
    global goma
    ret_val, image = cap.read()
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    H,W,_=image.shape
    oH,oW,_ = canvas.shape
    if oH > H or oW > W:
        raise ValueError('shape of image needs to be larger than size')
    y_offset = int(round((H - oH) / 2.))
    x_offset = int(round((W - oW) / 2.))
    y_slice = slice(y_offset, y_offset + oH)
    x_slice = slice(x_offset, x_offset + oW)
    canvas[:]=image[y_slice,x_slice]
    goma = image

def captureraw():
    ret_val, image = cap.read()
    return image

def savecanvas(canvas):
    cv2.imwrite("goma.png", canvas)
"""

function capture!(canvas)
    pycall(py"capture",Nothing,canvas)
end

function capture()
    canvas=py"captureraw"()
    return canvas
end

max_stars_repo_name: terasakisatoshi/juliaExer

max_issues_repo_name: terasakisatoshi/juliaExer

max_forks_repo_name: terasakisatoshi/juliaExer


text:
module m1test1
using FinEtools
using FinEtoolsDeforLinear.DeforModelRedModule: DeforModelRed3D
using FinEtoolsDeforNonlinear
using LinearAlgebra: norm
using Test
function test()
    mr = DeforModelRed3D
    E, nu = 7.0*phun("MPa"), 0.3
    m = FinEtoolsDeforNonlinear.MatDeforNeohookeanModule.MatDeforNeohookean(mr, E, nu)
    # @show m
    update! = FinEtoolsDeforNonlinear.MatDeforNonlinearModule.update!

    stress = fill(0.0, 6)
    output = FFlt[]
    statev = FFlt[]
    tn = 0.0
    dtn = 0.0
    loc = [0.0 0.0 0.0]
    label = 0
    quantity=:nothing

    Fn1 = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress) == 0.0

    Fn1 = [1.01 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[93365.8, 39786.2, 39786.2, 0.0, 0.0, 0.0]) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.01 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[39786.2, 93365.8, 39786.2, 0.0, 0.0, 0.0]) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.0 0.01; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[0.0, 269.231, 0.0, 0.0, 0.0, 26923.1]) / E < 1.0e-7
end
end
using .m1test1
m1test1.test()

module m2test1
using FinEtools
using FinEtoolsDeforLinear.DeforModelRedModule: DeforModelRed3D
using FinEtoolsDeforNonlinear
using LinearAlgebra: norm
using Test
function test()
    mr = DeforModelRed3D
    E, nu = 7.0*phun("MPa"), 0.3

    m = FinEtoolsDeforNonlinear.MatDeforStVKModule.MatDeforStVK(mr, E, nu)
    # @show m
    update! = FinEtoolsDeforNonlinear.MatDeforNonlinearModule.update!

    stress = fill(0.0, 6)
    output = FFlt[]
    statev = FFlt[]
    tn = 0.0
    dtn = 0.0
    loc = [0.0 0.0 0.0]
    label = 0
    quantity=:nothing

    Fn1 = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress) == 0.0

    Fn1 = [1.01 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[95648.94230769236, 40184.69154607771, 40184.69154607771, 0.0, 0.0, 0.0]) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.01 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[40184.6915460777, 95648.94230769236, 40184.69154607771, 0.0, 0.0, 0.0] ) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.0 0.01; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[201.92307692305477, 740.4317307692086, 471.1538461537944, 0.0, 0.0, 26927.78846153846]) / E < 1.0e-7
end
end
using .m2test1
m2test1.test()

module m1test2
using FinEtools
using FinEtoolsDeforLinear.DeforModelRedModule: DeforModelRed3D
using FinEtoolsDeforNonlinear
using LinearAlgebra: norm
using Test
function test()
    mr = DeforModelRed3D
    E, nu = 7.0*phun("MPa"), 0.3
    m = FinEtoolsDeforNonlinear.MatDeforNeohookeanModule.MatDeforNeohookean(mr, E, nu)
    # @show m
    update! = FinEtoolsDeforNonlinear.MatDeforNonlinearModule.update!

    stress = fill(0.0, 6)
    output = FFlt[]
    statev = FFlt[]
    tn = 0.0
    dtn = 0.0
    loc = [0.0 0.0 0.0]
    label = 0
    quantity=:nothing

    Fn1 = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress) == 0.0

    Fn1 = [1.01 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[93365.8, 39786.2, 39786.2, 0.0, 0.0, 0.0]) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.01 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[39786.2, 93365.8, 39786.2, 0.0, 0.0, 0.0]) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.0 0.00001; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[0.0, 0.0002692307915070229, 0.0, 0.0, 0.0, 26.923076923076923]) / E < 1.0e-7
end
end
using .m1test2
m1test2.test()

module m2test2
using FinEtools
using FinEtoolsDeforLinear.DeforModelRedModule: DeforModelRed3D
using FinEtoolsDeforNonlinear
using LinearAlgebra: norm
using Test
function test()
    mr = DeforModelRed3D
    E, nu = 7.0*phun("MPa"), 0.3

    m = FinEtoolsDeforNonlinear.MatDeforStVKModule.MatDeforStVK(mr, E, nu)
    # @show m
    update! = FinEtoolsDeforNonlinear.MatDeforNonlinearModule.update!

    stress = fill(0.0, 6)
    output = FFlt[]
    statev = FFlt[]
    tn = 0.0
    dtn = 0.0
    loc = [0.0 0.0 0.0]
    label = 0
    quantity=:nothing

    Fn1 = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress) == 0.0

    Fn1 = [1.01 0 0; 0 1.0 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[95648.94230769236, 40184.69154607771, 40184.69154607771, 0.0, 0.0, 0.0]) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.01 0; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[40184.6915460777, 95648.94230769236, 40184.69154607771, 0.0, 0.0, 0.0] ) / E < 1.0e-7

    Fn1 = [1.0 0 0; 0 1.0 0.00001; 0 0 1.0]
    Fn = [1.0 0 0; 0 1.0 0; 0 0 1.0]
    update!(m, statev, stress, output, Fn1, Fn, tn, dtn, loc, label, quantity)
    # @show stress
    @test norm(stress-[0.0002019230936302673, 0.0007403846321389213, 0.0004711538851372903, 0.0, 0.0, 26.92307692778846]) / E < 1.0e-7
end
end
using .m2test2
m2test2.test()