text:
const TEST_FLOAT32 = false
const TEST_DOUBLE64 = false
const TEST_STATIC_SIZED = false

using Manifolds
using ManifoldsBase

using LinearAlgebra
using Distributions
using DoubleFloats
using ForwardDiff
using Random
using ReverseDiff
using StaticArrays
using Test
using LightGraphs
using SimpleWeightedGraphs

find_eps(x::Type{TN}) where TN<:Number = eps(real(TN))
find_eps(x) = find_eps(number_eltype(x))
find_eps(x...) = find_eps(Base.promote_type(map(number_eltype, x)...))

"""
    test_manifold(m::Manifold, pts::AbstractVector;
    args
    )

Tests general properties of manifold `m`, given at least three different points
that lie on it (contained in `pts`).

# Arguments
- `default_inverse_retraction_method = ManifoldsBase.LogarithmicInverseRetraction()` - default method for inverse retractions ([`log`](@ref))
- `default_retraction_method = ManifoldsBase.ExponentialRetraction()` - default method for retractions ([`exp`](@ref))
- `exp_log_atol_multiplier = 0`, change absolute tolerance of exp/log tests (0 use default, i.e. deactivate atol and use rtol)
- `exp_log_rtol_multiplier = 1`, change the relative tolerance of exp/log tests (1 use default). This is deactivated if the `exp_log_atol_multiplier` is nonzero.
- `retraction_atol_multiplier = 0`, change absolute tolerance of (inverse) retraction tests (0 use default, i.e. deactivate atol and use rtol)
- `retraction_rtol_multiplier = 1`, change the relative tolerance of (inverse) retraction tests (1 use default). This is deactivated if the `exp_log_atol_multiplier` is nonzero.
- `inverse_retraction_methods = []`: inverse retraction methods that will be tested.
- `point_distributions = []` : point distributions to test
- `projection_tvector_atol_multiplier = 0` : chage absolute tolerance in testing projections (0 use default, i.e. deactivate atol and use rtol)
-  tvector_distributions = []` : tangent vector distributions to test
- `basis_types = ()` : basis types that will be tested
- `rand_tvector_atol_multiplier = 0` : chage absolute tolerance in testing random vectors (0 use default, i.e. deactivate atol and use rtol)
  random tangent vectors are tangent vectors
- `retraction_methods = []`: retraction methods that will be tested.
- `test_forward_diff = true`: if true, automatic differentiation using
  ForwardDiff is tested.
- `test_reverse_diff = true`: if true, automatic differentiation using
  ReverseDiff is tested.
- `test_musical_isomorphisms = false` : test musical isomorphisms
- `test_mutating_rand = false` : test the mutating random function for points on manifolds
- `test_project_tangent = false` : test projections on tangent spaces
- `test_representation_size = true` : test repersentation size of points/tvectprs
- `test_tangent_vector_broadcasting = true` : test boradcasting operators on TangentSpace
- `test_vector_transport = false` : test vector transport
- `test_vector_spaces = true` : test Vector bundle of this manifold
"""
function test_manifold(M::Manifold, pts::AbstractVector;
    test_exp_log = true,
    test_is_tangent = true,
    test_injectivity_radius=true,
    test_forward_diff = true,
    test_reverse_diff = true,
    test_tangent_vector_broadcasting = true,
    test_project_tangent = false,
    test_project_point = false,
    test_representation_size = true,
    test_musical_isomorphisms = false,
    test_vector_transport = false,
    test_mutating_rand = false,
    test_vector_spaces = true,
    test_vee_hat = false,
    is_mutating = true,
    default_inverse_retraction_method = ManifoldsBase.LogarithmicInverseRetraction(),
    default_retraction_method = ManifoldsBase.ExponentialRetraction(),
    retraction_methods = [],
    inverse_retraction_methods = [],
    point_distributions = [],
    tvector_distributions = [],
    basis_types_vecs = (),
    basis_types_to_from = (),
    basis_has_specialized_diagonalizing_get = false,
    exp_log_atol_multiplier = 0,
    exp_log_rtol_multiplier = 1,
    retraction_atol_multiplier = 0,
    retraction_rtol_multiplier = 1,
    projection_atol_multiplier = 0,
    rand_tvector_atol_multiplier = 0,
    is_tangent_atol_multiplier=1,
)

    length(pts) ≥ 3 || error("Not enough points (at least three expected)")
    isapprox(M, pts[1], pts[2]) && error("Points 1 and 2 are equal")
    isapprox(M, pts[1], pts[3]) && error("Points 1 and 3 are equal")

    # get a default tangent vector for every of the three tangent spaces
    n = length(pts)
    if default_inverse_retraction_method === nothing
        tv = [ zero_tangent_vector(M,pts[i]) for i=1:n ] # no other available
    else
        tv = [ inverse_retract(M,pts[i],pts[((i+1)%n)+1],default_inverse_retraction_method) for i=1:n]
    end
    @testset "dimension" begin
        @test isa(manifold_dimension(M), Integer)
        @test manifold_dimension(M) ≥ 0
        @test manifold_dimension(M) == vector_space_dimension(Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M))
        @test manifold_dimension(M) == vector_space_dimension(Manifolds.VectorBundleFibers(Manifolds.CotangentSpace, M))
    end

    test_representation_size && @testset "representation" begin
        function test_repr(repr)
            @test isa(repr, Tuple)
            for rs ∈ repr
                @test rs > 0
            end
        end

        test_repr(Manifolds.representation_size(M))
        for fiber ∈ (Manifolds.TangentSpace, Manifolds.CotangentSpace)
            test_repr(Manifolds.representation_size(Manifolds.VectorBundleFibers(fiber, M)))
        end
    end

    test_injectivity_radius && @testset "injectivity radius" begin
        @test injectivity_radius(M, pts[1]) > 0
        @test injectivity_radius(M, pts[1]) ≥ injectivity_radius(M)
        for rm ∈ retraction_methods
            @test injectivity_radius(M, rm) > 0
            @test injectivity_radius(M, pts[1], rm) ≥ injectivity_radius(M, rm)
            @test injectivity_radius(M, pts[1], rm) ≤ injectivity_radius(M, pts[1])
        end
    end

    @testset "is_manifold_point" begin
        for pt ∈ pts
            @test is_manifold_point(M, pt)
            @test check_manifold_point(M, pt) === nothing
            @test check_manifold_point(M, pt) === nothing
        end
    end

    test_is_tangent && @testset "is_tangent_vector" begin
        for (p, X) in zip(pts, tv)
            atol = is_tangent_atol_multiplier * find_eps(p)
            if !( check_tangent_vector(M, p, X; atol = atol) === nothing )
                print( check_tangent_vector(M, p, X; atol = atol) )
            end
            @test is_tangent_vector(M, p, X; atol = atol)
            @test check_tangent_vector(M, p, X; atol = atol) === nothing
        end
    end

    test_exp_log && @testset "log/exp tests" begin
        epsp1p2 = find_eps(pts[1], pts[2])
        atolp1p2 = exp_log_atol_multiplier * epsp1p2
        rtolp1p2 = exp_log_atol_multiplier == 0. ? sqrt(epsp1p2)*exp_log_rtol_multiplier : 0
        X1 = log(M, pts[1], pts[2])
        X2 = log(M, pts[2], pts[1])
        @test isapprox(M, pts[2], exp(M, pts[1], X1); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[1], exp(M, pts[1], X1, 0); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[2], exp(M, pts[1], X1, 1); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[1], exp(M, pts[2], X2); atol = atolp1p2, rtol = rtolp1p2)
        @test is_manifold_point(M, exp(M, pts[1], X1); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[1], exp(M, pts[1], X1, 0); atol = atolp1p2, rtol = rtolp1p2)
        for p ∈ pts
            epsx = find_eps(p)
            @test isapprox(M, p, zero_tangent_vector(M, p), log(M, p, p);
                atol = epsx * exp_log_atol_multiplier,
                rtol = exp_log_atol_multiplier == 0. ? sqrt(epsx)*exp_log_rtol_multiplier : 0
            )
            @test isapprox(M, p, zero_tangent_vector(M, p), inverse_retract(M, p, p);
                atol = epsx * exp_log_atol_multiplier,
                rtol = exp_log_atol_multiplier == 0. ? sqrt(epsx)*exp_log_rtol_multiplier : 0.
            )
        end
        atolp1 = exp_log_atol_multiplier * find_eps(pts[1])
        if is_mutating
            zero_tangent_vector!(M, X1, pts[1])
        else
            X1 = zero_tangent_vector(M, pts[1])
        end
        @test isapprox(M, pts[1], X1, zero_tangent_vector(M, pts[1]); atol = atolp1)
        if is_mutating
            log!(M, X1, pts[1], pts[2])
        else
            X1 = log(M,pts[1], pts[2])
        end

        @test isapprox(M, exp(M, pts[1], X1, 1), pts[2]; atol = atolp1)
        @test isapprox(M, exp(M, pts[1], X1, 0), pts[1]; atol = atolp1)

        @test distance(M, pts[1], pts[2]) ≈ norm(M, pts[1], X1)

        X3 = log(M, pts[1], pts[3])

        @test real(inner(M, pts[1], X1, X3)) ≈ real(inner(M, pts[1], X3, X1))
        @test imag(inner(M, pts[1], X1, X3)) ≈ -imag(inner(M, pts[1], X3, X1))
        @test imag(inner(M, pts[1], X1, X1)) ≈ 0

        @test norm(M, pts[1], X1) isa Real
        @test norm(M, pts[1], X1) ≈ sqrt(inner(M, pts[1], X1, X1))
    end

    @testset "(inverse &) retraction tests" begin
        for (p, X) in zip(pts, tv)
            epsx = find_eps(p)
            for retr_method ∈ retraction_methods
                @test is_manifold_point(M, retract(M, p, X, retr_method))
                @test isapprox(M, p, retract(M, p, X, 0, retr_method);
                    atol = epsx * retraction_atol_multiplier,
                    rtol = retraction_atol_multiplier == 0 ? sqrt(epsx)*retraction_rtol_multiplier : 0
                )
                if is_mutating
                    new_pt = allocate(p)
                    retract!(M, new_pt, p, X, retr_method)
                else
                    new_pt = retract(M, p, X, retr_method)
                end
                @test is_manifold_point(M, new_pt)
            end
        end
        for p ∈ pts
            epsx = find_eps(p)
            for inv_retr_method ∈ inverse_retraction_methods
                @test isapprox(M, p, zero_tangent_vector(M, p), inverse_retract(M, p, p, inv_retr_method);
                    atol = epsx * retraction_atol_multiplier,
                    rtol = retraction_atol_multiplier == 0 ? sqrt(epsx)*retraction_rtol_multiplier : 0
                )
            end
        end
    end

    test_vector_spaces && @testset "vector spaces tests" begin
        for p ∈ pts
            X = zero_tangent_vector(M, p)
            mts = Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M)
            @test isapprox(M, p, X, zero_vector(mts, p))
            zero_vector!(mts, X, p)
            @test isapprox(M, p, X, zero_tangent_vector(M,p))
        end
    end

    @testset "basic linear algebra in tangent space" begin
        for (p, X) in zip(pts, tv)
            @test isapprox(M, p, 0*X, zero_tangent_vector(M, p); atol = find_eps(pts[1]))
            @test isapprox(M, p, 2*X, X+X)
            @test isapprox(M, p, 0*X, X-X)
            @test isapprox(M, p, (-1)*X, -X)
        end
    end

    test_tangent_vector_broadcasting && @testset "broadcasted linear algebra in tangent space" begin
        for (p, X) in zip(pts, tv)
            @test isapprox(M, p, 3*X, 2 .* X .+ X)
            @test isapprox(M, p, -X, X .- 2 .* X)
            @test isapprox(M, p, -X, .-X)
            if (isa(X, AbstractArray))
                Y = allocate(X)
                Y .= 2 .* X .+ X
            else
                Y = 2*X+X
            end
            @test isapprox(M, p, Y, 3*X)
        end
    end

    test_project_tangent && @testset "project tangent test" begin
        for (p, X) in zip(pts, tv)
            atol = find_eps(p) * projection_atol_multiplier
            @test isapprox(M, p, X, project(M, p, X); atol = atol)
            if is_mutating
                X2 = allocate(X)
                project!(M, X2, p, X)
            else
                X2 = project(M, p, X)
            end
            @test isapprox(M, p, X2, X; atol = atol)
        end
    end

    test_project_point && @testset "project point test" begin
        for p in pts
            atol = find_eps(p) * projection_atol_multiplier
            @test isapprox(M, p, project(M, p); atol = atol)
            if is_mutating
                p2 = allocate(p)
                project!(M, p2, p)
            else
                p2 = project(M, p)
            end
            @test isapprox(M, p2, p; atol = atol)
        end
    end

    test_vector_transport && !( default_inverse_retraction_method === nothing) && @testset "vector transport" begin
        tvatol = is_tangent_atol_multiplier*find_eps(pts[1])
        X1 = inverse_retract(M, pts[1], pts[2], default_inverse_retraction_method)
        X2 = inverse_retract(M, pts[1], pts[3], default_inverse_retraction_method)
        v1t1 = vector_transport_to(M, pts[1], X1, pts[3])
        v1t2 = vector_transport_direction(M, pts[1], X1, X2)
        @test is_tangent_vector(M, pts[3], v1t1; atol=tvatol)
        @test is_tangent_vector(M, pts[3], v1t2; atol=tvatol)
        @test isapprox(M, pts[3], v1t1, v1t2)
        @test isapprox(M, pts[1], vector_transport_to(M, pts[1], X1, pts[1]), X1)
    end

    for btype ∈ basis_types_vecs
        @testset "Basis support for $(btype)" begin
            p = pts[1]
            b = get_basis(M, p, btype)
            @test isa(b, CachedBasis)
            bvectors = get_vectors(M, p, b)
            N = length(bvectors)
            @test real_dimension(number_system(btype)) * N == manifold_dimension(M)

            # test orthonormality
            for i in 1:N
                @test norm(M, p, bvectors[i]) ≈ 1
                for j in i+1:N
                    @test real(inner(M, p, bvectors[i], bvectors[j])) ≈ 0 atol = sqrt(find_eps(p))
                end
            end
            if isa(btype, ProjectedOrthonormalBasis)
                # check projection idempotency
                for i in 1:N
                    @test norm(M, p, bvectors[i]) ≈ 1
                    for j in i+1:N
                        @test real(inner(M, p, bvectors[i], bvectors[j])) ≈ 0 atol = sqrt(find_eps(p))
                    end
                end
                # check projection idempotency
                for i in 1:N
                    @test isapprox(M, p, project(M, p, bvectors[i]), bvectors[i])
                end
            end
            if !isa(btype, ProjectedOrthonormalBasis) &&
                (basis_has_specialized_diagonalizing_get || !isa(btype, DiagonalizingOrthonormalBasis))

                X1 = inverse_retract(M, p, pts[2], default_inverse_retraction_method)
                Xb = get_coordinates(M, p, X1, btype)

                @test get_coordinates(M, p, X1, b) ≈ Xb
                @test isapprox(M, p, get_vector(M, p, Xb, b), get_vector(M, p, Xb, btype))
            end
        end
    end

    for btype ∈ (basis_types_to_from..., basis_types_vecs...)
        p = pts[1]
        N = manifold_dimension(M)
        if !isa(btype, ProjectedOrthonormalBasis) &&
            (basis_has_specialized_diagonalizing_get || !isa(btype, DiagonalizingOrthonormalBasis))

            X1 = inverse_retract(M, p, pts[2], default_inverse_retraction_method)

            Xb = get_coordinates(M, p, X1, btype)
            #@test isa(Xb, AbstractVector{<:Real})
            @test length(Xb) == N
            Xbi = get_vector(M, p, Xb, btype)
            @test isapprox(M, p, X1, Xbi)

            Xs = [[ifelse(i==j, 1, 0) for j in 1:N] for i in 1:N]
            Xs_invs = [get_vector(M, p, Xu, btype) for Xu in Xs]
            # check orthonormality of inverse representation
            for i in 1:N
                @test norm(M, p, Xs_invs[i]) ≈ 1 atol = find_eps(p)
                for j in i+1:N
                    @test real(inner(M, p, Xs_invs[i], Xs_invs[j])) ≈ 0 atol = sqrt(find_eps(p))
                end
            end

            if is_mutating
                Xb_s = allocate(Xb)
                @test get_coordinates!(M, Xb_s, p, X1, btype) === Xb_s
                @test isapprox(Xb_s, Xb; atol = find_eps(p))

                Xbi_s = allocate(Xbi)
                @test get_vector!(M, Xbi_s, p, Xb, btype) === Xbi_s
                @test isapprox(M, p, X1, Xbi_s)
            end
        end
    end

    test_vee_hat && @testset "vee and hat" begin
        p = pts[1]
        q = pts[2]
        X = inverse_retract(M, p, q, default_inverse_retraction_method)
        Y = vee(M, p, X)
        @test length(Y) == manifold_dimension(M)
        @test isapprox(M, p, X, hat(M, p, Y))
        Y2 = allocate(Y)
        vee_ret = vee!(M, Y2, p, X)
        @test vee_ret === Y2
        @test isapprox(Y, Y2)
        X2 = allocate(X)
        hat_ret = hat!(M, X2, p, Y)
        @test hat_ret === X2
        @test isapprox(M, p, X2, X)
    end

    test_forward_diff && @testset "ForwardDiff support" begin
        for (p, X) in zip(pts, tv)
            exp_f(t) = distance(M, p, exp(M, p, t[1]*X))
            d12 = norm(M, p, X)
            for t ∈ 0.1:0.1:0.9
                @test d12 ≈ ForwardDiff.derivative(exp_f, t)
            end

            retract_f(t) = distance(M, p, retract(M, p, t[1]*X))
            for t ∈ 0.1:0.1:0.9
                @test ForwardDiff.derivative(retract_f, t) ≥ 0
            end
        end
    end

    test_reverse_diff && @testset "ReverseDiff support" begin
        for (p, X) in zip(pts, tv)
            exp_f(t) = distance(M, p, exp(M, p, t[1]*X))
            d12 = norm(M, p, X)
            for t ∈ 0.1:0.1:0.9
                @test d12 ≈ ReverseDiff.gradient(exp_f, [t])[1]
            end

            retract_f(t) = distance(M, p, retract(M, p, t[1]*X))
            for t ∈ 0.1:0.1:0.9
                @test ReverseDiff.gradient(retract_f, [t])[1] ≥ 0
            end
        end
    end

    test_musical_isomorphisms && @testset "Musical isomorphisms" begin
        if default_inverse_retraction_method !== nothing
            tv_m = inverse_retract(M, pts[1], pts[2],default_inverse_retraction_method)
        else
            tv_m = zero_tangent_vector(M,pts[1])
        end
        ctv_m = flat(M, pts[1], FVector(TangentSpace, tv_m))
        @test ctv_m.type == CotangentSpace
        tv_m_back = sharp(M, pts[1], ctv_m)
        @test tv_m_back.type == TangentSpace
    end

    @testset "number_eltype" begin
        for (p, X) in zip(pts, tv)
            @test number_eltype(X) == number_eltype(p)
            p = retract(M, p, X, default_retraction_method)
            @test number_eltype(p) == number_eltype(p)
        end
    end

    is_mutating && @testset "copyto!" begin
        for (p, X) in zip(pts, tv)
            p2 = allocate(p)
            copyto!(p2, p)
            @test isapprox(M, p2, p)

            X2 = allocate(X)
            if default_inverse_retraction_method === nothing
                X3 = zero_tangent_vector(M, p)
                copyto!(X2, X3)
                @test isapprox(M, p, X2, zero_tangent_vector(M, p))
            else
                q = retract(M, p, X, default_retraction_method)
                X3 = inverse_retract(M, p, q, default_inverse_retraction_method)
                copyto!(X2, X3)
                @test isapprox(M, p, X2, inverse_retract(M, p, q, default_inverse_retraction_method))
            end
        end
    end

    is_mutating && @testset "point distributions" begin
        for p in pts
            prand = allocate(p)
            for pd ∈ point_distributions
                for _ in 1:10
                    @test is_manifold_point(M, rand(pd))
                    if test_mutating_rand
                        rand!(pd, prand)
                        @test is_manifold_point(M, prand)
                    end
                end
            end
        end
    end

    @testset "tangent vector distributions" begin
        for tvd ∈ tvector_distributions
            supp = Manifolds.support(tvd)
            for _ in 1:10
                randtv = rand(tvd)
                atol = rand_tvector_atol_multiplier * find_eps(randtv)
                @test is_tangent_vector(M, supp.point, randtv; atol = atol)
            end
        end
    end
end


text:
using ArgParse

# Transliteration systems and standards

eng_default = Dict("ა" => "a", "ბ" => "b", "გ" => "g", "დ" => "d", "ე" => "e", "ვ" => "v", "ზ" => "z", "თ" => "t'", "ი" => "i", "კ" => "k", "ლ" => "l", "მ" => "m", "ნ" => "n", "ო" => "o", "პ" => "p", "ჟ" => "zh", "რ" => "r", "ს" => "s", "ტ" => "t", "უ" => "u", "ფ" => "p'", "ქ" => "k'", "ღ" => "r'", "ყ" => "q'", "შ" => "sh", "ჩ" => "ch", "ც" => "ts'", "ძ" => "dz", "წ" => "ts", "ჭ" => "ch'", "ხ" => "kh", "ჯ" => "j", "ჰ" => "h")

eng_ISO_9984 = Dict("ა" => "a", "ბ" => "b", "გ" => "g", "დ" => "d", "ე" => "e", "ვ" => "v", "ზ" => "z", "ჱ" => "ē", "თ" => "t̕ ", "ი" => "i", "კ" => "k", "ლ" => "l", "მ" => "m", "ნ" => "n", "ჲ" => "y", "ო" => "o", "პ" => "p", "ჟ" => "ž", "რ" => "r", "ს" => "s", "ტ" => "t", "ჳ" => "w", "უ" => "u", "ფ" => "p'", "ქ" => "k'", "ღ" => "ġ", "ყ" => "q", "შ" => "š", "ჩ" => "č'", "ც" => "c'", "ძ" => "ż", "წ" => "c", "ჭ" => "č", "ხ" => "x", "ჴ" => "x̣", "ჯ" => "j", "ჰ" => "h", "ჵ" => "ō")

ru_custom = Dict("ა" => "а", "ბ" => "б", "გ" => "г", "დ" => "д", "ე" => "э", "ვ" => "в", "ზ" => "з", "თ" => "т'", "ი" => "и", "კ" => "к", "ლ" => "л", "მ" => "м", "ნ" => "н", "ო" => "о", "პ" => "п", "ჟ" => "ж", "რ" => "р", "ს" => "с", "ტ" => "т", "უ" => "у", "ფ" => "п'", "ქ" => "к'", "ღ" => "г'", "ყ" => "к''", "შ" => "ш", "ჩ" => "ч", "ც" => "ц'", "ძ" => "дз", "წ" => "ц", "ჭ" => "ч", "ხ" => "х", "ჯ" => "дж", "ჰ" => "х")

eng_national = Dict("ა" => "a", "ბ" => "b", "გ" => "g", "დ" => "d", "ე" => "e", "ვ" => "v", "ზ" => "z", "თ" => "t", "ი" => "i", "კ" => "k'", "ლ" => "l", "მ" => "m", "ნ" => "n", "ო" => "o", "პ" => "p", "ჟ" => "zh", "რ" => "r", "ს" => "s", "ტ" => "t'", "უ" => "u", "ფ" => "p'", "ქ" => "k", "ღ" => "gh", "ყ" => "q'", "შ" => "sh", "ჩ" => "ch'", "ც" => "ts", "ძ" => "dz", "წ" => "ts'", "ჭ" => "ch'", "ხ" => "kh", "ჯ" => "j", "ჰ" => "h")

# https://en.wikipedia.org/wiki/Romanization_of_Georgian

# Parsing arguments from command line
function parse_commandLine()
	s = ArgParseSettings()

	@add_arg_table! s begin
		"--file"
			help = "your name"
			required = true

		#required = true
		"--mode"
			help = "transliteration system"
			required = true
	end
	return parse_args(s)
end

# Parsing parameters
parsed_args = parse_commandLine()
file_to_transliterate = parsed_args["file"]
transliteration_mode = "eng_default"

# Choosing file in georgian
io = open(file_to_transliterate,"r")
buffer = read(io,String)
buffer = rsplit(buffer,"")

# Function for transliterating (geo-ru by default)
function transliterate()

	# name for transliterated file (the same as source file, but UPPERCASed)
	result_file_name = uppercase(file_to_transliterate)
	transliterated = open(result_file_name,"w")

	if parsed_args["mode"] == "1"
		for i in buffer
			if haskey(eng_default,i)
				for (key, value) in eng_default
					if key == i
						#print(value)
						print(transliterated,value)
					end
				end
			else
				print(transliterated,i)
			end
		end
	elseif parsed_args["mode"] == "2"
		for i in buffer
			if haskey(eng_ISO_9984,i)
				for (key, value) in eng_ISO_9984
					if key == i
						#print(value)
						print(transliterated,value)
					end
				end
			else
				print(transliterated,i)
			end
		end

	elseif parsed_args["mode"] == "3"
		for i in buffer
			if haskey(ru_custom,i)
				for (key, value) in ru_custom
					if key == i
						#print(value)
						print(transliterated,value)
					end
				end
			else
				print(transliterated,i)
			end
		end

	else
		for i in buffer
			if haskey(eng_national,i)
				for (key, value) in eng_national
					if key == i
						#print(value)
						print(transliterated,value)
					end
				end
			else
				print(transliterated,i)
			end
		end
	end

	close(transliterated)
end
transliterate()

ext: jl


text:
using GeometricEquations
using GeometricEquations: check_parameters, parameter_types, symplectic_matrix
using Test

include("functions.jl")
include("initial_conditions.jl")

@testset "$(rpad("Differential Algebraic Equations (DAE)",80))" begin

    dae_eqs  = (dae_v, dae_u, dae_ϕ, nothing, nothing, dae_v)
    dae_eqs1 = (dae_v, dae_u, dae_ϕ, nothing, nothing)
    dae_eqs2 = (dae_v, dae_u, dae_ϕ)

    dae = DAE(dae_eqs..., NullInvariants(), NullParameters(), NullPeriodicity())

    @test invariants(dae) == NullInvariants()
    @test parameters(dae) == NullParameters()
    @test periodicity(dae) == NullPeriodicity()

    @test hasvectorfield(dae) == true
    @test hassecondary(dae) == false
    @test hasinvariants(dae) == false
    @test hasparameters(dae) == false
    @test hasperiodicity(dae) == false

    funcs = functions(dae)
    @test funcs.v == dae_v == dae.v
    @test funcs.u == dae_u == dae.u
    @test funcs.ϕ == dae_ϕ == dae.ϕ
    @test funcs.v̄ == dae_v == dae.v̄
    @test !haskey(funcs, :ū)
    @test !haskey(funcs, :ψ)

    funcs = functions(dae, parameters(dae))
    @test funcs.v != dae_v
    @test funcs.u != dae_u
    @test funcs.ϕ != dae_ϕ
    @test funcs.v̄ != dae_v

    for eqs in (dae_eqs1, dae_eqs2)
        dae1 = DAE(eqs...)
        dae2 = DAE(eqs...; invariants=NullInvariants())
        dae3 = DAE(eqs...; parameters=NullParameters())
        dae4 = DAE(eqs...; periodicity=NullPeriodicity())

        @test dae == dae1
        @test dae == dae2
        @test dae == dae3
        @test dae == dae4

        @test hash(dae) == hash(dae1)
        @test hash(dae) == hash(dae2)
        @test hash(dae) == hash(dae3)
        @test hash(dae) == hash(dae4)
    end

    dae_eqs  = (dae_v, dae_u, dae_ϕ, dae_ū, dae_ψ)
    dae_args = (invariants=(h=pdae_h,), parameters=(a=1,), periodicity=π*ones(2))

    dae = DAE(dae_eqs..., dae_v, dae_args.invariants, parameter_types(dae_args.parameters), dae_args.periodicity)

    @test invariants(dae) == dae_args.invariants
    @test parameters(dae) == parameter_types(dae_args.parameters)
    @test periodicity(dae) == dae_args.periodicity

    @test hasvectorfield(dae) == true
    @test hassecondary(dae) == true
    @test hasinvariants(dae) == true
    @test hasparameters(dae) == true
    @test hasperiodicity(dae) == true

    @test check_parameters(dae, dae_args.parameters) == true

    funcs = functions(dae)
    @test funcs.v == dae_v == dae.v
    @test funcs.u == dae_u == dae.u
    @test funcs.ϕ == dae_ϕ == dae.ϕ
    @test funcs.ū == dae_ū == dae.ū
    @test funcs.ψ == dae_ψ == dae.ψ
    @test funcs.v̄ == dae_v == dae.v̄

    funcs = functions(dae, dae_args.parameters)
    @test funcs.v != dae_v
    @test funcs.u != dae_u
    @test funcs.ϕ != dae_ϕ
    @test funcs.ū != dae_ū
    @test funcs.ψ != dae_ψ
    @test funcs.v̄ != dae_v

    dae1 = DAE(dae_eqs...; v̄=dae_v, invariants=dae_args.invariants, parameters=parameter_types(dae_args.parameters), periodicity=dae_args.periodicity)
    dae2 = DAE(dae_eqs...; invariants=dae_args.invariants, parameters=parameter_types(dae_args.parameters), periodicity=dae_args.periodicity)

    @test dae == dae1
    @test dae == dae2

    @test hash(dae) == hash(dae1)
    @test hash(dae) == hash(dae2)

end

@testset "$(rpad("Partitioned Differential Algebraic Equations (PDAE)",80))" begin

    pdae_eqs  = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, nothing, nothing, nothing, pdae_v, pdae_f)
    pdae_eqs1 = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, nothing, nothing, nothing)
    pdae_eqs2 = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ)

    pdae  = PDAE(pdae_eqs..., NullInvariants(), NullParameters(), NullPeriodicity())

    @test invariants(pdae) == NullInvariants()
    @test parameters(pdae) == NullParameters()
    @test periodicity(pdae) == NullPeriodicity()

    @test hasvectorfield(pdae) == true
    @test hassecondary(pdae) == false
    @test hasinvariants(pdae) == false
    @test hasparameters(pdae) == false
    @test hasperiodicity(pdae) == false

    funcs = functions(pdae)
    @test funcs.v == pdae_v == pdae.v
    @test funcs.f == pdae_f == pdae.f
    @test funcs.u == pdae_u == pdae.u
    @test funcs.g == pdae_g == pdae.g
    @test funcs.ϕ == pdae_ϕ == pdae.ϕ
    @test funcs.v̄ == pdae_v == pdae.v̄
    @test funcs.f̄ == pdae_f == pdae.f̄

    # @test pdae == similar(pdae, t₀, q₀, p₀, λ₀)
    # @test pdae == similar(pdae, t₀, q₀, p₀)
    # @test pdae == similar(pdae, q₀, p₀)

    for eqs in (pdae_eqs1, pdae_eqs2)
        pdae1 = PDAE(eqs...)
        pdae2 = PDAE(eqs...; invariants=NullInvariants())
        pdae3 = PDAE(eqs...; parameters=NullParameters())
        pdae4 = PDAE(eqs...; periodicity=NullPeriodicity())

        @test pdae == pdae1
        @test pdae == pdae2
        @test pdae == pdae3
        @test pdae == pdae4

        @test hash(pdae) == hash(pdae1)
        @test hash(pdae) == hash(pdae2)
        @test hash(pdae) == hash(pdae3)
        @test hash(pdae) == hash(pdae4)
    end

    pdae_eqs  = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, pdae_u, pdae_g, pdae_ψ)
    pdae_args = (invariants=(h=pdae_h,), parameters=(a=1,), periodicity=π*ones(1))

    pdae  = PDAE(pdae_eqs..., pdae_v, pdae_f, pdae_args.invariants, parameter_types(pdae_args.parameters), pdae_args.periodicity)

    @test invariants(pdae) == pdae_args.invariants
    @test parameters(pdae) == parameter_types(pdae_args.parameters)
    @test periodicity(pdae) == pdae_args.periodicity

    @test hasvectorfield(pdae) == true
    @test hassecondary(pdae) == true
    @test hasinvariants(pdae) == true
    @test hasparameters(pdae) == true
    @test hasperiodicity(pdae) == true

    funcs = functions(pdae)
    @test funcs.v == pdae_v == pdae.v
    @test funcs.f == pdae_f == pdae.f
    @test funcs.u == pdae_u == pdae.u
    @test funcs.g == pdae_g == pdae.g
    @test funcs.ϕ == pdae_ϕ == pdae.ϕ
    @test funcs.ū == pdae_u == pdae.ū
    @test funcs.ḡ == pdae_g == pdae.ḡ
    @test funcs.ψ == pdae_ψ == pdae.ψ
    @test funcs.v̄ == pdae_v == pdae.v̄
    @test funcs.f̄ == pdae_f == pdae.f̄

    funcs = functions(pdae, pdae_args.parameters)
    @test funcs.v != pdae_v
    @test funcs.f != pdae_f
    @test funcs.u != pdae_u
    @test funcs.g != pdae_g
    @test funcs.ϕ != pdae_ϕ
    @test funcs.ū != pdae_u
    @test funcs.ḡ != pdae_g
    @test funcs.ψ != pdae_ψ
    @test funcs.v̄ != pdae_v
    @test funcs.f̄ != pdae_f

    pdae1 = PDAE(pdae_eqs...; v̄=pdae_v, f̄=pdae_f, invariants=pdae_args.invariants, parameters=parameter_types(pdae_args.parameters), periodicity=pdae_args.periodicity)
    pdae2 = PDAE(pdae_eqs...; invariants=pdae_args.invariants, parameters=parameter_types(pdae_args.parameters), periodicity=pdae_args.periodicity)

    @test pdae == pdae1
    @test pdae == pdae2

    @test hash(pdae) == hash(pdae1)
    @test hash(pdae) == hash(pdae2)

end

@testset "$(rpad("Implicit Differential Algebraic Equations (IDAE)",80))" begin

    idae_eqs  = (idae_ϑ, pdae_f, pdae_u, pdae_g, pdae_ϕ, nothing, nothing, nothing, idae_v, pdae_f)
    idae_eqs1 = (idae_ϑ, pdae_f, pdae_u, pdae_g, pdae_ϕ, nothing, nothing, nothing)
    idae_eqs2 = (idae_ϑ, pdae_f, pdae_u, pdae_g, pdae_ϕ)

    idae  = IDAE(idae_eqs..., NullInvariants(), NullParameters(), NullPeriodicity())

    @test invariants(idae) == NullInvariants()
    @test parameters(idae) == NullParameters()
    @test periodicity(idae) == NullPeriodicity()

    @test hasvectorfield(idae) == true
    @test hassecondary(idae) == false
    @test hasinvariants(idae) == false
    @test hasparameters(idae) == false
    @test hasperiodicity(idae) == false

    funcs = functions(idae)
    @test funcs.ϑ == idae_ϑ == idae.ϑ
    @test funcs.f == pdae_f == idae.f
    @test funcs.u == pdae_u == idae.u
    @test funcs.g == pdae_g == idae.g
    @test funcs.ϕ == pdae_ϕ == idae.ϕ
    @test funcs.v̄ == idae_v == idae.v̄
    @test funcs.f̄ == pdae_f == idae.f̄

    for eqs in (idae_eqs1, idae_eqs2)
        idae1 = IDAE(eqs...)
        idae2 = IDAE(eqs...; invariants=NullInvariants())
        idae3 = IDAE(eqs...; parameters=NullParameters())
        idae4 = IDAE(eqs...; periodicity=NullPeriodicity())

        @test idae == idae1
        @test idae == idae2
        @test idae == idae3
        @test idae == idae4

        @test hash(idae) == hash(idae1)
        @test hash(idae) == hash(idae2)
        @test hash(idae) == hash(idae3)
        @test hash(idae) == hash(idae4)
    end

    idae_eqs  = (idae_ϑ, pdae_f, pdae_u, pdae_g, pdae_ϕ, pdae_u, pdae_g, pdae_ψ)
    idae_args = (invariants=(h=pdae_h,), parameters=(a=1,), periodicity=π*ones(1))

    idae  = IDAE(idae_eqs..., idae_v, pdae_f, idae_args.invariants, parameter_types(idae_args.parameters), idae_args.periodicity)

    @test invariants(idae) == idae_args.invariants
    @test parameters(idae) == parameter_types(idae_args.parameters)
    @test periodicity(idae) == idae_args.periodicity

    @test hasvectorfield(idae) == true
    @test hassecondary(idae) == true
    @test hasinvariants(idae) == true
    @test hasparameters(idae) == true
    @test hasperiodicity(idae) == true

    funcs = functions(idae)
    @test funcs.ϑ == idae_ϑ == idae.ϑ
    @test funcs.f == pdae_f == idae.f
    @test funcs.u == pdae_u == idae.u
    @test funcs.g == pdae_g == idae.g
    @test funcs.ϕ == pdae_ϕ == idae.ϕ
    @test funcs.ū == pdae_u == idae.ū
    @test funcs.ḡ == pdae_g == idae.ḡ
    @test funcs.ψ == pdae_ψ == idae.ψ
    @test funcs.v̄ == idae_v == idae.v̄
    @test funcs.f̄ == pdae_f == idae.f̄

    funcs = functions(idae, idae_args.parameters)
    @test funcs.ϑ != idae_ϑ
    @test funcs.f != pdae_f
    @test funcs.u != pdae_u
    @test funcs.g != pdae_g
    @test funcs.ϕ != pdae_ϕ
    @test funcs.ū != pdae_u
    @test funcs.ḡ != pdae_g
    @test funcs.ψ != pdae_ψ
    @test funcs.v̄ != idae_v
    @test funcs.f̄ != pdae_f

    idae1 = IDAE(idae_eqs...; v̄=idae_v, f̄=pdae_f, invariants=idae_args.invariants, parameters=parameter_types(idae_args.parameters), periodicity=idae_args.periodicity)
    idae2 = IDAE(idae_eqs...; invariants=idae_args.invariants, parameters=parameter_types(idae_args.parameters), periodicity=idae_args.periodicity)

    @test idae == idae1
    @test idae == idae2

    @test hash(idae) == hash(idae1)
    @test hash(idae) == hash(idae2)

end

@testset "$(rpad("Hamiltonian Differential Algebraic Equations (HDAE)",80))" begin

    hdae_eqs  = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, nothing, nothing, nothing, pdae_v, pdae_f, hdae_ω, pdae_h)
    hdae_eqs1 = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, nothing, nothing, nothing, hdae_ω, pdae_h)
    hdae_eqs2 = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, hdae_ω, pdae_h)

    hdae = HDAE(hdae_eqs..., NullInvariants(), NullParameters(), NullPeriodicity())

    @test hasvectorfield(hdae) == true
    @test hashamiltonian(hdae) == true
    @test hassecondary(hdae) == false
    @test hasinvariants(hdae) == false
    @test hasparameters(hdae) == false
    @test hasperiodicity(hdae) == false

    funcs = functions(hdae)
    @test funcs.v == pdae_v == hdae.v
    @test funcs.f == pdae_f == hdae.f
    @test funcs.u == pdae_u == hdae.u
    @test funcs.g == pdae_g == hdae.g
    @test funcs.ϕ == pdae_ϕ == hdae.ϕ
    @test funcs.v̄ == pdae_v == hdae.v̄
    @test funcs.f̄ == pdae_f == hdae.f̄
    @test funcs.h == pdae_h == hdae.hamiltonian
    @test funcs.poisson == hdae_ω == hdae.poisson

    for eqs in (hdae_eqs1, hdae_eqs2)
        hdae1 = HDAE(eqs...)
        hdae2 = HDAE(eqs...; invariants=NullInvariants())
        hdae3 = HDAE(eqs...; parameters=NullParameters())
        hdae4 = HDAE(eqs...; periodicity=NullPeriodicity())

        @test hdae == hdae1
        @test hdae == hdae2
        @test hdae == hdae3
        @test hdae == hdae4

        @test hash(hdae) == hash(hdae1)
        @test hash(hdae) == hash(hdae2)
        @test hash(hdae) == hash(hdae3)
        @test hash(hdae) == hash(hdae4)
    end

    hdae_eqs  = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, pdae_u, pdae_g, pdae_ψ, pdae_v, pdae_f, hdae_ω, pdae_h)
    hdae_eqs1 = (pdae_v, pdae_f, pdae_u, pdae_g, pdae_ϕ, pdae_u, pdae_g, pdae_ψ, hdae_ω, pdae_h)
    hdae_args = (invariants=(h=pdae_h,), parameters=(a=1,), periodicity=π*ones(1))

    hdae = HDAE(hdae_eqs..., hdae_args.invariants, parameter_types(hdae_args.parameters), hdae_args.periodicity)

    @test hasvectorfield(hdae) == true
    @test hashamiltonian(hdae) == true
    @test hassecondary(hdae) == true
    @test hasinvariants(hdae) == true
    @test hasparameters(hdae) == true
    @test hasperiodicity(hdae) == true

    funcs = functions(hdae)
    @test funcs.v == pdae_v == hdae.v
    @test funcs.f == pdae_f == hdae.f
    @test funcs.u == pdae_u == hdae.u
    @test funcs.g == pdae_g == hdae.g
    @test funcs.ϕ == pdae_ϕ == hdae.ϕ
    @test funcs.ū == pdae_u == hdae.ū
    @test funcs.ḡ == pdae_g == hdae.ḡ
    @test funcs.ψ == pdae_ψ == hdae.ψ
    @test funcs.v̄ == pdae_v == hdae.v̄
    @test funcs.f̄ == pdae_f == hdae.f̄
    @test funcs.h == pdae_h == hdae.hamiltonian
    @test funcs.poisson == hdae_ω == hdae.poisson

    funcs = functions(hdae, hdae_args.parameters)
    @test funcs.v != pdae_v
    @test funcs.f != pdae_f
    @test funcs.u != pdae_u
    @test funcs.g != pdae_g
    @test funcs.ϕ != pdae_ϕ
    @test funcs.ū != pdae_u
    @test funcs.ḡ != pdae_g
    @test funcs.ψ != pdae_ψ
    @test funcs.v̄ != pdae_v
    @test funcs.f̄ != pdae_f
    @test funcs.h != pdae_h
    @test funcs.poisson != hdae_ω

    hdae1 = HDAE(hdae_eqs1...; v̄=pdae_v, f̄=pdae_f, invariants=hdae_args.invariants, parameters=parameter_types(hdae_args.parameters), periodicity=hdae_args.periodicity)
    hdae2 = HDAE(hdae_eqs1...; invariants=hdae_args.invariants, parameters=parameter_types(hdae_args.parameters), periodicity=hdae_args.periodicity)

    @test hdae == hdae1
    @test hdae == hdae2

    @test hash(hdae) == hash(hdae1)
    @test hash(hdae) == hash(hdae2)

end

@testset "$(rpad("Variational Differential Algebraic Equations (LDAE)",80))" begin

    ldae_eqs  = (iode_ϑ, iode_f, iode_u, iode_g, pdae_ϕ, nothing, nothing, nothing, lode_ω, ldae_v, iode_f, lode_l)
    ldae_eqs1 = (iode_ϑ, iode_f, iode_u, iode_g, pdae_ϕ, nothing, nothing, nothing, lode_ω, lode_l)
    ldae_eqs2 = (iode_ϑ, iode_f, iode_u, iode_g, pdae_ϕ, lode_ω, lode_l)

    ldae = LDAE(ldae_eqs..., NullInvariants(), NullParameters(), NullPeriodicity())

    @test hassecondary(ldae) == false
    @test hasinvariants(ldae) == false
    @test hasparameters(ldae) == false
    @test hasperiodicity(ldae) == false

    funcs = functions(ldae)
    @test funcs.ϑ == iode_ϑ == ldae.ϑ
    @test funcs.f == iode_f == ldae.f
    @test funcs.u == iode_u == ldae.u
    @test funcs.g == iode_g == ldae.g
    @test funcs.ϕ == pdae_ϕ == ldae.ϕ
    @test funcs.ω == lode_ω == ldae.ω
    @test funcs.v̄ == ldae_v == ldae.v̄
    @test funcs.f̄ == iode_f == ldae.f̄
    @test funcs.l == lode_l == ldae.lagrangian

    for eqs in (ldae_eqs1, ldae_eqs2)
        ldae1 = LDAE(eqs...)
        ldae2 = LDAE(eqs...; invariants=NullInvariants())
        ldae3 = LDAE(eqs...; parameters=NullParameters())
        ldae4 = LDAE(eqs...; periodicity=NullPeriodicity())

        @test ldae.ϑ == ldae1.ϑ
        @test ldae.f == ldae1.f
        @test ldae.u == ldae1.u
        @test ldae.g == ldae1.g
        @test ldae.ϕ == ldae1.ϕ
        @test ldae.ω == ldae1.ω
        @test ldae.v̄ == ldae1.v̄
        @test ldae.f̄ == ldae1.f̄

        @test ldae == ldae1
        @test ldae == ldae2
        @test ldae == ldae3
        @test ldae == ldae4

        @test hash(ldae) == hash(ldae1)
        @test hash(ldae) == hash(ldae2)
        @test hash(ldae) == hash(ldae3)
        @test hash(ldae) == hash(ldae4)
    end

    ldae_eqs  = (iode_ϑ, iode_f, iode_u, iode_g, pdae_ϕ, pdae_u, pdae_g, pdae_ψ)
    ldae_args = (invariants=(h=iode_h,), parameters=(a=1,), periodicity=π*ones(1))

    ldae = LDAE(ldae_eqs..., lode_ω, iode_v, iode_f, lode_l, ldae_args.invariants, parameter_types(ldae_args.parameters), ldae_args.periodicity)

    @test hassecondary(ldae) == true
    @test hasinvariants(ldae) == true
    @test hasparameters(ldae) == true
    @test hasperiodicity(ldae) == true

    funcs = functions(ldae)
    @test funcs.ϑ == iode_ϑ == ldae.ϑ
    @test funcs.f == iode_f == ldae.f
    @test funcs.u == iode_u == ldae.u
    @test funcs.g == iode_g == ldae.g
    @test funcs.ϕ == pdae_ϕ == ldae.ϕ
    @test funcs.ω == lode_ω == ldae.ω
    @test funcs.v̄ == iode_v == ldae.v̄
    @test funcs.f̄ == iode_f == ldae.f̄
    @test funcs.l == lode_l == ldae.lagrangian

    funcs = functions(ldae, ldae_args.parameters)
    @test funcs.ϑ != iode_ϑ
    @test funcs.f != iode_f
    @test funcs.u != iode_u
    @test funcs.g != iode_g
    @test funcs.ϕ != pdae_ϕ
    @test funcs.ω != lode_ω
    @test funcs.v̄ != iode_v
    @test funcs.f̄ != iode_f
    @test funcs.l != lode_l

    # ldae1 = LDAE(ldae_eqs..., lode_l, lode_ω, t₀, [q₀], [p₀], [λ₀], [λ₀]; v̄=iode_v, ldae_args...)
    # ldae2 = LDAE(ldae_eqs..., lode_l, lode_ω, t₀, [q₀], [p₀], [λ₀]; v̄=iode_v, ldae_args...)
    # ldae3 = LDAE(ldae_eqs..., lode_l, lode_ω, [q₀], [p₀], [λ₀], [λ₀]; v̄=iode_v, ldae_args...)
    # ldae4 = LDAE(ldae_eqs..., lode_l, lode_ω, [q₀], [p₀], [λ₀]; v̄=iode_v, ldae_args...)
    # ldae5 = LDAE(ldae_eqs..., lode_l, lode_ω, t₀, q₀, p₀, λ₀, λ₀; v̄=iode_v, ldae_args...)
    # ldae6 = LDAE(ldae_eqs..., lode_l, lode_ω, t₀, q₀, p₀, λ₀; v̄=iode_v, ldae_args...)
    # ldae7 = LDAE(ldae_eqs..., lode_l, lode_ω, q₀, p₀, λ₀, λ₀; v̄=iode_v, ldae_args...)
    # ldae8 = LDAE(ldae_eqs..., lode_l, lode_ω, q₀, p₀, λ₀; v̄=iode_v, ldae_args...)

    # @test ldae == ldae1
    # @test ldae == ldae2
    # @test ldae == ldae3
    # @test ldae == ldae4
    # @test ldae == ldae5
    # @test ldae == ldae6
    # @test ldae == ldae7
    # @test ldae == ldae8

    # @test hash(ldae) == hash(ldae1)
    # @test hash(ldae) == hash(ldae2)
    # @test hash(ldae) == hash(ldae3)
    # @test hash(ldae) == hash(ldae4)
    # @test hash(ldae) == hash(ldae5)
    # @test hash(ldae) == hash(ldae6)
    # @test hash(ldae) == hash(ldae7)
    # @test hash(ldae) == hash(ldae8)

end

@testset "$(rpad("Split Partitioned Differential Algebraic Equations (SPDAE)",80))" begin

    spdae_v = (pdae_v, pdae_u, pdae_u)
    spdae_f = (pdae_f, pdae_g, pdae_g)
    spdae_eqs = (spdae_v, spdae_f, pdae_ϕ, pdae_ψ)

    spdae = SPDAE(spdae_eqs..., t₀, [q₀], [p₀], [λ₀], [λ₀], NullInvariants(), NullParameters(), NullPeriodicity())

    @test ndims(spdae) == 1
    @test nsamples(spdae) == 1
    @test nconstraints(spdae) == 1

    @test periodicity(spdae) == zero(q₀)
    @test initial_conditions(spdae) == (t₀, [q₀], [p₀], [λ₀], [λ₀])

    @test hasinvariants(spdae) == false
    @test hasparameters(spdae) == false
    @test hasperiodicity(spdae) == false

    funcs = functions(spdae)
    @test funcs.v == spdae_v == spdae.v
    @test funcs.f == spdae_f == spdae.f
    @test funcs.ϕ ==  pdae_ϕ == spdae.ϕ
    @test funcs.ψ ==  pdae_ψ == spdae.ψ

    @test spdae == similar(spdae, t₀, q₀, p₀, λ₀)
    @test spdae == similar(spdae, t₀, q₀, p₀)
    @test spdae == similar(spdae, q₀, p₀)

    spdae1 = SPDAE(spdae_eqs..., t₀, [q₀], [p₀], [λ₀], [λ₀])
    spdae2 = SPDAE(spdae_eqs..., t₀, [q₀], [p₀], [λ₀])
    spdae3 = SPDAE(spdae_eqs..., [q₀], [p₀], [λ₀], [λ₀])
    spdae4 = SPDAE(spdae_eqs..., [q₀], [p₀], [λ₀])
    spdae5 = SPDAE(spdae_eqs..., t₀, q₀, p₀, λ₀, λ₀)
    spdae6 = SPDAE(spdae_eqs..., t₀, q₀, p₀, λ₀)
    spdae7 = SPDAE(spdae_eqs..., q₀, p₀, λ₀, λ₀)
    spdae8 = SPDAE(spdae_eqs..., q₀, p₀, λ₀)

    @test spdae == spdae1
    @test spdae == spdae2
    @test spdae == spdae3
    @test spdae == spdae4
    @test spdae == spdae5
    @test spdae == spdae6
    @test spdae == spdae7
    @test spdae == spdae8

    @test hash(spdae) == hash(spdae1)
    @test hash(spdae) == hash(spdae2)
    @test hash(spdae) == hash(spdae3)
    @test hash(spdae) == hash(spdae4)
    @test hash(spdae) == hash(spdae5)
    @test hash(spdae) == hash(spdae6)
    @test hash(spdae) == hash(spdae7)
    @test hash(spdae) == hash(spdae8)

end


text:
"""
KhTrack(args...)  -> PyPlot Plot

Return a plot Normalized Flow Capacity Kh logs in a single track.

 The next table show the list of variables allowed:

 The next table show the list of variables allowed:
|PropertyName|Args|Default|Input|Description
|---|---|---|---|---|
|Depth|Mandatory| -- | Array{Number,1}| 1D Array of Depths|
|Kh|Mandatory| -- | Array{Number,1}|1D Array of Kh Values|
|DepthFrom|Optional| minimum(Depth) | Number |Set the Depth upper limit to plot|
|DepthTo|Optional| maximum(Depth) | Number |  Set the Depth lower limit to plot|
|Dtick|Optional|true|Dtick=Bool|Set the Depth ticks On/Off|
"""
function KhTrack(Depth,Kh;DepthFrom=minimum(Depth),DepthTo=maximum(Depth),Dtick=false)
    khax=gca()
    khax.plot(Kh,Depth,linewidth=2,color="black")
    khax.set_xlim([0,1])
    khax.set_ylim([DepthTo,DepthFrom])
    khax.set_xlabel("Norm. Flow Capacity")
    khax.xaxis.tick_top()
    khax.xaxis.set_label_position("top")
    setp(khax.get_yticklabels(),visible=Dtick)
    khax.set_yticks(range(DepthFrom, stop=DepthTo, length=51),minor=true)
    khax.set_yticks(range(DepthFrom, stop=DepthTo, length=11))
end


text:
using Revise
import Distributions: Normal
using Random.randn

using Plots, Makie

const a::Int = 'c'  # struct Struct_wrong3 end

macro test_macro() end

"""
    test_fun(a::Int)
For test only
"""
function test_fun(a::Int, b::T) where #
        T <:Array{S} where S <: Number
    println(a)
    println("foo")
end

function Base.ifelse(a::Int)
    println("bar")
end
function lone_function end

function run_test(a::T) where T<:Int; a::Int end

eq(c=4; b=(1,2,3), c=:a=>5) = a == b
eq(a::T, b::T, c=4; b=(1,2,3), c=:a=>5)::T where T<:Real = (a == b; a)

#=
  Structs
=#
abstract type ATest end

mutable struct STest <: ATest; a::Int end

struct Test1 <: ATest
    a::Int
    α::Real

    Test1() = new(0)
    Test1(a) = new(1)
    Test1(a, b) = begin new(2) end
end


text:
using DelimitedFiles

x = readdlm("data_1.dat", '\n', Int, '\n')

global sumcount = 0

for n in 1:length(x)-3
    sum1 = sum(x[n:n+2])
    sum2 = sum(x[n+1:n+3])
    if sum2 > sum1
        global sumcount += 1
    end
end

print(sumcount)


text:
using Documenter
using Currencies

makedocs(
    modules = [Currencies],
    doctest = false
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
deploydocs(
    deps = Deps.pip(
            "pygments",
            "mkdocs",
            "mkdocs-material",
            "python-markdown-math"),
    repo = "github.com/JuliaFinance/Currencies.jl.git"
)


text:
import Printf: ini_dec, fix_dec, ini_hex, ini_HEX

if VERSION < v"1.1"
using Base.Grisu: DIGITSs
fix_dec(out, d::ArbFloat{P}, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
fix_dec(out, d::ArbReal{P}, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
ini_dec(out, d::ArbFloat{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
ini_dec(out, d::ArbReal{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
ini_hex(out, d::ArbFloat{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
ini_HEX(out, d::ArbFloat{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
ini_hex(out, d::ArbReal{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
ini_HEX(out, d::ArbReal{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, DIGITSs[Threads.threadid()])
else
fix_dec(out, d::ArbFloat{P}, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
fix_dec(out, d::ArbReal{P}, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
ini_dec(out, d::ArbFloat{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
ini_dec(out, d::ArbReal{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
ini_hex(out, d::ArbFloat{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
ini_HEX(out, d::ArbFloat{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
ini_hex(out, d::ArbReal{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
ini_HEX(out, d::ArbReal{P}, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) where P =
    Base.Printf.bigfloat_printf(out, BigFloat(d), flags, width, precision, c, digits)
end


text:
module IDFDataCanada

using AxisArrays, CSV, DataFrames, Dates, Glob, HTTP, LibCURL, NCDatasets, FTPClient

include("functions.jl")

export get_idf, txt2csv, txt2netcdf, data_download, netcdf_generator, drive_download

end


text:
function right!(r)
    r = 0
    while (isborder(r, Ost) && !isborder(r,Nord))==true
        move!(r,Nord)
        r = r + 1
    end
    if !isborder(r,Ost)
        move!(r,Ost)
        while isborder(r,Sud)
            move!(r,Ost)
        end
    end
    while r > 0
        move!(r, Sud)
        r = r - 1
    end
end
function left!(r)
    l = 0
    while (isborder(r, West) && !isborder(r,Nord))==true
        move!(r, Nord)
        l = l + 1
    end
    if !isborder(r,West)
        move!(r,West)
        while isborder(r,Sud)
            move!(r,West)
        end
    end
    while l > 0
        move!(r, Sud)
        l = l - 1
    end
end
function mark_cross!(r::Robot)
    a = 0
    b = 0
    while (isborder(r,Nord) && isborder(r,West))==false
        while !isborder(r , Nord)
            move!(r, Nord)
            a = a + 1
        end
        while !isborder(r, West)
            move!(r,West)
            b = b + 1
        end
    end
    while ismarker(r)
        while !isborder(r, Ost)
            if ismarker(r)
                break
            end
            if isborder(r, Ost)
                right!(r)
            else
                move!(r, Ost)
            end
        end
        if ismarker(r)
            break
        end
        move!(r, Sud)
        while !isborder(r, West)
            if ismarker(r)
                break
            end
            if isborder(r, West)
                left!(r)
            else
                move!(r, West)
            end
        end
        if ismarker(r)
            break
        end
        move!(r, Sud)
    end
end


text:
# Defines detection for default `ShowBackend` and convenience methods
# that use that backend.

"""
    default_showbackend()

Return the default [`ShowBackend`](#) to use. If a Makie.jl backend
is loaded (i.e. `Makie.current_backend[] !== missing`), return [`ShowMakie`](#).
Else, return [`ShowText`](#).
"""
default_showbackend() = ShowText()

showblock(block, obs) = showblock(default_showbackend(), block, obs)
showblocks(block, obss) = showblocks(default_showbackend(), block, obss)
showblockinterpretable(encodings, block, obs) =
    showblockinterpretable(default_showbackend(), encodings, block, obs)
showblocksinterpretable(encodings, block, obs) =
    showblocksinterpretable(default_showbackend(), encodings, block, obs)


text:
abstract type BinningProbabilitiesEstimator <: ProbabilitiesEstimator end

include("binning_schemes.jl")

include("count_box_visits.jl")
include("histogram_estimation.jl")

include("VisitationFrequency.jl")
include("TransferOperator.jl")


text:
using Pkg.Artifacts
## YOLOV2
function v2_COCO(;batch=1, silent=false, w=608, h=608, cfgchanges=[(:net, 1, :width, w), (:net, 1, :height, h)])
    yolo(joinpath(models_dir,"yolov2-608.cfg"), joinpath(artifact"yolov2-COCO", "yolov2-COCO.weights"), batch, silent=silent, cfgchanges=cfgchanges)
end
v2_608_COCO(;batch=1, silent=false, cfgchanges=nothing) = v2_COCO(w=608, h=608, batch=batch, silent=silent, cfgchanges=cfgchanges)

## YOLOV2-tiny
function v2_tiny_COCO(;batch=1, silent=false, w=416, h=416, cfgchanges=[(:net, 1, :width, w), (:net, 1, :height, h)])
    yolo(joinpath(models_dir,"yolov2-tiny.cfg"), joinpath(artifact"yolov2-tiny-COCO", "yolov2-tiny-COCO.weights"), batch, silent=silent, cfgchanges=cfgchanges)
end
v2_tiny_416_COCO(;batch=1, silent=false, cfgchanges=nothing) = v2_tiny_COCO(w=416, h=416, batch=batch, silent=silent, cfgchanges=cfgchanges)

## YOLOV3
function v3_COCO(;batch=1, silent=false, w=416, h=416, cfgchanges=[(:net, 1, :width, w), (:net, 1, :height, h)])
    yolo(joinpath(models_dir,"yolov3-416.cfg"), joinpath(artifact"yolov3-COCO", "yolov3-COCO.weights"), batch, silent=silent, cfgchanges=cfgchanges)
end
v3_320_COCO(;batch=1, silent=false, cfgchanges=nothing) = v3_COCO(w=320, h=320, batch=batch, silent=silent, cfgchanges=cfgchanges)
v3_416_COCO(;batch=1, silent=false, cfgchanges=nothing) = v3_COCO(w=416, h=416, batch=batch, silent=silent, cfgchanges=cfgchanges)
v3_608_COCO(;batch=1, silent=false, cfgchanges=nothing) = v3_COCO(w=608, h=608, batch=batch, silent=silent, cfgchanges=cfgchanges)

## YOLOV3 SPP
function v3_SPP_COCO(;batch=1, silent=false, w=608, h=608, cfgchanges=[(:net, 1, :width, w), (:net, 1, :height, h)])
    yolo(joinpath(models_dir,"yolov3-spp.cfg"), joinpath(artifact"yolov3-spp-COCO", "yolov3-spp-COCO.weights"), batch, silent=silent, cfgchanges=cfgchanges)
end
v3_spp_608_COCO(;batch=1, silent=false, cfgchanges=nothing) = v3_SPP_COCO(w=608, h=608, batch=batch, silent=silent, cfgchanges=cfgchanges)

## YOLOV3-tiny
function v3_tiny_COCO(;batch=1, silent=false, w=416, h=416, cfgchanges=[(:net, 1, :width, w), (:net, 1, :height, h)])
    yolo(joinpath(models_dir,"yolov3-tiny.cfg"), joinpath(artifact"yolov3-tiny-COCO", "yolov3-tiny-COCO.weights"), batch, silent=silent, cfgchanges=cfgchanges)
end

## YOLOV3-tiny
function v3_tiny_COCO_LOCALFILE(;cfgFile=joinpath(models_dir,"yolov3-tiny.cfg"),weigthsFile=joinpath(artifact"yolov3-tiny-COCO", "yolov3-tiny-COCO.weights"),batch=1, silent=false, w=416, h=416, cfgchanges=[(:net, 1, :width, w), (:net, 1, :height, h)])
    yolo(cfgFile, weigthsFile, batch, silent=silent, cfgchanges=cfgchanges)
end

v3_tiny_416_COCO(;batch=1, silent=false, cfgchanges=nothing) = v3_tiny_COCO(w=416, h=416, batch=batch, silent=silent, cfgchanges=cfgchanges)


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

struct DeploymentOperationsApi <: SwaggerApi
    client::Swagger.Client
end

"""

Gets a deployments operation.
Param: resourceGroupName::String (required)
Param: deploymentName::String (required)
Param: operationId::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: DeploymentOperation
"""
function _swaggerinternal_deploymentOperationsGet(_api::DeploymentOperationsApi, resourceGroupName::String, deploymentName::String, operationId::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "deploymentOperationsGet", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "deploymentOperationsGet", :minLength, resourceGroupName, 1)

    Swagger.validate_param("deploymentName", "deploymentOperationsGet", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsGet", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperation, "/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/deployments/{deploymentName}/operations/{operationId}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "operationId", operationId)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsGet(_api::DeploymentOperationsApi, resourceGroupName::String, deploymentName::String, operationId::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGet(_api, resourceGroupName, deploymentName, operationId, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsGet(_api::DeploymentOperationsApi, response_stream::Channel, resourceGroupName::String, deploymentName::String, operationId::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGet(_api, resourceGroupName, deploymentName, operationId, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets a deployments operation.
Param: groupId::String (required)
Param: deploymentName::String (required)
Param: operationId::String (required)
Param: api_version::String (required)
Return: DeploymentOperation
"""
function _swaggerinternal_deploymentOperationsGetAtManagementGroupScope(_api::DeploymentOperationsApi, groupId::String, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    Swagger.validate_param("groupId", "deploymentOperationsGetAtManagementGroupScope", :maxLength, groupId, 90)
    Swagger.validate_param("groupId", "deploymentOperationsGetAtManagementGroupScope", :minLength, groupId, 1)

    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtManagementGroupScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtManagementGroupScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperation, "/providers/Microsoft.Management/managementGroups/{groupId}/providers/Microsoft.Resources/deployments/{deploymentName}/operations/{operationId}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "groupId", groupId)  # type String
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "operationId", operationId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsGetAtManagementGroupScope(_api::DeploymentOperationsApi, groupId::String, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtManagementGroupScope(_api, groupId, deploymentName, operationId, api_version; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsGetAtManagementGroupScope(_api::DeploymentOperationsApi, response_stream::Channel, groupId::String, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtManagementGroupScope(_api, groupId, deploymentName, operationId, api_version; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets a deployments operation.
Param: scope::String (required)
Param: deploymentName::String (required)
Param: operationId::String (required)
Param: api_version::String (required)
Return: DeploymentOperation
"""
function _swaggerinternal_deploymentOperationsGetAtScope(_api::DeploymentOperationsApi, scope::String, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperation, "/{scope}/providers/Microsoft.Resources/deployments/{deploymentName}/operations/{operationId}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "scope", scope)  # type String
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "operationId", operationId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsGetAtScope(_api::DeploymentOperationsApi, scope::String, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtScope(_api, scope, deploymentName, operationId, api_version; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsGetAtScope(_api::DeploymentOperationsApi, response_stream::Channel, scope::String, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtScope(_api, scope, deploymentName, operationId, api_version; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets a deployments operation.
Param: deploymentName::String (required)
Param: operationId::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: DeploymentOperation
"""
function _swaggerinternal_deploymentOperationsGetAtSubscriptionScope(_api::DeploymentOperationsApi, deploymentName::String, operationId::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtSubscriptionScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtSubscriptionScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperation, "/subscriptions/{subscriptionId}/providers/Microsoft.Resources/deployments/{deploymentName}/operations/{operationId}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "operationId", operationId)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsGetAtSubscriptionScope(_api::DeploymentOperationsApi, deploymentName::String, operationId::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtSubscriptionScope(_api, deploymentName, operationId, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsGetAtSubscriptionScope(_api::DeploymentOperationsApi, response_stream::Channel, deploymentName::String, operationId::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtSubscriptionScope(_api, deploymentName, operationId, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets a deployments operation.
Param: deploymentName::String (required)
Param: operationId::String (required)
Param: api_version::String (required)
Return: DeploymentOperation
"""
function _swaggerinternal_deploymentOperationsGetAtTenantScope(_api::DeploymentOperationsApi, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtTenantScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsGetAtTenantScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperation, "/providers/Microsoft.Resources/deployments/{deploymentName}/operations/{operationId}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "operationId", operationId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsGetAtTenantScope(_api::DeploymentOperationsApi, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtTenantScope(_api, deploymentName, operationId, api_version; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsGetAtTenantScope(_api::DeploymentOperationsApi, response_stream::Channel, deploymentName::String, operationId::String, api_version::String; _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsGetAtTenantScope(_api, deploymentName, operationId, api_version; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets all deployments operations for a deployment.
Param: resourceGroupName::String (required)
Param: deploymentName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: top::Int32
Return: DeploymentOperationsListResult
"""
function _swaggerinternal_deploymentOperationsList(_api::DeploymentOperationsApi, resourceGroupName::String, deploymentName::String, api_version::String, subscriptionId::String; top=nothing, _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "deploymentOperationsList", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "deploymentOperationsList", :minLength, resourceGroupName, 1)

    Swagger.validate_param("deploymentName", "deploymentOperationsList", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsList", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperationsListResult, "/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/deployments/{deploymentName}/operations", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "\$top", top)  # type Int32
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsList(_api::DeploymentOperationsApi, resourceGroupName::String, deploymentName::String, api_version::String, subscriptionId::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsList(_api, resourceGroupName, deploymentName, api_version, subscriptionId; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsList(_api::DeploymentOperationsApi, response_stream::Channel, resourceGroupName::String, deploymentName::String, api_version::String, subscriptionId::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsList(_api, resourceGroupName, deploymentName, api_version, subscriptionId; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets all deployments operations for a deployment.
Param: groupId::String (required)
Param: deploymentName::String (required)
Param: api_version::String (required)
Param: top::Int32
Return: DeploymentOperationsListResult
"""
function _swaggerinternal_deploymentOperationsListAtManagementGroupScope(_api::DeploymentOperationsApi, groupId::String, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    Swagger.validate_param("groupId", "deploymentOperationsListAtManagementGroupScope", :maxLength, groupId, 90)
    Swagger.validate_param("groupId", "deploymentOperationsListAtManagementGroupScope", :minLength, groupId, 1)

    Swagger.validate_param("deploymentName", "deploymentOperationsListAtManagementGroupScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtManagementGroupScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperationsListResult, "/providers/Microsoft.Management/managementGroups/{groupId}/providers/Microsoft.Resources/deployments/{deploymentName}/operations", ["azure_auth"])
    Swagger.set_param(_ctx.path, "groupId", groupId)  # type String
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.query, "\$top", top)  # type Int32
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsListAtManagementGroupScope(_api::DeploymentOperationsApi, groupId::String, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtManagementGroupScope(_api, groupId, deploymentName, api_version; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsListAtManagementGroupScope(_api::DeploymentOperationsApi, response_stream::Channel, groupId::String, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtManagementGroupScope(_api, groupId, deploymentName, api_version; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets all deployments operations for a deployment.
Param: scope::String (required)
Param: deploymentName::String (required)
Param: api_version::String (required)
Param: top::Int32
Return: DeploymentOperationsListResult
"""
function _swaggerinternal_deploymentOperationsListAtScope(_api::DeploymentOperationsApi, scope::String, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperationsListResult, "/{scope}/providers/Microsoft.Resources/deployments/{deploymentName}/operations", ["azure_auth"])
    Swagger.set_param(_ctx.path, "scope", scope)  # type String
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.query, "\$top", top)  # type Int32
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsListAtScope(_api::DeploymentOperationsApi, scope::String, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtScope(_api, scope, deploymentName, api_version; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsListAtScope(_api::DeploymentOperationsApi, response_stream::Channel, scope::String, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtScope(_api, scope, deploymentName, api_version; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets all deployments operations for a deployment.
Param: deploymentName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: top::Int32
Return: DeploymentOperationsListResult
"""
function _swaggerinternal_deploymentOperationsListAtSubscriptionScope(_api::DeploymentOperationsApi, deploymentName::String, api_version::String, subscriptionId::String; top=nothing, _mediaType=nothing)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtSubscriptionScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtSubscriptionScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperationsListResult, "/subscriptions/{subscriptionId}/providers/Microsoft.Resources/deployments/{deploymentName}/operations", ["azure_auth"])
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "\$top", top)  # type Int32
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsListAtSubscriptionScope(_api::DeploymentOperationsApi, deploymentName::String, api_version::String, subscriptionId::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtSubscriptionScope(_api, deploymentName, api_version, subscriptionId; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsListAtSubscriptionScope(_api::DeploymentOperationsApi, response_stream::Channel, deploymentName::String, api_version::String, subscriptionId::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtSubscriptionScope(_api, deploymentName, api_version, subscriptionId; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Gets all deployments operations for a deployment.
Param: deploymentName::String (required)
Param: api_version::String (required)
Param: top::Int32
Return: DeploymentOperationsListResult
"""
function _swaggerinternal_deploymentOperationsListAtTenantScope(_api::DeploymentOperationsApi, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtTenantScope", :maxLength, deploymentName, 64)
    Swagger.validate_param("deploymentName", "deploymentOperationsListAtTenantScope", :minLength, deploymentName, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", DeploymentOperationsListResult, "/providers/Microsoft.Resources/deployments/{deploymentName}/operations", ["azure_auth"])
    Swagger.set_param(_ctx.path, "deploymentName", deploymentName)  # type String
    Swagger.set_param(_ctx.query, "\$top", top)  # type Int32
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function deploymentOperationsListAtTenantScope(_api::DeploymentOperationsApi, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtTenantScope(_api, deploymentName, api_version; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deploymentOperationsListAtTenantScope(_api::DeploymentOperationsApi, response_stream::Channel, deploymentName::String, api_version::String; top=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deploymentOperationsListAtTenantScope(_api, deploymentName, api_version; top=top, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

export deploymentOperationsGet, deploymentOperationsGetAtManagementGroupScope, deploymentOperationsGetAtScope, deploymentOperationsGetAtSubscriptionScope, deploymentOperationsGetAtTenantScope, deploymentOperationsList, deploymentOperationsListAtManagementGroupScope, deploymentOperationsListAtScope, deploymentOperationsListAtSubscriptionScope, deploymentOperationsListAtTenantScope


text:
# Functional implementation of https://egraphs-good.github.io/
# https://dl.acm.org/doi/10.1145/3434304

using DataStructures

"""
Abstract type representing an [`EGraph`](@ref) analysis,
attaching values from a join semi-lattice domain to
an EGraph
"""
abstract type AbstractAnalysis end
const ClassMem = Dict{Int64,EClassData}
const HashCons = Dict{Any,Int64}
const Analyses = Vector{AbstractAnalysis}
const SymbolCache = Dict{Any, Vector{Int64}}

"""
A concrete type representing an [`EGraph`].
See the [egg paper](https://dl.acm.org/doi/pdf/10.1145/3434304)
for implementation details
"""
mutable struct EGraph
    """stores the equality relations over e-class ids"""
    U::IntDisjointSets
    """map from eclass id to eclasses"""
    M::ClassMem             #
    H::HashCons             # hashcons
    """worklist for ammortized upwards merging"""
    dirty::Vector{Int64}
    root::Int64
    """A vector of analyses associated to the EGraph"""
    analyses::Analyses
    """
    a cache mapping function symbols to e-classes that
    contain e-nodes with that function symbol.
    """
    symcache::SymbolCache
end

EGraph() = EGraph(
    IntDisjointSets(0),
    ClassMem(),
    HashCons(),
    # ParentMem(),
    Vector{Int64}(),
    0,
    Analyses(),
    SymbolCache()
)

function EGraph(e)
    G = EGraph()
    rootclass = addexpr!(G, e)
    G.root = rootclass.id
    G
end

function canonicalize(g::EGraph, n::ENode)
    new_args = map(x -> find(g, x), n.args)
    ENode(n.head, new_args, n.sourcetype, n.metadata)
end

function canonicalize!(g::EGraph, n::ENode)
    for i ∈ 1:ariety(n)
        n.args[i] = find(g, n.args[i])
    end
    return n
end

"""
Returns the canonical e-class id for a given e-class.
"""
find(G::EGraph, a::Int64)::Int64 = find_root!(G.U, a)
find(G::EGraph, a::EClass)::Int64 = find_root!(G.U, a.id)

### Definition 2.3: canonicalization
# iscanonical(U::IntDisjointSets, n::Expr) = n == canonicalize(U, n)
iscanonical(g::EGraph, n::ENode) = n == canonicalize(g, n)
iscanonical(g::EGraph, e::EClass) = find(g, e.id) == e.id

"""
Inserts an e-node in an [`EGraph`](@ref)
"""
function add!(G::EGraph, n::ENode)::EClass
    @debug("adding ", n)

    n = canonicalize!(G, n)
    if haskey(G.H, n)
        return find(G, G.H[n]) |> EClass
    end
    @debug(n, " not found in H")

    id = push!(G.U) # create new singleton eclass

    for c_id ∈ n.args
        addparent!(G.M[c_id], (n, id))
    end

    G.H[n] = id

    classdata = EClassData(id, [n], [])
    G.M[id] = classdata

    # cache the eclass for the symbol for faster matching
    sym = n.head
    if !haskey(G.symcache, sym)
        G.symcache[sym] = []
    end
    push!(G.symcache[sym], id)

    # make analyses for new enode
    for analysis ∈ G.analyses
        if !islazy(analysis)
            analysis[id] = make(analysis, n)
            modify!(analysis, id)
        end
    end

    return EClass(id)
end

"""
Recursively traverse an [`Expr`](@ref) and insert terms into an
[`EGraph`](@ref). If `e` is not an [`Expr`](@ref), then directly
insert the literal into the [`EGraph`](@ref).
"""
function addexpr!(G::EGraph, e)::EClass
    e = preprocess(e)
    # println("========== $e ===========")
    class_ids = Int64[]

    for child ∈ getargs(e)
        c_eclass = addexpr!(G, child)
        push!(class_ids, c_eclass.id)
    end

    node = ENode(e, class_ids)

    return add!(G, node)
end

addexpr!(G::EGraph, e::EClass)::EClass = e

function clean_enode!(g::EGraph, t::ENode, to::Int64)
    delete!(g.H, t)
    t = canonicalize!(g, t)
    g.H[t] = to
    return t
end

"""
Given an [`EGraph`](@ref) and two e-class ids, set
the two e-classes as equal.
"""
function Base.merge!(G::EGraph, a::Int64, b::Int64)::Int64
    id_a = find(G, a)
    id_b = find(G, b)
    id_a == id_b && return id_a
    id_u = union!(G.U, id_a, id_b)

    @debug "merging" id_a id_b

    from, to = if (id_u == id_a)
        id_b, id_a
    elseif (id_u == id_b)
        id_a, id_b
    else
        error("egraph invariant maintenance error")
    end

    push!(G.dirty, id_u)

    G.M[to] = union!(G.M[to], G.M[from])
    G.M[from] = G.M[to]

    for analysis ∈ G.analyses
        if haskey(analysis, from) && haskey(analysis, to)
            analysis[to] = join(analysis, analysis[from], analysis[to])
            delete!(analysis, from)
        end
    end

    return id_u
end

"""
This function restores invariants and executes
upwards merging in an [`EGraph`](@ref). See
the [egg paper](https://dl.acm.org/doi/pdf/10.1145/3434304)
for more details.
"""
function rebuild!(egraph::EGraph)
    while !isempty(egraph.dirty)
        todo = unique([find(egraph, id) for id ∈ egraph.dirty])
        empty!(egraph.dirty)
        foreach(todo) do x
            repair!(egraph, x)
        end
    end

    for (sym, ids) ∈ egraph.symcache
        egraph.symcache[sym] = unique(ids .|> x -> find(egraph, x))
    end

    if egraph.root != 0
        egraph.root = find(egraph, egraph.root)
    end

    # INVARIANTS ASSERTIONS
    # for (id, c) ∈  egraph.M
    # #     ecdata.nodes = map(n -> canonicalize(egraph.U, n), ecdata.nodes)
    #     for an ∈ egraph.analyses
    #         if haskey(an, id)
    #             @assert an[id] == mapreduce(x -> make(an, x), (x, y) -> join(an, x, y), c.nodes)
    #         end
    #     end
    #
    #     for n ∈ c
    #         # println(n)
    #         # println("canon = ", canonicalize(egraph, n))
    #         @assert egraph.H[canonicalize(egraph, n)] == find(egraph, id)
    #     end
    # end
end

function repair!(G::EGraph, id::Int64)
    id = find(G, id)
    ecdata = G.M[id]
    @debug "repairing " id

    ecdata.parents = map(ecdata.parents) do (p_enode, p_eclass)
        clean_enode!(G, p_enode, find(G, p_eclass))
        (p_enode, p_eclass)
    end

    new_parents = OrderedDict{ENode,Int64}()

    for (p_enode, p_eclass) ∈ ecdata.parents
        p_enode = canonicalize!(G, p_enode)
        # deduplicate parents
        if haskey(new_parents, p_enode)
            @debug "merging classes" p_eclass (new_parents[p_enode])
            merge!(G, p_eclass, new_parents[p_enode])
        end
        new_parents[p_enode] = find(G, p_eclass)
    end
    ecdata.parents = collect(new_parents) .|> Tuple
    @debug "updated parents " id G.parents[id]

    # ecdata.nodes = map(n -> canonicalize(G.U, n), ecdata.nodes)

    # Analysis invariant maintenance
    for an ∈ G.analyses
        haskey(an, id) && modify!(an, id)
        # modify!(an, id)
        # id = find(G, id)
        for (p_enode, p_eclass) ∈ ecdata.parents
            # p_eclass = find(G, p_eclass)
            if !islazy(an) && !haskey(an, p_eclass)
                an[p_eclass] = make(an, p_enode)
            end
            if haskey(an, p_eclass)
                new_data = join(an, an[p_eclass], make(an, p_enode))
                if new_data != an[p_eclass]
                    an[p_eclass] = new_data
                    push!(G.dirty, p_eclass)
                end
            end
        end
    end

    # ecdata.nodes = map(n -> canonicalize(G.U, n), ecdata.nodes)

end

"""
Recursive function that traverses an [`EGraph`](@ref) and
returns a vector of all reachable e-classes from a given e-class id.
"""
function reachable(g::EGraph, id::Int64)
    id = find(g, id)
    hist = Int64[id]
    todo = Int64[id]
    while !isempty(todo)
        curr = find(g, pop!(todo))
        for n ∈ g.M[curr]
            nn = canonicalize(g, n)
            # println("node in reachability is ", n)
            for c_id ∈ nn.args
                if c_id ∉ hist
                    push!(hist, c_id)
                    push!(todo, c_id)
                end
            end
        end
    end

    return hist
end


text:
gyroid(v) = cos(v[1])*sin(v[2])+cos(v[2])*sin(v[3])+cos(v[3])*sin(v[1])
gyroid_shell(v) = max(gyroid(v)-0.4,-gyroid(v)-0.4)
xr,yr,zr = ntuple(_->LinRange(0,pi*4,50),3)
A = [gyroid_shell((x,y,z)) for x in xr, y in yr, z in zr]
A[1,:,:] .= 1e10
A[:,1,:] .= 1e10
A[:,:,1] .= 1e10
A[end,:,:] .= 1e10
A[:,end,:] .= 1e10
A[:,:,end] .= 1e10

volume(A, algorithm=:iso, isovalue=0.5, isorange=0.05)

using FFTW
using LinearAlgebra
using Random
# Fourier synthesis of an isosurface using 1/f^α noise
function one_on_f_noise(T, α, f)
    # 2α needed to convert powers to amplitudes here
    ϵ = sqrt(eps(real(T)))
    A = randn(T)
    return convert(T, A / (ϵ + norm(f))^2α)
end

N = 200
α = 1.2f0
Random.seed!(2)
# symmetric frequency space
fx = range(-1,1,length=N)
fy = reshape(fx,1,:)
fz = reshape(fx,1,1,:)
spectrum = one_on_f_noise.(ComplexF32, α, Vec3f0.(fx, fy, fz))
iso = real.(fft(fftshift(spectrum)))
mini, maxi = extrema(iso)
iso_norm = (iso .- mini) ./ (maxi - mini)
volume(iso_norm, algorithm=:iso, isovalue=0.5, isorange=0.05)


text:
@doc raw"""
    MaternKernel([ν=1 [, θ=1]])

The Matern kernel is a Mercer kernel with parameters `ν > 0` and `ρ > 0`. See the published
documentation for the full definition of the function.

# Examples

```jldoctest; setup = :(using MLKernels)
julia> MaternKernel()
MaternKernel{Float64}(1.0,1.0)

julia> MaternKernel(2.0f0)
MaternKernel{Float32}(2.0,1.0)

julia> MaternKernel(2.0f0, 2.0)
MaternKernel{Float64}(2.0,2.0)
```
"""
struct MaternKernel{T<:AbstractFloat} <: MercerKernel{T}
    ν::T
    ρ::T
    function MaternKernel{T}(ν::Real=T(1), ρ::Real=T(1)) where {T<:AbstractFloat}
        @check_args(MaternKernel, ν, ν > zero(T), "ν > 0")
        @check_args(MaternKernel, ρ, ρ > zero(T), "ρ > 0")
        return new{T}(ν, ρ)
    end
end
function MaternKernel(ν::T₁=1.0, ρ::T₂=T₁(1)) where {T₁<:Real,T₂<:Real}
    MaternKernel{promote_float(T₁,T₂)}(ν,ρ)
end

@inline basefunction(::MaternKernel) = SquaredEuclidean()

@inline function kappa(κ::MaternKernel{T}, d²::T) where {T}
    d = √(d²)
    d = d < eps(T) ? eps(T) : d  # If d is zero, besselk will return NaN
    tmp = √(2κ.ν)*d/κ.ρ
    return (convert(T, 2)^(one(T) - κ.ν))*(tmp^κ.ν)*besselk(κ.ν, tmp)/gamma(κ.ν)
end

function convert(::Type{K}, κ::MaternKernel) where {K>:MaternKernel{T}} where T
    return MaternKernel{T}(κ.ν, κ.ρ)
end


text:
ThreadsX.foreach(
    f,
    xs::AbstractArray;
    basesize::Integer = default_basesize(xs),
    simd::SIMDFlag = Val(false),
) = foreach_array(f, IndexStyle(xs), xs, basesize, verify_simd_flag(simd))

function foreach_array(f, ::IndexLinear, xs, basesize, simd::SIMDValFlag)
    @argcheck basesize >= 1
    # TODO: Switch to `Channel`-based implementation when
    # `length(partition(xs, basesize))` is much larger than
    # `nthreads`?
    @sync for p in _partition(xs, basesize)
        @spawn foreach_linear_seq(f, p, simd::SIMDValFlag)
    end
    return
end

function _simdify_if(simd, expr)
    simd === false && return expr
    simd === true && return :(@simd $expr)
    simd === :ivdep && return :(@simd ivdep $expr)
    throw(ArgumentError("simd = $simd"))
end

for simd in [false, true, :ivdep]
    # Note: Using `for x in xs` is sometimes _much_ slower when
    # `@simd` is not used (e.g., `bench_foreach_seq_double.jl`).  This
    # is probably because `@simd` macro forces indexing-based loop.
    body = :(
        for i in eachindex(xs)
            f(@inbounds xs[i])
        end
    )
    @eval @inline foreach_linear_seq(f, xs, ::Val{$(QuoteNode(simd))}) =
        $(_simdify_if(simd, body))
end

function foreach_array(f, style::IndexCartesian, xs, basesize, simd::SIMDValFlag)
    @argcheck basesize >= 1
    if length(xs) == 0
        return
    elseif length(xs) <= basesize
        foreach_cartesian_seq(f, xs, simd)
        return
    end
    left, right = SplittablesBase.halve(xs)
    @sync begin
        @spawn foreach_array(f, style, right, basesize, simd)
        foreach_array(f, style, left, basesize, simd)
    end
    return
end

# Until https://github.com/JuliaLang/julia/pull/35036 is merged and
# released, implement special `foreach` here.
# TODO: Upstream this to Transducers.jl?
@inline foreach_cartesian_seq(
    f::F,
    xs::AbstractArray,
    simd::SIMDValFlag,
    idx::CartesianIndex = CartesianIndex(),
) where {F} = foreach_cartesian_seq(
    f,
    xs,
    simd,
    idx,
    CartesianIndex(1, idx), # "dummy index" used to capture the last loop
)
# Not using default argument for "dummy index" here to make sure
# @inline works.

@inline function foreach_cartesian_seq(
    f::F,
    xs::AbstractArray{<:Any,N},
    simd::SIMDValFlag,
    idx::CartesianIndex{M},
    ::CartesianIndex,
) where {F,N,M}
    for i in axes(xs, N - M)
        foreach_cartesian_seq(f, xs, simd, CartesianIndex(i, idx))
    end
end

for simd in [false, true, :ivdep]
    body = :(
        for i in axes(xs, 1)
            f(@inbounds xs[CartesianIndex(i, idx)])
        end
    )
    @eval @inline foreach_cartesian_seq(
        f::F,
        xs::AbstractArray{<:Any,N},
        ::Val{$(QuoteNode(simd))},
        idx::CartesianIndex{M},
        ::CartesianIndex{N},  # the "dummy index"
    ) where {F,N,M} = $(_simdify_if(simd, body))
end

ThreadsX.foreach(
    f,
    array::AbstractArray{<:Any,N},
    arrays::AbstractArray{<:Any,N}...;
    kw...,
) where {N} =
    ThreadsX.foreach(eachindex(array, arrays...); kw...) do i
        Base.@_inline_meta
        f((@inbounds array[i]), map(x -> (@inbounds x[i]), arrays)...)
    end

#=
ThreadsX.foreach(f, array::AbstractArray, arrays::AbstractArray; kw...) =
    ThreadsX.foreach(f, map(vec, tuple(array, arrays...))...; kw...)
=#

@inline return_nothing(_...) = nothing

ThreadsX.foreach(f::F, itr; kw...) where {F} = foldxt(
    return_nothing,
    Map(f),
    itr;
    init = nothing,
    basesize = default_basesize(itr),
    kw...,
)

ThreadsX.foreach(f::F, itr, itrs...; kw...) where {F} = foldxt(
    return_nothing,
    MapSplat(f),
    zip(itr, itrs...);
    init = nothing,
    basesize = default_basesize(itr),
    kw...,
)


text:
function rewritequotedparen(s)
    segments = split(s, "\"")
    for i in 1:length(segments)
        if i & 1 == 0  # even i
            ret = replace(segments[i], r"\(", s"_O_PAREN")
            segments[i] = replace(ret, r"\)", s"_C_PAREN")
        end
    end
    join(segments, "\"")
end

function reconsdata(n, s)
    if n > 1
        print(" ")
    end
    if s isa String && ismatch(r"[\$\%\!\$\#]", s) == false
        print("\"$s\"")
    else
        print(s)
    end
end

function printAny(anyarr)
    print("(")
    for (i, el) in enumerate(anyarr)
        if el isa Array
            print("(")
            for (j, el2) in enumerate(el)
                if el2 isa Array
                    print("(")
                    for(k, el3) in enumerate(el2)
                        if el3 isa Array
                            print(" (")
                            for(n, el4) in enumerate(el3)
                                reconsdata(n, el4)
                            end
                            print(")")
                        else
                            reconsdata(k, el3)
                        end
                    end
                    print(")")
                else
                    reconsdata(j, el2)
                end
            end
            if i == 1
                print(")\n ")
            else
                print(")")
            end
        end
    end
    println(")")
end

removewhitespace(s) = replace(replace(s, r"\n", " "), r"^\s*(\S.*\S)\s*$", s"\1")
quote3op(s) = replace(s, r"([\$\!\@\#\%]{3})", s"\"\1\"")
paren2bracket(s) = replace(replace(s, r"\(", s"["), r"\)", s"]")
data2symbol(s) = replace(s, "[data", "[:data")
unrewriteparens(s) = replace(replace(s, "_C_PAREN", ")"), "_O_PAREN", "(")
addcommas(s) = replace(replace(s, r"\]\s*\[", "],["), r" (?![a-z])", ",")

inputstring = """
((data "quoted data" 123 4.5)
 (data (!@# (4.5) "(more" "data)")))
 """

println("The input string is:\n", inputstring)
processed = (inputstring |> removewhitespace |> rewritequotedparen |> quote3op
                        |> paren2bracket |> data2symbol |> unrewriteparens |> addcommas)
nat = eval(parse("""$processed"""))
println("The processed native structure is:\n", nat)
println("The reconstructed string is:\n"), printAny(nat)

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
"""
    struct ElementMatrix{T, TM <: AbstractMatrix{T}} <: AbstractMatrix{T}
        matrix::TM
        mask
        meandiag::T
    end

An element stiffness matrix. `matrix` is the unconstrained element stiffness matrix. `mask` is a `BitVector` where `mask[i]` is 1 iff the local degree of freedom `i` is not constrained by a Dirichlet boundary condition. `meandiag` is the mean of the diagonal of the unconstrained element stiffness matrix.
"""
@params struct ElementMatrix{T,TM<:AbstractMatrix{T}} <: AbstractMatrix{T}
    matrix::TM
    mask::Any
    meandiag::T
end
ElementMatrix(matrix, mask) = ElementMatrix(matrix, mask, sumdiag(matrix) / size(matrix, 1))
function Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ElementMatrix)
    return println("TopOpt element matrix")
end

"""
    rawmatrix(m::ElementMatrix)

Returns the unconstrained element stiffness matrix `m.matrix`.
"""
rawmatrix(m::ElementMatrix) = m.matrix
rawmatrix(m::Symmetric{T,<:ElementMatrix{T}}) where {T} = Symmetric(m.data.matrix)

"""
    bcmatrix(m::ElementMatrix{T, TM}) where {dim, T, TM <: StaticMatrix{dim, dim, T}}

Returns the constrained element stiffness matrix where the elements in the rows and columns corresponding to any local degree of freedom with a Dirichlet boundary condition are replaced by 0.
"""
@generated function bcmatrix(
    m::ElementMatrix{T,TM}
) where {dim,T,TM<:StaticMatrix{dim,dim,T}}
    expr = Expr(:tuple)
    for j in 1:dim, i in 1:dim
        push!(expr.args, :(ifelse(m.mask[$i] && m.mask[$j], m.matrix[$i, $j], zero(T))))
    end
    return :($(Expr(:meta, :inline)); $TM($expr))
end
@generated function bcmatrix(
    m::Symmetric{T,<:ElementMatrix{T,TM}}
) where {dim,T,TM<:StaticMatrix{dim,dim,T}}
    expr = Expr(:tuple)
    for j in 1:dim, i in 1:dim
        push!(
            expr.args,
            :(ifelse(m.data.mask[$i] && m.data.mask[$j], m.data.matrix[$i, $j], zero(T))),
        )
    end
    return :($(Expr(:meta, :inline)); Symmetric($TM($expr)))
end

Base.size(m::ElementMatrix) = size(m.matrix)
Base.getindex(m::ElementMatrix, i...) = m.matrix[i...]

"""
    convert(::Type{Vector{<:ElementMatrix}}, Kes::Vector{<:AbstractMatrix})

Converts the element stiffness matrices `Kes` from an abstract vector of matrices to a vector of instances of the type `ElementMatrix`.
"""
function Base.convert(
    ::Type{Vector{<:ElementMatrix}}, Kes::Vector{TM}; bc_dofs, dof_cells
) where {N,T,TM<:StaticMatrix{N,N,T}}
    fill_matrix = zero(TM)
    fill_mask = ones(SVector{N,Bool})
    element_Kes = fill(ElementMatrix(fill_matrix, fill_mask), length(Kes))
    for i in bc_dofs
        d_cells = dof_cells[i]
        for c in d_cells
            (cellid, localdof) = c
            Ke = element_Kes[cellid]
            new_Ke = @set Ke.mask[localdof] = false
            element_Kes[cellid] = Symmetric(new_Ke)
        end
    end
    for e in 1:length(element_Kes)
        Ke = element_Kes[e]
        matrix = Kes[e]
        Ke = @set Ke.matrix = matrix
        element_Kes[e] = @set Ke.meandiag = sumdiag(Ke.matrix)
    end
    return element_Kes
end
function Base.convert(
    ::Type{Vector{<:ElementMatrix}}, Kes::Vector{Symmetric{T,TM}}; bc_dofs, dof_cells
) where {N,T,TM<:StaticMatrix{N,N,T}}
    fill_matrix = zero(TM)
    fill_mask = ones(SVector{N,Bool})
    element_Kes = fill(Symmetric(ElementMatrix(fill_matrix, fill_mask)), length(Kes))
    for i in bc_dofs
        d_cells = dof_cells[i]
        for c in d_cells
            (cellid, localdof) = c
            Ke = element_Kes[cellid].data
            new_Ke = @set Ke.mask[localdof] = false
            element_Kes[cellid] = Symmetric(new_Ke)
        end
    end
    for e in 1:length(element_Kes)
        Ke = element_Kes[e].data
        matrix = Kes[e].data
        Ke = @set Ke.matrix = matrix
        element_Kes[e] = Symmetric(@set Ke.meandiag = sumdiag(Ke.matrix))
    end
    return element_Kes
end
function Base.convert(
    ::Type{Vector{<:ElementMatrix}}, Kes::Vector{TM}; bc_dofs, dof_cells
) where {T,TM<:AbstractMatrix{T}}
    N = size(Kes[1], 1)
    fill_matrix = zero(TM)
    fill_mask = ones(Bool, N)
    element_Kes = [deepcopy(ElementMatrix(fill_matrix, fill_mask)) for i in 1:length(Kes)]
    for i in bc_dofs
        d_cells = dof_cells[i]
        for c in d_cells
            (cellid, localdof) = c
            Ke = element_Kes[cellid]
            Ke.mask[localdof] = false
        end
    end
    return element_Kes
end
function Base.convert(
    ::Type{Vector{<:ElementMatrix}}, Kes::Vector{Symmetric{T,TM}}; bc_dofs, dof_cells
) where {T,TM<:AbstractMatrix{T}}
    N = size(Kes[1], 1)
    fill_matrix = zero(TM)
    fill_mask = ones(Bool, N)
    element_Kes = [
        Symmetric(deepcopy(ElementMatrix(fill_matrix, fill_mask))) for i in 1:length(Kes)
    ]
    for i in bc_dofs
        d_cells = dof_cells[i]
        for c in d_cells
            (cellid, localdof) = c
            Ke = element_Kes[cellid].data
            Ke.mask[localdof] = false
        end
    end
    return element_Kes
end

for TM in (:(StaticMatrix{m,m,T}), :(Symmetric{T,<:StaticMatrix{m,m,T}}))
    @eval begin
        @generated function sumdiag(K::$TM) where {m,T}
            return reduce((ex1, ex2) -> :($ex1 + $ex2), [:(K[$j, $j]) for j in 1:m])
        end
    end
end
@doc """
sumdiag(K::Union{StaticMatrix, Symmetric{<:Any, <:StaticMatrix}})

Computes the sum of the diagonal of the static matrix `K`.
""" sumdiag


text:
module TechnicalSignals

test_me() = "TechnicalSignals"

end # module