text:
function parse_heat!(dtr::DieterModel, df::DataFrame)
# function parse_heat!(dtr::DieterModel, path::AbstractString)
    # df = CSV.read(path)
    dtr.sets[:BuildingType] = disallowmissing(unique(df[!,:BuildingType]))
    dtr.sets[:HeatingType] = disallowmissing(unique(df[!,:HeatingType]))

    params = map_idcol(df, id_cols=2)
    merge!(dtr.parameters, params)

    return nothing
end

function parse_heat_technologies!(dtr::DieterModel, df::DataFrame)
# function parse_heat_technologies!(dtr::DieterModel, path::AbstractString)
    # df = CSV.read(path)
    params = map_idcol(df)
    merge!(dtr.parameters, params)

    return nothing
end

function parse_buildings!(dtr::DieterModel, df::DataFrame)
# function parse_buildings!(dtr::DieterModel, path::AbstractString)
    # df = CSV.read(path)

    params = map_idcol(df)
    for (k,v) in params update_dict!(dtr.parameters, k, v) end

    return nothing
end

function parse_temperature!(dtr::DieterModel, df::DataFrame)
# function parse_temperature!(dtr::DieterModel, path::AbstractString)
    # params = CSV.read(path) |> map_dfheader_to_col
    params = map_dfheader_to_col(df)
    update_dict!(dtr.parameters, :Temperature, params)

    return nothing
end

function parse_heat_demand!(dtr::DieterModel, df::DataFrame)
# function parse_heat_demand!(dtr::DieterModel, path::AbstractString)
    # params = CSV.read(path) |> map_dfheader_to_col
    params = map_dfheader_to_col(df)
    update_dict!(dtr.parameters, :HeatDemand, params)

    return nothing
end

function parse_dhw_demand!(dtr::DieterModel, df::DataFrame)
# function parse_dhw_demand!(dtr::DieterModel, path::AbstractString)
    # params = CSV.read(path) |> map_dfheader_to_col
    params = map_dfheader_to_col(df)
    update_dict!(dtr.parameters, :HotwaterDemand, params)

    return nothing
end

function calc_hp_cop!(dtr::DieterModel)

    cop = Dict{Tuple{String, String}, Array{Float64,1}}()

    for bu in dtr.sets[:BuildingType], hp in dtr.sets[:HeatingType]

        if dtr.parameters[:Outside][hp] == 1
            temp_diff = dtr.parameters[:TemperatureSink][bu] .- dtr.parameters[:Temperature][hp]
        else
            temp_diff = fill(dtr.parameters[:TemperatureSink][bu] - dtr.parameters[:TemperatureSource][bu], 8760)
        end

        delta = (dtr.parameters[:TemperatureSink][bu] + 273.15) ./ temp_diff
        cop[bu,hp] = dtr.parameters[:DynamicEfficiency][hp] .* delta
    end

    dtr.parameters[:CoP] = cop

    return nothing
end

function calc_heat_demand!(dtr::DieterModel)
# function calc_heat_demand!(dtr::DieterModel, heat::Number)
    heat = dtr.settings[:heat]
    heat_demand = Dict{Tuple{String, String}, Array{Float64,1}}()
    total_share = heat/100

    for bu in dtr.sets[:BuildingType], hp in dtr.sets[:HeatingType]
        hs = dtr.parameters[:HeatShare][bu,hp]
        af = dtr.parameters[:AreaFloor][bu]
        d = dtr.parameters[:HeatDemand][bu] + dtr.parameters[:HotwaterDemand][bu]

        heat_demand[bu,hp] = total_share * (hs * (af .* d))
    end

    dtr.parameters[:HeatConsumption] = heat_demand

    return nothing
end


text:
# Using Convex.jl to test if the probability distributions in Homework problem 2
# Sets up the optimization problem for the Cut Inflation for the triangle causal
# model with the distribution:
#
# P(a, b, c; t) = if (a == b == c) => t/2
#                 else             => (1 - t)/6
#
# the optimal value of t is found through repetition

using Convex
using GLPK
using Inflation
using Printf

struct InflationLoopData
	all_feasible::Bool
	all_infeasible::Bool
	tmin::Float64
	tmax::Float64
	Ndiv::Int64
end

function check_inflation_t(infl_strings::Vector{String}, M::Array{Int64, 2}, idata::InflationLoopData)::InflationLoopData
	# checks if an inflation is feasible for pfunct, for certain values of t
	tmin = idata.tmin
	tmax = idata.tmax
	Ndiv = idata.Ndiv
	dt   = (tmax - tmin)/(Ndiv - 1)
	for (i, t) in enumerate(range(tmin, tmax, length = Ndiv))
		# avoid repeatedly testing the 1st element
		if i == 1 && t != 0.0
			continue
		end

		# get pfunc(a, b, c; t) and get the associated b-array
		pfunct = get_pfunct(t)
		b      = get_b_array(infl_strings, pfunct, cut)

		# the variables that get optimized by Convex.jl
		v = get_v_array_Variable(infl_strings)

		# set up the objective and constraints
		objective   = 0
		constraints = [v >= 0, M*v == b]

		# create the problem and solve
		problem = Convex.maximize(objective, constraints)
		solve!(problem, GLPK.Optimizer, verbose=false)

		# return the results
		if problem.status == Convex.MathOptInterface.INFEASIBLE
			@printf("Checked t = %0.6f : solution is infeasible.\n", t)
			if (i == 1) # all infeasible
				return InflationLoopData(false, true, tmin, tmax, Ndiv)
			else
				new_tmin = t - (tmax - tmin)/(Ndiv - 1)
				new_tmax = t
				return InflationLoopData(false, false, new_tmin, new_tmax, Ndiv)
			end
		else
			@printf("Checked t = %0.6f : solution is feasible.\n", t)
		end
	end

	return InflationLoopData(true, false, tmin, tmax, Ndiv)
end

# the probability matrix is set up as
# P_{A2,B1} = sum_{C1}P_{A2,B1,C1}   --> 'yyx'  (3rd term is integrated)
# P_{A2,C1} = sum_{B1}P_{A2,B1,C1}   --> 'yxy'  (2nd term is integrated)
# P_{B1,C1} = sum_{A2}P_{A2,B1,C1}   --> 'xyy'  (1st term is integrated)
infl_strings = ["yyx", "yxy", "xyy"]

# creating the M-matrix to be used in the Convex.jl optimization
M = get_M_matrix(infl_strings)

tmin = 0.0  # smallest allowed value of t
tmax = 1.0  # greatest allowed value of t
Ndiv = 11   # number of elements to divide the range into
idata = InflationLoopData(false, false, tmin, tmax, Ndiv)

# check to 5 decimal places
for i in 1:6
	global idata
	idata = check_inflation_t(infl_strings, M, idata)
	if idata.all_feasible
		@printf("All values of t between %0.6f and %0.6f are feasible.\n", tmin, idata.tmax)
		break
	elseif idata.all_infeasible
		@printf("Unable to find further feasible values.\n")
		break
	else
		idata = InflationLoopData(false, false, idata.tmin, idata.tmax, idata.Ndiv)
	end
end

@printf("All values of t between %0.6f and %0.6f are feasible.\n", tmin, idata.tmin)

ext: jl


text:
using Test
using DFTK
using DFTK: index_G_vectors
using LinearAlgebra

include("testcases.jl")

function test_pw_cutoffs(testcase, Ecut, fft_size)
    model = Model(testcase.lattice; testcase.n_electrons)
    basis = PlaneWaveBasis(model; Ecut, fft_size, kgrid=(2, 5, 5), kshift=[1, 0, 0]/2)

    for (ik, kpt) in enumerate(basis.kpoints)
        for G in G_vectors(basis, kpt)
            @test sum(abs2, model.recip_lattice * (kpt.coordinate + G)) ≤ 2 * Ecut
        end
    end
end

@testset "PlaneWaveBasis: Check struct construction" begin
    Ecut = 3
    fft_size = [15, 15, 15]
    model = Model(silicon.lattice, silicon.atoms, silicon.positions)
    basis = PlaneWaveBasis(model, Ecut, silicon.kcoords, silicon.kweights; fft_size)

    @test basis.model.lattice == silicon.lattice
    @test basis.model.recip_lattice ≈ 2π * inv(silicon.lattice)
    @test basis.model.unit_cell_volume ≈ det(silicon.lattice)
    @test basis.model.recip_cell_volume ≈ (2π)^3 * det(inv(silicon.lattice))

    @test basis.Ecut == 3
    @test basis.fft_size == Tuple(fft_size)

    g_start = -ceil.(Int, (Vec3(basis.fft_size) .- 1) ./ 2)
    g_stop  = floor.(Int, (Vec3(basis.fft_size) .- 1) ./ 2)
    g_all = vec(collect(G_vectors(basis)))

    for (ik, kpt) in enumerate(basis.kpoints)
        kpt = basis.kpoints[ik]
        @test kpt.coordinate == silicon.kcoords[basis.krange_thisproc[ik]]

        for (ig, G) in enumerate(G_vectors(basis, kpt))
            @test g_start <= G <= g_stop
        end
        @test g_all[kpt.mapping] == G_vectors(basis, kpt)
    end
    @test basis.kweights == ([1, 8, 6, 12] / 27)[basis.krange_thisproc]
end

@testset "PlaneWaveBasis: Energy cutoff is respected" begin
    test_pw_cutoffs(silicon, 4.0, [15, 15, 15])
    test_pw_cutoffs(silicon, 3.0, [15, 13, 13])
    test_pw_cutoffs(silicon, 4.0, [11, 13, 11])
end

@testset "PlaneWaveBasis: Check cubic basis and cubic index" begin
    model = Model(silicon.lattice; silicon.n_electrons)
    basis = PlaneWaveBasis(model; Ecut=3, fft_size=(15, 15, 15), kgrid=(1, 1, 1))
    g_all = collect(G_vectors(basis))

    for i in 1:15, j in 1:15, k in 1:15
        @test index_G_vectors(basis, g_all[i, j, k]) == CartesianIndex(i, j, k)
    end
    @test index_G_vectors(basis, [15, 1, 1]) === nothing
    @test index_G_vectors(basis, [-15, 1, 1]) === nothing
end

@testset "PlaneWaveBasis: Check index for kpoints" begin
    Ecut = 3
    fft_size = [7, 9, 11]
    model = Model(silicon.lattice, silicon.atoms, silicon.positions)
    basis = PlaneWaveBasis(model, Ecut, silicon.kcoords, silicon.kweights; fft_size)
    g_all = collect(G_vectors(basis))

    for kpt in basis.kpoints
        for (iball, ifull) in enumerate(kpt.mapping)
            @test index_G_vectors(basis, kpt, g_all[ifull]) == iball
        end

        if kpt.coordinate == [1/3, 1/3, 0]
            @test index_G_vectors(basis, kpt, [-2, -3, -1]) == 62
        else
            @test index_G_vectors(basis, kpt, [-2, -3, -1]) === nothing
        end
        @test index_G_vectors(basis, kpt, [15, 1, 1]) === nothing
        @test index_G_vectors(basis, kpt, [-15, 1, 1]) === nothing
    end
end

@testset "PlaneWaveBasis: kpoint mapping" begin
    model = Model(silicon.lattice, silicon.atoms, silicon.positions)
    basis = PlaneWaveBasis(model; Ecut=3, kgrid=(2, 2, 2), fft_size=[7, 9, 11],
                           kshift=ones(3)/2)

    for kpt in basis.kpoints
        Gs_basis = collect(G_vectors(basis))
        Gs_kpt   = collect(G_vectors(basis, kpt))
        for i in 1:length(kpt.mapping)
            @test Gs_basis[kpt.mapping[i]] == Gs_kpt[i]
        end
        for i in keys(kpt.mapping_inv)
            @test Gs_basis[i] == Gs_kpt[kpt.mapping_inv[i]]
        end
    end
end

@testset "PlaneWaveBasis: Check G_vector-like accessor functions" begin
    Ecut = 3
    fft_size = [15, 15, 15]
    model = Model(silicon.lattice, silicon.atoms, silicon.positions)
    basis = PlaneWaveBasis(model, Ecut, silicon.kcoords, silicon.kweights; fft_size)

    @test length(G_vectors(fft_size)) == prod(fft_size)
    @test length(r_vectors(basis))    == prod(fft_size)

    @test all(G_vectors(basis)      .== G_vectors(fft_size))
    @test all(G_vectors_cart(basis) .== map(G -> model.recip_lattice * G,
                                            G_vectors(fft_size)))
    @test all(r_vectors_cart(basis) .== map(r -> model.lattice * r,
                                            r_vectors(basis)))

    for kpt in basis.kpoints
        @test length(G_vectors(basis, kpt)) == length(kpt.mapping)
        @test all(G_vectors_cart(basis, kpt) .== map(G -> model.recip_lattice * G,
                                                     G_vectors(basis, kpt)))

        @test all(Gplusk_vectors(basis, kpt) .== map(G -> G + kpt.coordinate,
                                                     G_vectors(basis, kpt)))
        @test all(Gplusk_vectors_cart(basis, kpt) .== map(q -> model.recip_lattice * q,
                                                          Gplusk_vectors(basis, kpt)))
    end
end


text:
z = katz_centrality(g5, 0.4)
@test round(z, 2) == [0.32, 0.44, 0.62, 0.56]


text:
baremodule EscapeAnalysis

export
    analyze_escapes,
    cache_escapes!,
    has_no_escape,
    has_return_escape,
    has_thrown_escape,
    has_all_escape,
    is_load_forwardable,
    is_sroa_eligible,
    can_elide_finalizer

# analysis
# ========

const _TOP_MOD = ccall(:jl_base_relative_to, Any, (Any,), EscapeAnalysis)::Module

# imports
import ._TOP_MOD: ==, getindex, setindex!
# usings
import Core:
    MethodInstance, Const, Argument, SSAValue, PiNode, PhiNode, UpsilonNode, PhiCNode,
    ReturnNode, GotoNode, GotoIfNot, SimpleVector, sizeof, ifelse, arrayset, arrayref,
    arraysize
import ._TOP_MOD:     # Base definitions
    @__MODULE__, @eval, @assert, @nospecialize, @inbounds, @inline, @noinline, @label, @goto,
    !, !==, !=, ≠, +, -, ≤, <, ≥, >, &, |, include, error, missing, copy,
    Vector, BitSet, IdDict, IdSet, UnitRange, ∪, ⊆, ∩, :, ∈, ∉, in, length, get, first, last,
    isempty, isassigned, pop!, push!, pushfirst!, empty!, max, min, Csize_t
import Core.Compiler: # Core.Compiler specific definitions
    isbitstype, isexpr, is_meta_expr_head, println,
    IRCode, IR_FLAG_EFFECT_FREE, widenconst, argextype, singleton_type, fieldcount_noerror,
    try_compute_field, try_compute_fieldidx, hasintersect, ⊑ as ⊑ₜ, intrinsic_nothrow,
    array_builtin_common_typecheck, arrayset_typecheck, setfield!_nothrow, compute_trycatch

if _TOP_MOD !== Core.Compiler
    include(@__MODULE__, "disjoint_set.jl")
else
    include(@__MODULE__, "compiler/ssair/EscapeAnalysis/disjoint_set.jl")
end

# XXX better to be IdSet{Int}?
const FieldEscape = BitSet
const FieldEscapes = Vector{BitSet}
const ArrayEscapes = IdSet{Int}

"""
    x::EscapeLattice

A lattice for escape information, which holds the following properties:
- `x.Analyzed::Bool`: not formally part of the lattice, only indicates `x` has not been analyzed or not
- `x.ReturnEscape::BitSet`: records SSA statements where `x` can escape to the caller via return
  where `0 ∈ x.ReturnEscape` has the special meaning that it's visible to the caller
  simply because it's passed as call argument
- `x.ThrownEscape::BitSet`: records SSA statements where `x` can be thrown as exception:
  this information will be used by `escape_exception!` to propagate potential escapes via exception
- `x.AliasEscapes::Union{FieldEscapes,ArrayEscapes,Bool}`: maintains all possible values
  that can be aliased to fields or array elements of `x`:
  * `x.AliasEscapes === false` indicates the fields/elements of `x` isn't analyzed yet
  * `x.AliasEscapes === true` indicates the fields/elements of `x` can't be analyzed,
    e.g. the type of `x` is not known or is not concrete and thus its fields/elements
    can't be known precisely
  * `x.AliasEscapes::FieldEscapes` records all the possible values that can be aliased fields of object `x`,
  * `x.AliasEscapes::ArrayEscapes` records all the possible values that be aliased to elements of array `x`
- `x.ArgEscape::Int` (not implemented yet): indicates it will escape to the caller through
  `setfield!` on argument(s)
  * `-1` : no escape
  * `0` : unknown or multiple
  * `n` : through argument N

There are utility constructors to create common `EscapeLattice`s, e.g.,
- `NoEscape()`: the bottom(-like) element of this lattice, meaning it won't escape to anywhere
- `AllEscape()`: the topmost element of this lattice, meaning it will escape to everywhere

`analyze_escapes` will transition these elements from the bottom to the top,
in the same direction as Julia's native type inference routine.
An abstract state will be initialized with the bottom(-like) elements:
- the call arguments are initialized as `ArgumentReturnEscape()`, because they're visible from a caller immediately
- the other states are initialized as `NotAnalyzed()`, which is a special lattice element that
  is slightly lower than `NoEscape`, but at the same time doesn't represent any meaning
  other than it's not analyzed yet (thus it's not formally part of the lattice).
"""
struct EscapeLattice
    Analyzed::Bool
    ReturnEscape::BitSet
    ThrownEscape::BitSet
    AliasEscapes #::Union{FieldEscapes,ArrayEscapes,Bool}
    # TODO: ArgEscape::Int

    function EscapeLattice(
        Analyzed::Bool,
        ReturnEscape::BitSet,
        ThrownEscape::BitSet,
        AliasEscapes#=::Union{FieldEscapes,ArrayEscapes,Bool}=#,
        )
        @nospecialize AliasEscapes
        return new(
            Analyzed,
            ReturnEscape,
            ThrownEscape,
            AliasEscapes,
            )
    end
    function EscapeLattice(
        x::EscapeLattice,
        # non-concrete fields should be passed as default arguments
        # in order to avoid allocating non-concrete `NamedTuple`s
        AliasEscapes#=::Union{FieldEscapes,ArrayEscapes,Bool}=# = x.AliasEscapes;
        Analyzed::Bool = x.Analyzed,
        ReturnEscape::BitSet = x.ReturnEscape,
        ThrownEscape::BitSet = x.ThrownEscape,
        )
        @nospecialize AliasEscapes
        return new(
            Analyzed,
            ReturnEscape,
            ThrownEscape,
            AliasEscapes,
            )
    end
end

# precomputed default values in order to eliminate computations at each callsite
const BOT_RETURN_ESCAPE = BitSet()
const ARG_RETURN_ESCAPE = BitSet(0)
const TOP_RETURN_ESCAPE = BitSet(0:100_000)

const BOT_THROWN_ESCAPE = BitSet()
const TOP_THROWN_ESCAPE = BitSet(0:100_000)

const BOT_ALIAS_ESCAPES = false
const TOP_ALIAS_ESCAPES = true

# the constructors
NotAnalyzed() = EscapeLattice(false, BOT_RETURN_ESCAPE, BOT_THROWN_ESCAPE, BOT_ALIAS_ESCAPES) # not formally part of the lattice
NoEscape() = EscapeLattice(true, BOT_RETURN_ESCAPE, BOT_THROWN_ESCAPE, BOT_ALIAS_ESCAPES)
ReturnEscape(pc::Int) = EscapeLattice(true, BitSet(pc), BOT_THROWN_ESCAPE, BOT_ALIAS_ESCAPES)
ArgumentReturnEscape() = EscapeLattice(true, ARG_RETURN_ESCAPE, BOT_THROWN_ESCAPE, TOP_ALIAS_ESCAPES) # TODO allow interprocedural field analysis?
AllReturnEscape() = EscapeLattice(true, TOP_RETURN_ESCAPE, BOT_THROWN_ESCAPE, BOT_ALIAS_ESCAPES)
ThrownEscape(pc::Int) = EscapeLattice(true, BOT_RETURN_ESCAPE, BitSet(pc), BOT_ALIAS_ESCAPES)
ThrownEscape(pcs::BitSet) = EscapeLattice(true, BOT_RETURN_ESCAPE, pcs, BOT_ALIAS_ESCAPES)
AllEscape() = EscapeLattice(true, TOP_RETURN_ESCAPE, TOP_THROWN_ESCAPE, TOP_ALIAS_ESCAPES)

const ⊥, ⊤ = NotAnalyzed(), AllEscape()

# Convenience names for some ⊑ queries
has_no_escape(x::EscapeLattice) = ignore_aliasescapes(x) ⊑ NoEscape()
has_return_escape(x::EscapeLattice) = !isempty(x.ReturnEscape)
has_return_escape(x::EscapeLattice, pc::Int) = pc in x.ReturnEscape
has_thrown_escape(x::EscapeLattice) = !isempty(x.ThrownEscape)
has_thrown_escape(x::EscapeLattice, pc::Int) = pc in x.ThrownEscape
has_all_escape(x::EscapeLattice) = ⊤ ⊑ x

# utility lattice constructors
ignore_thrownescapes(x::EscapeLattice) = EscapeLattice(x; ThrownEscape=BOT_THROWN_ESCAPE)
ignore_aliasescapes(x::EscapeLattice) = EscapeLattice(x, BOT_ALIAS_ESCAPES)

"""
    is_load_forwardable(x::EscapeLattice) -> Bool

Queries if `x` is elibigle for store-to-load forwarding optimization.
"""
function is_load_forwardable(x::EscapeLattice)
    if x.AliasEscapes === false || # allows this query to work for immutables since we don't impose escape on them
       isa(x.AliasEscapes, FieldEscapes)
        # NOTE technically we also need to check `!has_thrown_escape(x)` here as well,
        # but we can also do equivalent check during forwarding
        return true
    end
    return false
end

"""
    is_sroa_eligible(x::EscapeLattice) -> Bool

Queries allocation eliminability by SROA.
"""
is_sroa_eligible(x::EscapeLattice) = is_load_forwardable(x) && !has_return_escape(x)

"""
    can_elide_finalizer(x::EscapeLattice, pc::Int) -> Bool

Queries the validity of the finalizer elision optimization at the return site of SSA statement `pc`,
which inserts `finalize` call when the lifetime of interested object ends.
Note that we don't need to take `x.ThrownEscape` into account because it would have never
been thrown when the program execution reaches the `return` site.
"""
can_elide_finalizer(x::EscapeLattice, pc::Int) =
    !(has_return_escape(x, 0) || has_return_escape(x, pc))

# we need to make sure this `==` operator corresponds to lattice equality rather than object equality,
# otherwise `propagate_changes` can't detect the convergence
x::EscapeLattice == y::EscapeLattice = begin
    # fast pass: better to avoid top comparison
    x === y && return true
    xr, yr = x.ReturnEscape, y.ReturnEscape
    if xr === TOP_RETURN_ESCAPE
        yr === TOP_RETURN_ESCAPE || return false
    elseif yr === TOP_RETURN_ESCAPE
        return false # x.ReturnEscape === TOP_RETURN_ESCAPE
    else
        xr == yr || return false
    end
    xt, yt = x.ThrownEscape, y.ThrownEscape
    if xt === TOP_THROWN_ESCAPE
        yt === TOP_THROWN_ESCAPE || return false
    elseif yt === TOP_THROWN_ESCAPE
        return false # x.ThrownEscape === TOP_THROWN_ESCAPE
    else
        xt == yt || return false
    end
    xf, yf = x.AliasEscapes, y.AliasEscapes
    if isa(xf, Bool)
        xf === yf || return false
    elseif isa(xf, FieldEscapes)
        isa(yf, FieldEscapes) || return false
        xf == yf || return false
    else
        xf = xf::ArrayEscapes
        isa(yf, ArrayEscapes) || return false
        xf == yf || return false
    end
    return x.Analyzed === y.Analyzed
end

"""
    x::EscapeLattice ⊑ y::EscapeLattice -> Bool

The non-strict partial order over `EscapeLattice`.
"""
x::EscapeLattice ⊑ y::EscapeLattice = begin
    # fast pass: better to avoid top comparison
    if y === ⊤
        return true
    elseif x === ⊤
        return false # return y === ⊤
    elseif x === ⊥
        return true
    elseif y === ⊥
        return false # return x === ⊥
    end
    xr, yr = x.ReturnEscape, y.ReturnEscape
    if xr === TOP_RETURN_ESCAPE
        yr !== TOP_RETURN_ESCAPE && return false
    elseif yr !== TOP_RETURN_ESCAPE
        xr ⊆ yr || return false
    end
    xt, yt = x.ThrownEscape, y.ThrownEscape
    if xt === TOP_THROWN_ESCAPE
        yt !== TOP_THROWN_ESCAPE && return false
    elseif yt !== TOP_THROWN_ESCAPE
        xt ⊆ yt || return false
    end
    xf, yf = x.AliasEscapes, y.AliasEscapes
    if isa(xf, Bool)
        xf && yf !== true && return false
    elseif isa(xf, FieldEscapes)
        if isa(yf, FieldEscapes)
            xn, yn = length(xf), length(yf)
            xn > yn && return false
            for i in 1:xn
                xf[i] ⊆ yf[i] || return false
            end
        else
            yf === true || return false
        end
    else
        xf = xf::ArrayEscapes
        if isa(yf, ArrayEscapes)
            xf ⊆ yf || return false
        else
            yf === true || return false
        end
    end
    return x.Analyzed ≤ y.Analyzed
end

"""
    x::EscapeLattice ⊏ y::EscapeLattice -> Bool

The strict partial order over `EscapeLattice`.
This is defined as the irreflexive kernel of `⊏`.
"""
x::EscapeLattice ⊏ y::EscapeLattice = x ⊑ y && !(y ⊑ x)

"""
    x::EscapeLattice ⋤ y::EscapeLattice -> Bool

This order could be used as a slightly more efficient version of the strict order `⊏`,
where we can safely assume `x ⊑ y` holds.
"""
x::EscapeLattice ⋤ y::EscapeLattice = !(y ⊑ x)

"""
    x::EscapeLattice ⊔ y::EscapeLattice -> EscapeLattice

Computes the join of `x` and `y` in the partial order defined by `EscapeLattice`.
"""
x::EscapeLattice ⊔ y::EscapeLattice = begin
    # fast pass: better to avoid top join
    if x === ⊤ || y === ⊤
        return ⊤
    elseif x === ⊥
        return y
    elseif y === ⊥
        return x
    end
    xr, yr = x.ReturnEscape, y.ReturnEscape
    if xr === TOP_RETURN_ESCAPE || yr === TOP_RETURN_ESCAPE
        ReturnEscape = TOP_RETURN_ESCAPE
    elseif xr === BOT_RETURN_ESCAPE
        ReturnEscape = yr
    elseif yr === BOT_RETURN_ESCAPE
        ReturnEscape = xr
    else
        ReturnEscape = xr ∪ yr
    end
    xt, yt = x.ThrownEscape, y.ThrownEscape
    if xt === TOP_THROWN_ESCAPE || yt === TOP_THROWN_ESCAPE
        ThrownEscape = TOP_THROWN_ESCAPE
    elseif xt === BOT_THROWN_ESCAPE
        ThrownEscape = yt
    elseif yt === BOT_THROWN_ESCAPE
        ThrownEscape = xt
    else
        ThrownEscape = xt ∪ yt
    end
    xf, yf = x.AliasEscapes, y.AliasEscapes
    if xf === true || yf === true
        AliasEscapes = true
    elseif xf === false
        AliasEscapes = yf
    elseif yf === false
        AliasEscapes = xf
    elseif isa(xf, FieldEscapes)
        if isa(yf, FieldEscapes)
            xn, yn = length(xf), length(yf)
            nmax, nmin = max(xn, yn), min(xn, yn)
            AliasEscapes = Vector{FieldEscape}(undef, nmax)
            for i in 1:nmax
                if i > nmin
                    AliasEscapes[i] = (xn > yn ? xf : yf)[i]
                else
                    AliasEscapes[i] = xf[i] ∪ yf[i]
                end
            end
        else
            AliasEscapes = true # handle conflicting case conservatively
        end
    else
        xf = xf::ArrayEscapes
        if isa(yf, ArrayEscapes)
            AliasEscapes = xf ∪ yf
        else
            AliasEscapes = true # handle conflicting case conservatively
        end
    end
    return EscapeLattice(
        x.Analyzed | y.Analyzed,
        ReturnEscape,
        ThrownEscape,
        AliasEscapes,
        )
end

# TODO setup a more effient struct for cache
# which can discard escape information on SSS values and arguments that don't join dispatch signature

const AliasSet = IntDisjointSet{Int}

"""
    estate::EscapeState

Extended lattice that maps arguments and SSA values to escape information represented as `EscapeLattice`.
Escape information imposed on SSA IR element `x` can be retrieved by `estate[x]`.
"""
struct EscapeState
    escapes::Vector{EscapeLattice}
    aliasset::AliasSet
    nargs::Int
end
function EscapeState(nargs::Int, nstmts::Int)
    escapes = EscapeLattice[
        1 ≤ i ≤ nargs ? ArgumentReturnEscape() : ⊥ for i in 1:(nargs+nstmts)]
    aliaset = AliasSet(nargs+nstmts)
    return EscapeState(escapes, aliaset, nargs)
end
function getindex(estate::EscapeState, @nospecialize(x))
    if isa(x, Argument) || isa(x, SSAValue)
        return estate.escapes[iridx(x, estate)]
    else
        return nothing
    end
end
function setindex!(estate::EscapeState, v::EscapeLattice, @nospecialize(x))
    if isa(x, Argument) || isa(x, SSAValue)
        estate.escapes[iridx(x, estate)] = v
    end
    return estate
end

"""
    iridx(x, estate::EscapeState) -> xidx::Union{Int,Nothing}

Tries to convert analyzable IR element `x::Union{Argument,SSAValue}` to
its unique identifier number `xidx` that is valid in the analysis context of `estate`.
Returns `nothing` if `x` isn't maintained by `estate` and thus unanalyzable (e.g. `x::GlobalRef`).

`irval` is the inverse function of `iridx` (not formally), i.e.
`irval(iridx(x::Union{Argument,SSAValue}, state), state) === x`.
"""
function iridx(@nospecialize(x), estate::EscapeState)
    if isa(x, Argument)
        xidx = x.n
        @assert 1 ≤ xidx ≤ estate.nargs "invalid Argument"
    elseif isa(x, SSAValue)
        xidx = x.id + estate.nargs
    else
        return nothing
    end
    return xidx
end

"""
    irval(xidx::Int, estate::EscapeState) -> x::Union{Argument,SSAValue}

Converts its unique identifier number `xidx` to the original IR element `x::Union{Argument,SSAValue}`
that is analyzable in the context of `estate`.

`iridx` is the inverse function of `irval` (not formally), i.e.
`iridx(irval(xidx, state), state) === xidx`.
"""
function irval(xidx::Int, estate::EscapeState)
    x = xidx > estate.nargs ? SSAValue(xidx-estate.nargs) : Argument(xidx)
    return x
end

function getaliases(xidx::Int, estate::EscapeState)
    aliasset = estate.aliasset
    root = find_root!(aliasset, xidx)
    if xidx ≠ root || aliasset.ranks[xidx] > 0
        # the size of this alias set containing `key` is larger than 1,
        # collect the entire alias set
        aliases = Int[]
        for aidx in 1:length(aliasset.parents)
            if aliasset.parents[aidx] == root
                push!(aliases, aidx)
            end
        end
        return aliases
    else
        return nothing
    end
end

"""
    EscapeLatticeCache(x::EscapeLattice) -> x′::EscapeLatticeCache

The data structure for caching `x::EscapeLattice` for interprocedural propagation,
which is slightly more efficient than the original `x` object.
"""
struct EscapeLatticeCache
    AllEscape::Bool
    ReturnEscape::Bool
    ThrownEscape::Bool
    function EscapeLatticeCache(x::EscapeLattice)
        x === ⊤ && return new(true, true, true)
        ReturnEscape = x.ReturnEscape === ARG_RETURN_ESCAPE ? false : true
        ThrownEscape = isempty(x.ThrownEscape)              ? false : true
        return new(false, ReturnEscape, ThrownEscape)
    end
end

"""
    cache_escapes!(linfo::MethodInstance, estate::EscapeState, _::IRCode)

Transforms escape information of `estate` for interprocedural propagation,
and caches it in a global cache that can then be looked up later when
`linfo` callsite is seen again.
"""
function cache_escapes! end

# when working outside of Core.Compiler, cache as much as information for later inspection and debugging
if _TOP_MOD !== Core.Compiler
    struct EscapeCache
        cache::Vector{EscapeLatticeCache}
        state::EscapeState # preserved just for debugging purpose
        ir::IRCode         # preserved just for debugging purpose
    end
    const GLOBAL_ESCAPE_CACHE = IdDict{MethodInstance,EscapeCache}()
    function cache_escapes!(linfo::MethodInstance, estate::EscapeState, cacheir::IRCode)
        cache = EscapeCache(to_interprocedural(estate), estate, cacheir)
        GLOBAL_ESCAPE_CACHE[linfo] = cache
        return cache
    end
    argescapes_from_cache(cache::EscapeCache) = cache.cache
else
    const GLOBAL_ESCAPE_CACHE = IdDict{MethodInstance,Vector{EscapeLatticeCache}}()
    function cache_escapes!(linfo::MethodInstance, estate::EscapeState, _::IRCode)
        cache = to_interprocedural(estate)
        GLOBAL_ESCAPE_CACHE[linfo] = cache
        return cache
    end
    argescapes_from_cache(cache::Vector{EscapeLatticeCache}) = cache
end

function to_interprocedural(estate::EscapeState)
    cache = Vector{EscapeLatticeCache}(undef, estate.nargs)
    for i = 1:estate.nargs
        cache[i] = EscapeLatticeCache(estate.escapes[i])
    end
    return cache
end

__clear_escape_cache!() = empty!(GLOBAL_ESCAPE_CACHE)

const EscapeChange = Pair{Int,EscapeLattice}
const AliasChange  = Pair{Int,Int}
const Changes      = Vector{Union{EscapeChange,AliasChange}}

struct AnalysisState
    ir::IRCode
    estate::EscapeState
    changes::Changes
end

function getinst(ir::IRCode, idx::Int)
    nstmts = length(ir.stmts)
    if idx ≤ nstmts
        return ir.stmts[idx]
    else
        return ir.new_nodes.stmts[idx - nstmts]
    end
end

"""
    analyze_escapes(ir::IRCode, nargs::Int) -> estate::EscapeState

Analyzes escape information in `ir`.
`nargs` is the number of actual arguments of the analyzed call.
"""
function analyze_escapes(ir::IRCode, nargs::Int)
    stmts = ir.stmts
    nstmts = length(stmts) + length(ir.new_nodes.stmts)

    # only manage a single state, some flow-sensitivity is encoded as `EscapeLattice` properties
    estate = EscapeState(nargs, nstmts)
    changes = Changes() # stashes changes that happen at current statement
    tryregions = compute_tryregions(ir)
    astate = AnalysisState(ir, estate, changes)

    local debug_itr_counter = 0
    while true
        local anyupdate = false

        for pc in nstmts:-1:1
            stmt = getinst(ir, pc)[:inst]

            # collect escape information
            if isa(stmt, Expr)
                head = stmt.head
                if head === :call
                    escape_call!(astate, pc, stmt.args)
                elseif head === :invoke
                    escape_invoke!(astate, pc, stmt.args)
                elseif head === :new || head === :splatnew
                    escape_new!(astate, pc, stmt.args)
                elseif head === :(=)
                    lhs, rhs = stmt.args
                    if isa(lhs, GlobalRef) # global store
                        add_escape_change!(astate, rhs, ⊤)
                    else
                        unexpected_assignment!(ir, pc)
                    end
                elseif head === :foreigncall
                    escape_foreigncall!(astate, pc, stmt.args)
                elseif head === :throw_undef_if_not # XXX when is this expression inserted ?
                    add_escape_change!(astate, stmt.args[1], ThrownEscape(pc))
                elseif is_meta_expr_head(head)
                    # meta expressions doesn't account for any usages
                    continue
                elseif head === :enter || head === :leave || head === :the_exception || head === :pop_exception
                    # ignore these expressions since escapes via exceptions are handled by `escape_exception!`
                    # `escape_exception!` conservatively propagates `AllEscape` anyway,
                    # and so escape information imposed on `:the_exception` isn't computed
                    continue
                elseif head === :static_parameter ||  # this exists statically, not interested in its escape
                       head === :copyast ||           # XXX can this account for some escapes?
                       head === :undefcheck ||        # XXX can this account for some escapes?
                       head === :isdefined ||         # just returns `Bool`, nothing accounts for any escapes
                       head === :gc_preserve_begin || # `GC.@preserve` expressions themselves won't be used anywhere
                       head === :gc_preserve_end      # `GC.@preserve` expressions themselves won't be used anywhere
                    continue
                else
                    for x in stmt.args
                        add_escape_change!(astate, x, ⊤)
                    end
                end
            elseif isa(stmt, ReturnNode)
                if isdefined(stmt, :val)
                    add_escape_change!(astate, stmt.val, ReturnEscape(pc))
                end
            elseif isa(stmt, PhiNode)
                escape_edges!(astate, pc, stmt.values)
            elseif isa(stmt, PiNode)
                escape_val_ifdefined!(astate, pc, stmt)
            elseif isa(stmt, PhiCNode)
                escape_edges!(astate, pc, stmt.values)
            elseif isa(stmt, UpsilonNode)
                escape_val_ifdefined!(astate, pc, stmt)
            elseif isa(stmt, GlobalRef) # global load
                add_escape_change!(astate, SSAValue(pc), ⊤)
            elseif isa(stmt, SSAValue)
                escape_val!(astate, pc, stmt)
            elseif isa(stmt, Argument)
                escape_val!(astate, pc, stmt)
            else # otherwise `stmt` can be GotoNode, GotoIfNot, and inlined values etc.
                continue
            end

            isempty(changes) && continue

            anyupdate |= propagate_changes!(estate, changes)

            empty!(changes)
        end

        tryregions !== nothing && escape_exception!(astate, tryregions)

        debug_itr_counter += 1

        anyupdate || break
    end

    # if debug_itr_counter > 2
    #     println("[EA] excessive iteration count found ", debug_itr_counter, " (", singleton_type(ir.argtypes[1]), ")")
    # end

    return estate
end

# propagate changes, and check convergence
function propagate_changes!(estate::EscapeState, changes::Changes)
    local anychanged = false
    for change in changes
        if isa(change, EscapeChange)
            anychanged |= propagate_escape_change!(estate, change)
            xidx, info = change
            aliases = getaliases(xidx, estate)
            if aliases !== nothing
                for aidx in aliases
                    morechange = EscapeChange(aidx, info)
                    anychanged |= propagate_escape_change!(estate, morechange)
                end
            end
        else
            anychanged |= propagate_alias_change!(estate, change)
        end
    end
    return anychanged
end

function propagate_escape_change!(estate::EscapeState, change::EscapeChange)
    xidx, info = change
    old = estate.escapes[xidx]
    new = old ⊔ info
    if old ≠ new
        estate.escapes[xidx] = new
        return true
    end
    return false
end

function propagate_alias_change!(estate::EscapeState, change::AliasChange)
    xidx, yidx = change
    xroot = find_root!(estate.aliasset, xidx)
    yroot = find_root!(estate.aliasset, yidx)
    if xroot ≠ yroot
        union!(estate.aliasset, xroot, yroot)
        return true
    end
    return false
end

function add_escape_change!(astate::AnalysisState, @nospecialize(x), info::EscapeLattice)
    info === ⊥ && return nothing # performance optimization
    xidx = iridx(x, astate.estate)
    if xidx !== nothing
        if !isbitstype(widenconst(argextype(x, astate.ir)))
            push!(astate.changes, EscapeChange(xidx, info))
        end
    end
    return nothing
end

function add_alias_change!(astate::AnalysisState, @nospecialize(x), @nospecialize(y))
    if isa(x, GlobalRef)
        return add_escape_change!(astate, y, ⊤)
    elseif isa(y, GlobalRef)
        return add_escape_change!(astate, x, ⊤)
    end
    estate = astate.estate
    xidx = iridx(x, estate)
    yidx = iridx(y, estate)
    if xidx !== nothing && yidx !== nothing
        pushfirst!(astate.changes, AliasChange(xidx, yidx)) # propagate `AliasChange` first for faster convergence
        xinfo = estate.escapes[xidx]
        yinfo = estate.escapes[yidx]
        xyinfo = xinfo ⊔ yinfo
        add_escape_change!(astate, x, xyinfo)
        add_escape_change!(astate, y, xyinfo)
    end
    return nothing
end

function escape_edges!(astate::AnalysisState, pc::Int, edges::Vector{Any})
    ret = SSAValue(pc)
    for i in 1:length(edges)
        if isassigned(edges, i)
            v = edges[i]
            add_alias_change!(astate, ret, v)
        end
    end
end

escape_val_ifdefined!(astate::AnalysisState, pc::Int, x) =
    isdefined(x, :val) && escape_val!(astate, pc, x.val)

function escape_val!(astate::AnalysisState, pc::Int, @nospecialize(val))
    ret = SSAValue(pc)
    add_alias_change!(astate, ret, val)
end

# NOTE if we don't maintain the alias set that is separated from the lattice state, we can do
# soemthing like below: it essentially incorporates forward escape propagation in our default
# backward propagation, and leads to inefficient convergence that requires more iterations
# # lhs = rhs: propagate escape information of `rhs` to `lhs`
# function escape_alias!(astate::AnalysisState, @nospecialize(lhs), @nospecialize(rhs))
#     if isa(rhs, SSAValue) || isa(rhs, Argument)
#         vinfo = astate.estate[rhs]
#     else
#         return
#     end
#     add_escape_change!(astate, lhs, vinfo)
# end

# linear scan to find regions in which potential throws will be caught
function compute_tryregions(ir::IRCode)
    tryregions = nothing
    for idx in 1:length(ir.stmts)
        stmt = ir.stmts[idx][:inst]
        if isexpr(stmt, :enter)
            tryregions === nothing && (tryregions = UnitRange{Int}[])
            leave_block = stmt.args[1]::Int
            leave_pc = first(ir.cfg.blocks[leave_block].stmts)
            push!(tryregions, idx:leave_pc)
        end
    end
    for idx in 1:length(ir.new_nodes.stmts)
        stmt = ir.new_nodes.stmts[idx][:inst]
        @assert !isexpr(stmt, :enter) "try/catch inside new_nodes unsupported"
    end
    return tryregions
end

"""
    escape_exception!(astate::AnalysisState, tryregions::Vector{UnitRange{Int}})

Propagates escapes via exceptions that can happen in `tryregions`.

Naively it seems enough to propagate escape information imposed on `:the_exception` object,
but actually there are several other ways to access to the exception object such as
`Base.current_exceptions` and manual catch of `rethrow`n object.
For example, escape analysis needs to account for potential escape of the allocated object
via `rethrow_escape!()` call in the example below:
```julia
const Gx = Ref{Any}()
@noinline function rethrow_escape!()
    try
        rethrow()
    catch err
        Gx[] = err
    end
end
unsafeget(x) = isassigned(x) ? x[] : throw(x)

code_escapes() do
    r = Ref{String}()
    try
        t = unsafeget(r)
    catch err
        t = typeof(err)  # `err` (which `r` may alias to) doesn't escape here
        rethrow_escape!() # `r` can escape here
    end
    return t
end
```

As indicated by the above example, it requires a global analysis in addition to a base escape
analysis to reason about all possible escapes via existing exception interfaces correctly.
For now we conservatively always propagate `AllEscape` to all potentially thrown objects,
since such an additional analysis might not be worthwhile to do given that exception handlings
and error paths usually don't need to be very performance sensitive, and optimizations of
error paths might be very ineffective anyway since they are sometimes "unoptimized"
intentionally for latency reasons.
"""
function escape_exception!(astate::AnalysisState, tryregions::Vector{UnitRange{Int}})
    estate = astate.estate
    # NOTE if `:the_exception` is the only way to access the exception, we can do:
    # exc = SSAValue(pc)
    # excinfo = estate[exc]
    excinfo = ⊤
    escapes = estate.escapes
    for i in 1:length(escapes)
        x = escapes[i]
        xt = x.ThrownEscape
        xt === TOP_THROWN_ESCAPE && @goto propagate_exception_escape # fast pass
        for pc in x.ThrownEscape
            for region in tryregions
                pc in region && @goto propagate_exception_escape # early break because of AllEscape
            end
        end
        continue
        @label propagate_exception_escape
        xval = irval(i, estate)
        add_escape_change!(astate, xval, excinfo)
    end
end

function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any})
    linfo = first(args)::MethodInstance
    cache = get(GLOBAL_ESCAPE_CACHE, linfo, nothing)
    if cache === nothing
        for i in 2:length(args)
            x = args[i]
            add_escape_change!(astate, x, ⊤)
        end
    else
        argescapes = argescapes_from_cache(cache)
        retinfo = astate.estate[SSAValue(pc)] # escape information imposed on the call statement
        method = linfo.def::Method
        nargs = Int(method.nargs)
        for i in 2:length(args)
            arg = args[i]
            if i-1 ≤ nargs
                argi = i-1
            else # handle isva signature: COMBAK will this be invalid once we take alias information into account ?
                argi = nargs
            end
            arginfo = argescapes[argi]
            info = from_interprocedural(arginfo, retinfo, pc)
            add_escape_change!(astate, arg, info)
        end
    end
end

"""
    from_interprocedural(arginfo::EscapeLatticeCache, retinfo::EscapeLattice, pc::Int) -> x::EscapeLattice

Reinterprets the escape information imposed on the call argument which is cached as `arginfo`
in the context of the caller frame, where `retinfo` is the escape information imposed on
the return value and `pc` is the SSA statement number of the return value.
"""
function from_interprocedural(arginfo::EscapeLatticeCache, retinfo::EscapeLattice, pc::Int)
    arginfo.AllEscape && return ⊤

    ThrownEscape = arginfo.ThrownEscape ? BitSet(pc) : BOT_THROWN_ESCAPE

    newarginfo = EscapeLattice(
        #=Analyzed=#true, #=ReturnEscape=#BOT_RETURN_ESCAPE, ThrownEscape,
        # FIXME implement interprocedural memory effect-analysis
        # currently, this essentially disables the entire field analysis
        # it might be okay from the SROA point of view, since we can't remove the allocation
        # as far as it's passed to a callee anyway, but still we may want some field analysis
        # for e.g. stack allocation or some other IPO optimizations
        #=AliasEscapes=#TOP_ALIAS_ESCAPES)

    if !arginfo.ReturnEscape
        # if this is simply passed as the call argument, we can discard the `ReturnEscape`
        # information and just propagate the other escape information
        return newarginfo
    end

    # if this argument can be "returned", we have to merge its escape information with that imposed on the return value
    return newarginfo ⊔ retinfo
end

@noinline function unexpected_assignment!(ir::IRCode, pc::Int)
    @eval Main (ir = $ir; pc = $pc)
    error("unexpected assignment found: inspect `Main.pc` and `Main.pc`")
end

function escape_new!(astate::AnalysisState, pc::Int, args::Vector{Any})
    obj = SSAValue(pc)
    objinfo = astate.estate[obj]
    AliasEscapes = objinfo.AliasEscapes
    nargs = length(args)
    if isa(AliasEscapes, Bool)
        @label conservative_propagation
        # the fields couldn't be analyzed precisely: propagate the entire escape information
        # of this object to all its fields as the most conservative propagation
        for i in 2:nargs
            add_escape_change!(astate, args[i], objinfo)
        end
    elseif isa(AliasEscapes, FieldEscapes)
        # fields are known: propagate escape information imposed on recorded possibilities
        nf = length(AliasEscapes)
        for i in 2:nargs
            # fields are known: propagate the escape information of this object ignoring field information
            add_escape_change!(astate, args[i], ignore_aliasescapes(objinfo))
            # fields are known: propagate escape information imposed on recorded possibilities
            i-1 > nf && break # may happen when e.g. ϕ-node merges values with different types
            escape_field!(astate, args[i], AliasEscapes[i-1])
        end
    else
        # this object has been used as array, but it is allocated as struct here (i.e. should throw)
        # update obj's field information and just handle this case conservatively
        @assert isa(AliasEscapes, ArrayEscapes)
        objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
        @goto conservative_propagation
    end
    if !(getinst(astate.ir, pc)[:flag] & IR_FLAG_EFFECT_FREE ≠ 0)
        add_thrown_escapes!(astate, pc, args)
    end
end

function escape_field!(astate::AnalysisState, @nospecialize(v), xf::FieldEscape)
    estate = astate.estate
    for xidx in xf
        x = irval(xidx, estate)::SSAValue # TODO remove me once we implement ArgEscape
        add_alias_change!(astate, v, x)
    end
end

function escape_unanalyzable_obj!(astate::AnalysisState, @nospecialize(obj), objinfo::EscapeLattice)
    objinfo = EscapeLattice(objinfo, TOP_ALIAS_ESCAPES)
    add_escape_change!(astate, obj, objinfo)
    return objinfo
end

function add_thrown_escapes!(astate::AnalysisState, pc::Int, args::Vector{Any},
    first_idx::Int = 1, last_idx::Int = length(args))
    for i in first_idx:last_idx
        add_escape_change!(astate, args[i], ThrownEscape(pc))
    end
end

# escape every argument `(args[6:length(args[3])])` and the name `args[1]`
# TODO: we can apply a similar strategy like builtin calls to specialize some foreigncalls
function escape_foreigncall!(astate::AnalysisState, pc::Int, args::Vector{Any})
    nargs = length(args)
    if nargs < 6
        # invalid foreigncall, just escape everything
        return add_thrown_escapes!(astate, pc, args)
    end
    foreigncall_nargs = length((args[3])::SimpleVector)
    name = args[1]
    nn = normalize(name)
    if isa(nn, Symbol)
        boundserror_ninds = array_resize_info(nn)
        if boundserror_ninds !== nothing
            boundserror, ninds = boundserror_ninds
            escape_array_resize!(boundserror, ninds, astate, pc, args)
            return
        end
        if is_array_copy(nn)
            escape_array_copy!(astate, pc, args)
            return
        elseif is_array_isassigned(nn)
            escape_array_isassigned!(astate, pc, args)
            return
        end
        # if nn === :jl_gc_add_finalizer_th
        #     # TODO add `FinalizerEscape` ?
        # end
    end
    # NOTE array allocations might have been proven as nothrow (https://github.com/JuliaLang/julia/pull/43565)
    info = astate.ir.stmts[pc][:flag] & IR_FLAG_EFFECT_FREE ≠ 0 ?
           EscapeLattice(NoEscape(), #=AliasEscapes=#true) :
           EscapeLattice(ThrownEscape(pc), #=AliasEscapes=#true)
    add_escape_change!(astate, name, info)
    for i in 6:5+foreigncall_nargs
        add_escape_change!(astate, args[i], info)
    end
end

normalize(@nospecialize x) = isa(x, QuoteNode) ? x.value : x

# NOTE error cases will be handled in `analyze_escapes` anyway, so we don't need to take care of them below
# TODO implement more builtins, make them more accurate
# TODO use `T_IFUNC`-like logic and don't not abuse dispatch ?

function escape_call!(astate::AnalysisState, pc::Int, args::Vector{Any})
    ir = astate.ir
    ft = argextype(first(args), ir, ir.sptypes, ir.argtypes)
    f = singleton_type(ft)
    if isa(f, Core.IntrinsicFunction)
        # XXX somehow `:call` expression can creep in here, ideally we should be able to do:
        # argtypes = Any[argextype(args[i], astate.ir) for i = 2:length(args)]
        argtypes = Any[]
        for i = 2:length(args)
            arg = args[i]
            push!(argtypes, isexpr(arg, :call) ? Any : argextype(arg, ir))
        end
        intrinsic_nothrow(f, argtypes) || add_thrown_escapes!(astate, pc, args, 2)
        return # TODO accounts for pointer operations
    end
    result = escape_builtin!(f, astate, pc, args)
    if result === missing
        # if this call hasn't been handled by any of pre-defined handlers,
        # we escape this call conservatively
        for i in 2:length(args)
            add_escape_change!(astate, args[i], ⊤)
        end
        return
    elseif result === true
        return # ThrownEscape is already checked
    end
    # we escape statements with the `ThrownEscape` property using the effect-freeness
    # computed by `stmt_effect_free` invoked within inlining
    # TODO throwness ≠ "effect-free-ness"
    if !(getinst(astate.ir, pc)[:flag] & IR_FLAG_EFFECT_FREE ≠ 0)
        add_thrown_escapes!(astate, pc, args, 2)
    end
end

escape_builtin!(@nospecialize(f), _...) = return missing

# safe builtins
escape_builtin!(::typeof(isa), _...) = return false
escape_builtin!(::typeof(typeof), _...) = return false
escape_builtin!(::typeof(sizeof), _...) = return false
escape_builtin!(::typeof(===), _...) = return false
# not really safe, but `ThrownEscape` will be imposed later
escape_builtin!(::typeof(isdefined), _...) = return false
escape_builtin!(::typeof(throw), _...) = return false

function escape_builtin!(::typeof(ifelse), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) == 4 || return false
    f, cond, th, el = args
    ret = SSAValue(pc)
    condt = argextype(cond, astate.ir)
    if isa(condt, Const) && (cond = condt.val; isa(cond, Bool))
        if cond
            add_alias_change!(astate, th, ret)
        else
            add_alias_change!(astate, el, ret)
        end
    else
        add_alias_change!(astate, th, ret)
        add_alias_change!(astate, el, ret)
    end
    return false
end

function escape_builtin!(::typeof(typeassert), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) == 3 || return false
    f, obj, typ = args
    ret = SSAValue(pc)
    add_alias_change!(astate, ret, obj)
    return false
end

function escape_builtin!(::typeof(tuple), astate::AnalysisState, pc::Int, args::Vector{Any})
    escape_new!(astate, pc, args)
    return false
end

function escape_builtin!(::typeof(getfield), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) ≥ 3 || return false
    ir, estate = astate.ir, astate.estate
    obj = args[2]
    typ = widenconst(argextype(obj, ir))
    if hasintersect(typ, Module) # global load
        add_escape_change!(astate, SSAValue(pc), ⊤)
    end
    if isa(obj, SSAValue) || isa(obj, Argument)
        objinfo = estate[obj]
    else
        return false
    end
    AliasEscapes = objinfo.AliasEscapes
    if isa(AliasEscapes, Bool)
        if !AliasEscapes
            # the fields of this object haven't been analyzed yet: analyze them now
            nfields = fieldcount_noerror(typ)
            if nfields !== nothing
                AliasEscapes = FieldEscape[FieldEscape() for _ in 1:nfields]
                @goto record_field_escape
            end
            # unsuccessful field analysis: update obj's field information
            objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
        end
        @label conservative_propagation
        # the field couldn't be analyzed precisely: propagate the escape information
        # imposed on the return value of this `getfield` call to the object itself
        # as the most conservative propagation
        ssainfo = estate[SSAValue(pc)]
        add_escape_change!(astate, obj, ssainfo)
    elseif isa(AliasEscapes, FieldEscapes)
        nfields = fieldcount_noerror(typ)
        if nfields === nothing
            # unsuccessful field analysis: update obj's field information
            objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
            @goto conservative_propagation
        else
            AliasEscapes = copy(AliasEscapes)
            if nfields > length(AliasEscapes)
                for _ in 1:(nfields-length(AliasEscapes))
                    push!(AliasEscapes, FieldEscape())
                end
            end
        end
        # fields are known: record the return value of this `getfield` call as a possibility
        # that imposes escape on field(s) being referenced
        @label record_field_escape
        if isa(typ, DataType)
            fld = args[3]
            fldval = try_compute_field(ir, fld)
            fidx = try_compute_fieldidx(typ, fldval)
        else
            fidx = nothing
        end
        if fidx !== nothing
            # the field is known precisely: propagate this escape information to the field
            push!(AliasEscapes[fidx], iridx(SSAValue(pc), estate))
        else
            # the field isn't known precisely: propagate this escape information to all the fields
            for FieldEscape in AliasEscapes
                push!(FieldEscape, iridx(SSAValue(pc), estate))
            end
        end
        add_escape_change!(astate, obj, EscapeLattice(objinfo, AliasEscapes))
    else
        # this object has been used as array, but it is used as struct here (i.e. should throw)
        # update obj's field information and just handle this case conservatively
        @assert isa(AliasEscapes, ArrayEscapes)
        objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
        @goto conservative_propagation
    end
    return false
end

function escape_builtin!(::typeof(setfield!), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) ≥ 4 || return false
    ir, estate = astate.ir, astate.estate
    obj = args[2]
    val = args[4]
    if isa(obj, SSAValue) || isa(obj, Argument)
        objinfo = estate[obj]
    else
        # unanalyzable object (e.g. obj::GlobalRef): escape field value conservatively
        add_escape_change!(astate, val, ⊤)
        @goto add_thrown_escapes
    end
    AliasEscapes = objinfo.AliasEscapes
    if isa(AliasEscapes, Bool)
        if !AliasEscapes
            # the fields of this object haven't been analyzed yet: analyze them now
            typ = widenconst(argextype(obj, ir))
            nfields = fieldcount_noerror(typ)
            if nfields !== nothing
                # successful field analysis: update obj's field information
                AliasEscapes = FieldEscape[FieldEscape() for _ in 1:nfields]
                objinfo = EscapeLattice(objinfo, AliasEscapes)
                add_escape_change!(astate, obj, objinfo)
                @goto add_field_escape
            end
            # unsuccessful field analysis: update obj's field information
            objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
        end
        @label conservative_propagation
        # the field couldn't be analyzed precisely: propagate the entire escape information
        # of this object to the value being assigned as the most conservative propagation
        add_escape_change!(astate, val, objinfo)
    elseif isa(AliasEscapes, FieldEscapes)
        typ = widenconst(argextype(obj, ir))
        nfields = fieldcount_noerror(typ)
        if nfields === nothing
            # unsuccessful field analysis: update obj's field information
            objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
            @goto conservative_propagation
        elseif nfields > length(AliasEscapes)
            AliasEscapes = copy(AliasEscapes)
            for _ in 1:(nfields-length(AliasEscapes))
                push!(AliasEscapes, FieldEscape())
            end
        end
        # fields are known: propagate escape information imposed on recorded possibilities
        @label add_field_escape
        if isa(typ, DataType)
            fld = args[3]
            fldval = try_compute_field(ir, fld)
            fidx = try_compute_fieldidx(typ, fldval)
        else
            fidx = nothing
        end
        if fidx !== nothing
            # the field is known precisely: propagate this escape information to the field
            escape_field!(astate, val, AliasEscapes[fidx])
        else
            # the field isn't known precisely: propagate this escape information to all the fields
            for FieldEscape in AliasEscapes
                escape_field!(astate, val, FieldEscape)
            end
        end
        # fields are known: propagate the escape information of this object ignoring field information
        add_escape_change!(astate, val, ignore_aliasescapes(objinfo))
    else
        # this object has been used as array, but it is "used" as struct here (i.e. should throw)
        # update obj's field information and just handle this case conservatively
        @assert isa(AliasEscapes, ArrayEscapes)
        objinfo = escape_unanalyzable_obj!(astate, obj, objinfo)
        @goto conservative_propagation
    end
    # also propagate escape information imposed on the return value of this `setfield!`
    ssainfo = estate[SSAValue(pc)]
    add_escape_change!(astate, val, ssainfo)
    # compute the throwness of this setfield! call here since builtin_nothrow doesn't account for that
    @label add_thrown_escapes
    argtypes = Any[]
    for i = 2:length(args)
        push!(argtypes, argextype(args[i], ir))
    end
    setfield!_nothrow(argtypes) || add_thrown_escapes!(astate, pc, args, 2)
    return true
end

function escape_builtin!(::typeof(arrayref), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) ≥ 4 || return false
    # check potential thrown escapes from this arrayref call
    argtypes = Any[argextype(args[i], astate.ir) for i in 2:length(args)]
    boundcheckt = argtypes[1]
    aryt = argtypes[2]
    if !array_builtin_common_typecheck(boundcheckt, aryt, argtypes, 3)
        add_thrown_escapes!(astate, pc, args, 2)
    end
    ary = args[3]
    inbounds = isa(boundcheckt, Const) && !boundcheckt.val::Bool
    inbounds || add_escape_change!(astate, ary, ThrownEscape(pc))
    # we don't track precise index information about this array and thus don't know what values
    # can be referenced here: directly propagate the escape information imposed on the return
    # value of this `arrayref` call to the array itself as the most conservative propagation
    # but also with updated index information
    # TODO enable index analysis when constant values are available?
    estate = astate.estate
    if isa(ary, SSAValue) || isa(ary, Argument)
        aryinfo = estate[ary]
    else
        return true
    end
    AliasEscapes = aryinfo.AliasEscapes
    ret = SSAValue(pc)
    if isa(AliasEscapes, Bool)
        if !AliasEscapes
            # the elements of this array haven't been analyzed yet: set ArrayEscapes now
            AliasEscapes = ArrayEscapes()
            @goto record_element_escape
        end
        @label conservative_propagation
        ssainfo = estate[ret]
        add_escape_change!(astate, ary, ssainfo)
    elseif isa(AliasEscapes, ArrayEscapes)
        # record the return value of this `arrayref` call as a possibility that imposes escape
        AliasEscapes = copy(AliasEscapes)
        @label record_element_escape
        push!(AliasEscapes, iridx(ret, estate))
        add_escape_change!(astate, ary, EscapeLattice(aryinfo, AliasEscapes))
    else
        # this object has been used as struct, but it is used as array here (thus should throw)
        # update ary's element information and just handle this case conservatively
        @assert isa(AliasEscapes, FieldEscapes)
        aryinfo = escape_unanalyzable_obj!(astate, ary, aryinfo)
        @goto conservative_propagation
    end
    return true
end

function escape_builtin!(::typeof(arrayset), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) ≥ 5 || return false
    # check potential escapes from this arrayset call
    # NOTE here we essentially only need to account for TypeError, assuming that
    # UndefRefError or BoundsError don't capture any of the arguments here
    argtypes = Any[argextype(args[i], astate.ir) for i in 2:length(args)]
    boundcheckt = argtypes[1]
    aryt = argtypes[2]
    valt = argtypes[3]
    if !(array_builtin_common_typecheck(boundcheckt, aryt, argtypes, 4) &&
         arrayset_typecheck(aryt, valt))
        add_thrown_escapes!(astate, pc, args, 2)
    end
    ary = args[3]
    val = args[4]
    inbounds = isa(boundcheckt, Const) && !boundcheckt.val::Bool
    inbounds || add_escape_change!(astate, ary, ThrownEscape(pc))
    # we don't track precise index information about this array and won't record what value
    # is being assigned here: directly propagate the escape information of this array to
    # the value being assigned as the most conservative propagation
    # TODO enable index analysis when constant values are available?
    estate = astate.estate
    if isa(ary, SSAValue) || isa(ary, Argument)
        aryinfo = estate[ary]
    else
        # unanalyzable object (e.g. obj::GlobalRef): escape field value conservatively
        add_escape_change!(astate, val, ⊤)
        return true
    end
    AliasEscapes = aryinfo.AliasEscapes
    if isa(AliasEscapes, Bool)
        if !AliasEscapes
            # the elements of this array haven't been analyzed yet: don't need to consider ArrayEscapes for now
            @goto add_ary_escape
        end
        @label conservative_propagation
        add_escape_change!(astate, val, aryinfo)
    elseif isa(AliasEscapes, ArrayEscapes)
        escape_elements!(astate, val, AliasEscapes)
        @label add_ary_escape
        add_escape_change!(astate, val, ignore_aliasescapes(aryinfo))
    else
        # this object has been used as struct, but it is "used" as array here (thus should throw)
        # update ary's element information and just handle this case conservatively
        @assert isa(AliasEscapes, FieldEscapes)
        aryinfo = escape_unanalyzable_obj!(astate, ary, aryinfo)
        @goto conservative_propagation
    end
    # also propagate escape information imposed on the return value of this `arrayset`
    ssainfo = estate[SSAValue(pc)]
    add_escape_change!(astate, ary, ssainfo)
    return true
end

function escape_elements!(astate::AnalysisState, @nospecialize(v), xa::ArrayEscapes)
    estate = astate.estate
    for xidx in xa
        x = irval(xidx, estate)::SSAValue # TODO remove me once we implement ArgEscape
        add_alias_change!(astate, v, x)
    end
end

function escape_builtin!(::typeof(arraysize), astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) == 3 || return false
    ary = args[2]
    dim = args[3]
    if !arraysize_typecheck(ary, dim, astate.ir)
        add_escape_change!(astate, ary, ThrownEscape(pc))
        add_escape_change!(astate, dim, ThrownEscape(pc))
    end
    # NOTE we may still see "arraysize: dimension out of range", but it doesn't capture anything
    return true
end

function arraysize_typecheck(@nospecialize(ary), @nospecialize(dim), ir::IRCode)
    aryt = argextype(ary, ir)
    aryt ⊑ₜ Array || return false
    dimt = argextype(dim, ir)
    dimt ⊑ₜ Int || return false
    return true
end

# returns nothing if this isn't array resizing operation,
# otherwise returns true if it can throw BoundsError and false if not
function array_resize_info(name::Symbol)
    if name === :jl_array_grow_beg || name === :jl_array_grow_end
        return false, 1
    elseif name === :jl_array_del_beg || name === :jl_array_del_end
        return true, 1
    elseif name === :jl_array_grow_at || name === :jl_array_del_at
        return true, 2
    else
        return nothing
    end
end

# NOTE may potentially throw "cannot resize array with shared data" error,
# but just ignore it since it doesn't capture anything
function escape_array_resize!(boundserror::Bool, ninds::Int,
    astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) ≥ 6+ninds || return add_thrown_escapes!(astate, pc, args)
    ary = args[6]
    aryt = argextype(ary, astate.ir)
    aryt ⊑ₜ Array || return add_thrown_escapes!(astate, pc, args)
    for i in 1:ninds
        ind = args[i+6]
        indt = argextype(ind, astate.ir)
        indt ⊑ₜ Integer || return add_thrown_escapes!(astate, pc, args)
    end
    if boundserror
        # this array resizing can potentially throw `BoundsError`, impose it now
        add_escape_change!(astate, ary, ThrownEscape(pc))
    end
end

is_array_copy(name::Symbol) = name === :jl_array_copy

# FIXME this implementation is very conservative, improve the accuracy and solve broken test cases
function escape_array_copy!(astate::AnalysisState, pc::Int, args::Vector{Any})
    length(args) ≥ 6 || return add_thrown_escapes!(astate, pc, args)
    ary = args[6]
    aryt = argextype(ary, astate.ir)
    aryt ⊑ₜ Array || return add_thrown_escapes!(astate, pc, args)
    if isa(ary, SSAValue) || isa(ary, Argument)
        newary = SSAValue(pc)
        aryinfo = astate.estate[ary]
        newaryinfo = astate.estate[newary]
        add_escape_change!(astate, newary, aryinfo)
        add_escape_change!(astate, ary, newaryinfo)
    end
end

is_array_isassigned(name::Symbol) = name === :jl_array_isassigned

function escape_array_isassigned!(astate::AnalysisState, pc::Int, args::Vector{Any})
    if !array_isassigned_nothrow(args, astate.ir)
        add_thrown_escapes!(astate, pc, args)
    end
end

function array_isassigned_nothrow(args::Vector{Any}, src::IRCode)
    # if !validate_foreigncall_args(args,
    #     :jl_array_isassigned, Cint, svec(Any,Csize_t), 0, :ccall)
    #     return false
    # end
    length(args) ≥ 7 || return false
    arytype = argextype(args[6], src)
    arytype ⊑ₜ Array || return false
    idxtype = argextype(args[7], src)
    idxtype ⊑ₜ Csize_t || return false
    return true
end

# # COMBAK do we want to enable this (and also backport this to Base for array allocations?)
# import Core.Compiler: Cint, svec
# function validate_foreigncall_args(args::Vector{Any},
#     name::Symbol, @nospecialize(rt), argtypes::SimpleVector, nreq::Int, convension::Symbol)
#     length(args) ≥ 5 || return false
#     normalize(args[1]) === name || return false
#     args[2] === rt || return false
#     args[3] === argtypes || return false
#     args[4] === vararg || return false
#     normalize(args[5]) === convension || return false
#     return true
# end

if isdefined(Core, :ImmutableArray)

import Core: ImmutableArray, arrayfreeze, mutating_arrayfreeze, arraythaw

escape_builtin!(::typeof(arrayfreeze), astate::AnalysisState, pc::Int, args::Vector{Any}) =
    is_safe_immutable_array_op(Array, astate, args)
escape_builtin!(::typeof(mutating_arrayfreeze), astate::AnalysisState, pc::Int, args::Vector{Any}) =
    is_safe_immutable_array_op(Array, astate, args)
escape_builtin!(::typeof(arraythaw), astate::AnalysisState, pc::Int, args::Vector{Any}) =
    is_safe_immutable_array_op(ImmutableArray, astate, args)
function is_safe_immutable_array_op(@nospecialize(arytype), astate::AnalysisState, args::Vector{Any})
    length(args) == 2 || return false
    argextype(args[2], astate.ir) ⊑ₜ arytype || return false
    return true
end

end # if isdefined(Core, :ImmutableArray)

# NOTE define fancy package utilities when developing EA as an external package
if _TOP_MOD !== Core.Compiler
    include(@__MODULE__, "EAUtils.jl")
    using .EAUtils: code_escapes, @code_escapes
    export code_escapes, @code_escapes
end

end # baremodule EscapeAnalysis


text:
# This file is part of the ValidatedNumerics.jl package; MIT licensed

function sinh{T}(a::Interval{T})
    isempty(a) && return a
    return Interval(sinh(a.lo, RoundDown), sinh(a.hi, RoundUp))
end

function cosh{T}(a::Interval{T})
    isempty(a) && return a
    return Interval(cosh(mig(a), RoundDown), cosh(mag(a), RoundUp))
end

# function tanh(a::Interval{Float64})
#     isempty(a) && return a
#
#     float(tanh(big53(a)))
# end

function tanh(a::Interval{BigFloat})
    isempty(a) && return a
    return @round(BigFloat, tanh(a.lo), tanh(a.hi))
end

function asinh(a::Interval{BigFloat})
    isempty(a) && return a
    @round(BigFloat, asinh(a.lo), asinh(a.hi))
end

function acosh(a::Interval{BigFloat})
    domain = Interval(one(eltype(a)), Inf)
    a = a ∩ domain
    isempty(a) && return a
    @round(BigFloat, acosh(a.lo), acosh(a.hi))
end

function atanh(a::Interval{BigFloat})
    domain = Interval(-one(eltype(a)), one(eltype(a)))
    a = a ∩ domain

    isempty(a) && return a

    result = @round(BigFloat, atanh(a.lo), atanh(a.hi))

    (isinf(result.lo) && isinf(result.hi) && isnan(diam(result))) && return emptyinterval(a)  # IEEE 1788-2015 does not allow intervals consisting only of ∞, i.e. Interval(∞,∞) and Interval(-∞,-∞)

    result
end

# Float64 versions of functions missing from CRlibm:
for f in (:tanh, :asinh, :acosh, :atanh)

    @eval function ($f)(a::Interval{Float64})
        isempty(a) && return a

        float( ($f)(big53(a)) )
    end
end


text:
# Naive detector
#
module Detector

function partSig(w, sig)
    # partition (3,[1,2,3,4]) -> [1,2,3], [2,3,4]
    @assert length(sig) >= w
    out=[]
    while length(sig)>=w
        push!(out, sig[1:w])
        shift!(sig)
    end
    return(out)
end

function movMin(w, sig)
    # Moving min value
    prt = partSig(w,sig)
    map(minimum,prt)
end

function naiveDetector{T<:Number}(sig::Vector{T}, threshold::T)
    # naive detector returns indices of changepoints given a fived threshold
    chps = filter(x -> x[2] >= threshold, [e for e in enumerate(sig)])
    map(x->x[1],chps)
end

function naiveDetectorDynamic{T<:Number}(sig::Vector{T}, threshold::Vector{T})
    # naive detector returns indices of changepoints given a dynamic threshold
    @assert length(sig)==length(threshold)
    n=length(sig)
    chps=Int64[]
    for i=1:n
        if sig[i] > threshold[i]
            push!(chps,i)
        end
    end
    chps
end

end

max_stars_repo_name: av-maslov/Recurrency

max_issues_repo_name: av-maslov/Recurrency

max_forks_repo_name: av-maslov/Recurrency


text:
#######################################################
#-->Author: Redeem_Grimm
#-->Purpose: Find Anagram Of A Word From A List Of Words
########################################################

using HTTP

function is_anagram(firstWord,secondWord)
    firstWordSorted=join(sort(collect(firstWord)))
    secondWordSorted=join(sort(collect(secondWord)))
    return firstWordSorted==secondWordSorted

end

function download_words(urlOfFile)
   contentOfFile=HTTP.download(urlOfFile)
   listOfWords=[lowercase(words) for words in readlines(contentOfFile)]
   return listOfWords

end

function find_anagram(word,wordList)

   return filter(x-> is_anagram(x,word),wordList)

end

max_stars_repo_name: Redeem-Grimm-Satoshi/Julia-Anagram

max_issues_repo_name: Redeem-Grimm-Satoshi/Julia-Anagram

max_forks_repo_name: Redeem-Grimm-Satoshi/Julia-Anagram


text:
using Pkg
Pkg.activate("../../")
using Plots
using Econometrics
include("JDlib.jl")
θ = TrueParameters()
#θ = PriorDraw()
rets, RV, MedRV, ret0, Monday = dgp(θ,1)

#=
#Ret0 = lsfit(abs.(Ret0),[ones(1000) Monday])[3]
plot(layout=(4,1))
p1 = plot(rets, label="rets")
vline!(jumptimes, label="jumps", linealpha=0.5)
p2 = plot(Ret0,label="ret0")
vline!(jumptimes, label="jumps", linealpha=0.5 )
p3 = plot(RV, label="RV")
vline!(jumptimes, label="jumps", linealpha=0.5)
p4 = plot(MedRV, label="MedRV")
vline!(jumptimes, label="jumps", linealpha=0.5)
plot(p1,p2, p3, p4)
=#

max_stars_repo_name: mcreel/ILSNM

max_issues_repo_name: mcreel/ILSNM

max_forks_repo_name: mcreel/ILSNM


text:
function Coeffgamm2Simple_dq0(k, T, m)
    (
        k * (coth(Epi(k, m) / (2 * T)) / Epi(k, m)^2)
    ) / (16 * pi^2)
end

function propImSimple_dq0(p0, ps, T, IRScale, UVScale, Npi, m, lamda)
    -hquadrature(
        x ->
            2*Coeffgamm2Simple_dq0(x, T, m) *
            central_fdm(5,1)(z->VImintqsSimple(p0, ps,z, k, T, Npi, m, lamda,UVScale),Epi(k,m)),
        IRScale,
        UVScale,
        atol = 1e-3,
        rtol = 1e-3,initdiv=10,maxevals=1600,
    )[1]
end


text:
@testset "connector" begin
include("connectorTest.jl")
end

@testset "NN" begin
include("NNTest.jl")
end

@testset "ResNN" begin
include("ResNNTest.jl")
end


text:
using Bridge
 using Random
 using DataFrames
 using CSV

 SRC_DIR = joinpath(Base.source_dir(), "..", "src")
 include(joinpath(SRC_DIR, "auxiliary", "data_simulation_fns.jl"))
 OUT_DIR = joinpath(Base.source_dir(), "..", "output")
 mkpath(OUT_DIR)

 include(joinpath(SRC_DIR, "types.jl"))
 include(joinpath(SRC_DIR, "stochastic_process", "guid_prop_bridge.jl"))
 include(joinpath(SRC_DIR, "stochastic_process", "bounded_diffusion_domain.jl"))
 include(joinpath(SRC_DIR, "solvers", "euler_maruyama_dom_restr.jl"))
 include(joinpath(SRC_DIR, "examples", "fitzHughNagumo.jl"))

 param = :simpleConjug
 FILENAME_OUT = joinpath(OUT_DIR,
                         "test_path_fpt_"*String(param)*".csv")
 let
     P = FitzhughDiffusion(param, 10.0, -8.0, 15.0, 0.0, 3.0)
     # starting point under :regular parametrisation
     x0 = ℝ{2}(-0.5, -0.3)
     # tranlate to conjugate parametrisation
     x0 = regularToConjug(x0, P.ϵ, 0.0)

     dt = 1/50000
     T = 10.0
     tt = 0.0:dt:T

     # due to memory constraints simulation needs to be done iteratively on segments

     # start from indicator that the down-crossing has already occured
     recentlyUpSearch = true
     tt_temp = copy(tt)
     Random.seed!(10)

     upLvl = 0.5
     downLvl = -0.5
     # Effective simulation of FPT over N*T interval
     N = 20
     upCrossingTimes = Float64[]
     upCrossingLvls = Float64[]
     downCrossingLvls = Float64[]
     x2 = Float64[]
     x0_perm = copy(x0)

     for i in 1:N
         # simulate path segment
         XX, _ = simulateSegment(0.0, x0, P, tt_temp)
         # determine all relevant up-crossings on it
         upCrossings, recentlyUpSearch = findCrossings(XX, upLvl, downLvl,
                                                       recentlyUpSearch)

         upCrossingTimes = vcat(upCrossingTimes, upCrossings)
         upCrossingLvls = vcat(upCrossingLvls, fill(upLvl, length(upCrossings)))
         downCrossingLvls = vcat(downCrossingLvls, fill(downLvl, length(upCrossings)))
         x2 = vcat(x2, fill(NaN, length(upCrossings)))

         x0 = XX.yy[end]
         tt_temp = tt .+ tt_temp[end]
     end

     # append info about the starting point:
     upCrossingTimes = vcat(0.0, upCrossingTimes)
     upCrossingLvls = vcat(x0_perm[1], upCrossingLvls)
     downCrossingLvls = vcat(downLvl, downCrossingLvls)
     x2 = vcat(x0_perm[2], x2)

     df = DataFrame(time=upCrossingTimes,
                    upCross=upCrossingLvls,
                    downCross=downCrossingLvls,
                    x2=x2)

     CSV.write(FILENAME_OUT, df)
 end


text:
function make_index(array::Array, next_state)
    next_state - 1
end
function make_index(array::AbstractArray, (axis, next_state))
    next_state
end
function make_index(something, next_state)
    make_index(parent(something), next_state)
end

function previous_index(something, index)
    previous_index(parent(something), index)
end
function previous_index(array::AbstractArray, index)
    index - 1
end

function last_index(array::AbstractArray)
    last(LinearIndices(array))
end
function last_index(iterator)
    last_index(parent(iterator))
end

# piracy

function parent(mapped::Generator)
    mapped.iter
end
function parent(filtered::Filter)
    filtered.itr
end
function getindex(mapped::Generator, index)
    mapped.f(getindex(parent(mapped), index))
end
function getindex(filtered::Filter, index)
    getindex(parent(filtered), index)
end
function view(mapped::Generator, indexes)
    Generator(mapped.f, view(parent(mapped), indexes))
end
function view(filtered::Filter, indexes)
    Filter(filtered.f, view(parent(filtered), indexes))
end


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

using Test, Base.CoreLogging
import Base.CoreLogging: BelowMinLevel, Debug, Info, Warn, Error,
    handle_message, shouldlog, min_enabled_level, catch_exceptions

import Test: collect_test_logs, TestLogger
using Printf: @sprintf

isdefined(Main, :MacroCalls) || @eval Main include("testhelpers/MacroCalls.jl")
using Main.MacroCalls

#-------------------------------------------------------------------------------
@testset "Logging" begin

@testset "Basic logging" begin
    @test_logs (Debug, "a") min_level=Debug @debug "a"
    @test_logs (Info,  "a") @info  "a"
    @test_logs (Warn,  "a") @warn  "a"
    @test_logs (Error, "a") @error "a"
end

#-------------------------------------------------------------------------------
# Front end

@testset "Log message formatting" begin
    @test_logs (Info, "sum(A) = 16.0") @info begin
        A = fill(1.0, 4, 4)
        "sum(A) = $(sum(A))"
    end
    x = 10.50
    @test_logs (Info, "10.5") @info "$x"
    @test_logs (Info, "10.500") @info @sprintf("%.3f", x)
end

@testset "Programmatically defined levels" begin
    level = Info
    @test_logs (Info, "X") @logmsg level "X"
    level = Warn
    @test_logs (Warn, "X") @logmsg level "X"
end

@testset "Structured logging with key value pairs" begin
    foo_val = 10
    bar_val = 100
    logs,_ = collect_test_logs() do
        @info "test"  bar_val  progress=0.1  foo=foo_val  2*3  real_line=(@__LINE__)
        @info begin
            value_in_msg_block = 1000.0
            "test2"
        end value_in_msg_block
        test_splatting(;kws...) = @info "test3" kws...
        test_splatting(a=1,b=2.0)
    end
    @test length(logs) == 3

    record = logs[1]
    kwargs = record.kwargs

    # Builtin metadata
    @test record._module == @__MODULE__
    @test record.file == Base.source_path()
    @test record.line == kwargs[:real_line]
    @test record.id isa Symbol
    @test occursin(r"^.*logging_[[:xdigit:]]{8}$", String(record.id))

    # User-defined metadata
    @test kwargs[:bar_val] === bar_val
    @test kwargs[:progress] == 0.1
    @test kwargs[:foo] === foo_val
    @test kwargs[Symbol(:(2*3))] === 6

    # Keyword values accessible from message block
    record2 = logs[2]
    @test occursin((Info, "test2"), record2)
    kwargs = record2.kwargs
    @test kwargs[:value_in_msg_block] === 1000.0

    # Splatting of keywords
    record3 = logs[3]
    @test occursin((Info, "test3"), record3)
    kwargs = record3.kwargs
    @test sort(collect(keys(kwargs))) == [:a, :b]
    @test kwargs[:a] === 1
    @test kwargs[:b] === 2.0
end

@testset "Log message exception handling" begin
    # Exceptions in message creation are caught by default
    @test_logs (Error,Test.Ignored(),Test.Ignored(),:logevent_error) catch_exceptions=true  @info "foo $(1÷0)"
    # Exceptions propagate if explicitly disabled for the logger (by default
    # for the test logger)
    @test_throws DivideError collect_test_logs() do
        @info  "foo $(1÷0)"
    end
end

@testset "Special keywords" begin
    logger = TestLogger()
    with_logger(logger) do
        @info "foo" _module=Base.Core _id=:asdf _group=:somegroup _file="/a/file" _line=-10
    end
    @test length(logger.logs) == 1
    record = logger.logs[1]
    @test record._module == Base.Core
    @test record.group == :somegroup
    @test record.id == :asdf
    @test record.file == "/a/file"
    @test record.line == -10
    # Test consistency with shouldlog() function arguments
    @test record.level   == logger.shouldlog_args[1]
    @test record._module == logger.shouldlog_args[2]
    @test record.group   == logger.shouldlog_args[3]
    @test record.id      == logger.shouldlog_args[4]

    # handling of nothing
    logger = TestLogger()
    with_logger(logger) do
        @info "foo" _module = nothing _file = nothing _line = nothing
    end
    @test length(logger.logs) == 1
    record = logger.logs[1]
    @test record._module == nothing
    @test record.file == nothing
    @test record.line == nothing
end

# PR #28209
@testset "0-arg MethodErrors" begin
    @test_throws MethodError @macrocall(@logmsg :Notice)
    @test_throws MethodError @macrocall(@debug)
    @test_throws MethodError @macrocall(@info)
    @test_throws MethodError @macrocall(@warn)
    @test_throws MethodError @macrocall(@error)
end

#-------------------------------------------------------------------------------
# Early log level filtering

@testset "Early log filtering" begin
    @testset "Log filtering, per task logger" begin
        @test_logs (Warn, "c") min_level=Warn begin
            @info "b"
            @warn "c"
        end
    end

    @testset "Log level filtering - global flag" begin
        # Test utility: Log once at each standard level
        function log_each_level()
            @debug "a"
            @info  "b"
            @warn  "c"
            @error "d"
        end

        disable_logging(BelowMinLevel)
        @test_logs (Debug, "a") (Info, "b") (Warn, "c") (Error, "d") min_level=Debug  log_each_level()

        disable_logging(Debug)
        @test_logs (Info, "b") (Warn, "c") (Error, "d") min_level=Debug  log_each_level()

        disable_logging(Info)
        @test_logs (Warn, "c") (Error, "d") min_level=Debug  log_each_level()

        disable_logging(Warn)
        @test_logs (Error, "d") min_level=Debug  log_each_level()

        disable_logging(Error)
        @test_logs log_each_level()

        # Reset to default
        disable_logging(BelowMinLevel)
    end

    @testset "Log level filtering - ENV" begin
        logger = TestLogger()
        with_logger(logger) do
            for (e, r) in (("", false),
                            (",,,,", false),
                            ("al", false),
                            ("all", true),
                            ("a,b,all,c", true),
                            ("a,b,,c", false),
                            ("Mainb", false),
                            ("aMain", false),
                            ("Main", true),
                            ("a,b,Main,c", true),
                            ("Base", true),
                            ("a,b,Base,c", true),
                            ("Filesystem", true),
                            ("a,b,Filesystem,c", true),
                            ("a,b,Base.Filesystem,c", false),
                            ("!al", true),
                            ("all,!al", true),
                            ("all,!al,!all", false),
                            ("!all,Main", true),
                            ("!all,!Main", false),
                            ("!all,a,b,!Main,c", false),
                            ("!all,Filesystem", true),
                            ("!all,Base.Filesystem", false),
                            ("a,b,all,!all,c", false),
                            ("!Main", false),
                            ("a,b,!Main,c", false),
                            ("!Base", false),
                            ("all,!Base", false),
                            ("!all,Base", true),
                            ("!all,!Base", false),
                            ("a,b,!Base,c", false),
                            ("all,a,b,!Base,c", false),
                            ("!all,a,b,Base,c", true),
                            ("!all,a,b,!Base,c", false),
                            ("!Filesystem", false),
                            ("all,!Filesystem", false),
                            ("!all,Filesystem", true),
                            ("!all,!Filesystem", false),
                            ("a,b,!Filesystem,c", false),
                            ("all,a,b,!Filesystem,c", false),
                            ("!all,a,b,Filesystem,c", true),
                            ("!all,a,b,!Filesystem,c", false),
                            ("a,b,!Base.Filesystem,c", true),
                            ("all,a,b,!Base.Filesystem,c", true),
                            ("!all,a,b,Base.Filesystem,c", false),
                            ("!all,a,b,!Base.Filesystem,c", false),
                           )
                ENV["JULIA_DEBUG"] = e
                @test CoreLogging.env_override_minlevel(:Main, Base.Filesystem) === r
                @test CoreLogging.current_logger_for_env(BelowMinLevel, :Main, Base.Filesystem) === (r ? logger : nothing)
                @test CoreLogging.current_logger_for_env(Info, :Main, Base.Filesystem) === logger
            end
        end
    end
    ENV["JULIA_DEBUG"] = ""
end

#-------------------------------------------------------------------------------

@eval module LogModuleTest
    function a()
        @info  "a"
    end

    module Submodule
        function b()
            @info  "b"
        end
    end
end

@testset "Capture of module information" begin
    @test_logs(
        (Info, "a", LogModuleTest),
        (Info, "b", LogModuleTest.Submodule),
        begin
            LogModuleTest.a()
            LogModuleTest.Submodule.b()
        end
    )
end

#-------------------------------------------------------------------------------
@testset "Logger installation and access" begin
    @testset "Global logger" begin
        logger1 = global_logger()
        logger2 = TestLogger()
        # global_logger() returns the previously installed logger
        @test logger1 === global_logger(logger2)
        # current logger looks up global logger by default.
        @test current_logger() === logger2
        global_logger(logger1) # Restore global logger
    end
end

#-------------------------------------------------------------------------------

# Custom log levels

@eval module LogLevelTest
    using Base.CoreLogging

    struct MyLevel
        level::Int
    end

    Base.convert(::Type{LogLevel}, l::MyLevel) = LogLevel(l.level)

    const critical = MyLevel(10000)
    const debug_verbose = MyLevel(-10000)
end

@testset "Custom log levels" begin
    @test_logs (LogLevelTest.critical, "blah") @logmsg LogLevelTest.critical "blah"
    logs,_ = collect_test_logs(min_level=Debug) do
        @logmsg LogLevelTest.debug_verbose "blah"
    end
    @test length(logs) == 0
end

#-------------------------------------------------------------------------------

@testset "SimpleLogger" begin
    # Log level limiting
    @test min_enabled_level(SimpleLogger(devnull, Debug)) == Debug
    @test min_enabled_level(SimpleLogger(devnull, Error)) == Error

    # Log limiting
    logger = SimpleLogger(devnull)
    @test shouldlog(logger, Info, Base, :group, :asdf) === true
    handle_message(logger, Info, "msg", Base, :group, :asdf, "somefile", 1, maxlog=2)
    @test shouldlog(logger, Info, Base, :group, :asdf) === true
    handle_message(logger, Info, "msg", Base, :group, :asdf, "somefile", 1, maxlog=2)
    @test shouldlog(logger, Info, Base, :group, :asdf) === false
    @test catch_exceptions(logger) === false

    # Log formatting
    function genmsg(level, message, _module, filepath, line; kws...)
        io = IOBuffer()
        logger = SimpleLogger(io, Debug)
        handle_message(logger, level, message, _module, :group, :id,
                       filepath, line; kws...)
        String(take!(io))
    end

    # Simple
    @test genmsg(Info, "msg", Main, "some/path.jl", 101) ==
    """
    ┌ Info: msg
    └ @ Main some/path.jl:101
    """

    # Multiline message
    @test genmsg(Warn, "line1\nline2", Main, "some/path.jl", 101) ==
    """
    ┌ Warning: line1
    │ line2
    └ @ Main some/path.jl:101
    """

    # Keywords
    @test genmsg(Error, "msg", Base, "other.jl", 101, a=1, b="asdf") ==
    """
    ┌ Error: msg
    │   a = 1
    │   b = asdf
    └ @ Base other.jl:101
    """

    # nothing values
    @test genmsg(Warn, "msg", nothing, nothing, nothing) ==
    """
    ┌ Warning: msg
    └ @ nothing nothing:nothing
    """
end

# Issue #26273
let m = Module(:Bare26273i, false)
    Core.eval(m, :(import Base: @error))
    @test_logs (:error, "Hello") Core.eval(m, quote
        @error "Hello"
    end)
end

@testset "#26335: _module and _file kwargs" begin
    ignored = Test.Ignored()
    @test_logs (:warn, "a", ignored, ignored, ignored, "foo.jl") (@warn "a" _file="foo.jl")
    @test_logs (:warn, "a", Base) (@warn "a" _module=Base)
end

# Issue #28786
@testset "ID generation" begin
    logs,_ = collect_test_logs() do
        for i in 1:2
            @info "test"
            @info "test"
        end
    end
    @test length(logs) == 4
    @test logs[1].id == logs[3].id
    @test logs[2].id == logs[4].id
    @test logs[1].id != logs[2].id
end

# Issue #34485
@testset "`_group` must be a `Symbol`" begin
    (record,), _ = collect_test_logs() do
        @info "test"
    end
    @test record.group == :logging  # name of this file
end

end

max_stars_repo_name: TimoLarson/julia

max_issues_repo_name: TimoLarson/julia

max_forks_repo_name: TimoLarson/julia


text:
using Test
using LinearAlgebraicRepresentation
Lar = LinearAlgebraicRepresentation
using ViewerGL
GL = ViewerGL

@testset "GLTest.jl" begin

   # function charpols(mystring)
   @testset "charpols" begin
      @test
      @test
      @test
      @test
   end

   # function charseq(mystring)
   @testset "charseq" begin
      @test
      @test
      @test
      @test
   end

   # function cons(funs)
   @testset "cons" begin
      @test
      @test
      @test
      @test
   end

   # function comp(funs)
   @testset "comp" begin
      @test
      @test
      @test
      @test
   end

   # function compose(f,g)
   @testset "compose" begin
      @test
      @test
      @test
      @test
   end

   # function distr(args)
   @testset "distr" begin
      @test
      @test
      @test
      @test
   end

   # function text(mystring,flag=true)
   @testset "text" begin
      @test
      @test
      @test
      @test
   end

   # function a2a(mat)
   @testset "a2a" begin
      @test
      @test
      @test
      @test
   end

   # function translate(c)
   @testset "translate" begin
      @test
      @test
      @test
      @test
   end

   # function align(textalignment="centre")
   @testset "align" begin
      @test
      @test
      @test
      @test
   end

   # function cat(args)
   @testset "cat" begin
      @test
      @test
      @test
      @test
   end

   # function k(any::Any)
   @testset "k" begin
      @test
      @test
      @test
      @test
   end

   # function apply(fun::Function)
   @testset "fapply" begin
      @test
      @test
      @test
      @test
   end

   # function embed(n)
   @testset "embed" begin
      @test
      @test
      @test
      @test
   end

   # function numbering(numberSizeScaling=1)
   @testset "numbering" begin
      @test
      @test
      @test
      @test
   end

   # function numbering1(scaling=0.1)
   @testset "numbering1" begin
      @test
      @test
      @test
      @test
   end

end


text:
module QuadraturesTests

using Test
using Gridap

const D = 2
quad = TensorProductQuadrature(orders=(2,4))

coords_ref = [
  -0.57735 0.57735 -0.57735 0.57735 -0.57735 0.57735;
  -0.774597 -0.774597 0.0 0.0 0.774597 0.774597]

@test isa(coordinates(quad),Array{Point{D,Float64},1})
@test isa(weights(quad),Array{Float64,1})

coords = coordinates(quad)

coords_arr = reshape( reinterpret(Float64,coords), (D,length(coords)) )

@test isapprox(coords_ref,coords_arr,rtol=10-3)

weigs = weights(quad)

weigs_ref = [0.555556, 0.555556, 0.888889, 0.888889, 0.555556, 0.555556]

@test isapprox(weigs,weigs_ref,rtol=10-3)

quad = Quadrature((HEX_AXIS,HEX_AXIS,HEX_AXIS),order=2)

@test isa(quad,TensorProductQuadrature{3})

end # module QuadraturesTests


text:
"""
    TuringModel <: Probabilistic

Fields:
- `model`: a Turing model function; define it via `@model func(X, y)`
- `n_samples`
- `sampler`
- `renamer`: Can be used to rename the MCMCChains parameter names.
    Defaults to `nothing`.
    If `renamer isa Dict`, then names are dropped from the data `X`.
- `n_chains`
- `multithreaded`: Defaults to `true`; this can be set to false when doing threaded evaluations via MLJ.
"""
mutable struct TuringModel <: Probabilistic
    model::Function
    n_samples::Int
    sampler::Any
    renamer::Union{Nothing,Dict}
    n_chains::Int
    multithreaded::Bool
end

function TuringModel(model, n_samples, sampler; renamer=nothing, n_chains=3, multithreaded=true)
    return TuringModel(model, n_samples, sampler, renamer, n_chains, multithreaded)
end

function _parameter_mean(chns, parameter)
    values = collect(Iterators.flatten(chns[parameter]))
    return mean(values)
end

"""
    _simplify_data(X) -> Matrix

Convert `X` to a Matrix since this usually works better with Turing.jl models.
Assumes that `X` satisfies the Tables interface.
This code is partially based on `Base.Matrix` in DataFrames.jl.
"""
function _simplify_data(X)::Matrix
    nrows = length(Tables.rows(X))
    ncols = length(Tables.columns(X))
    T = reduce(promote_type, (eltype(v) for v in Tables.columns(X)), init=Union{})
    out = Matrix{T}(undef, nrows, ncols)
    for (i, row) in enumerate(Tables.rows(X))
        for (j, col) in enumerate(Tables.columnnames(row))
            out[i, j] = Tables.getcolumn(row, col)
        end
    end
    return out
end

function MMI.fit(tm::TuringModel, verbosity::Int, X, y)
    renamed_X = isnothing(tm.renamer) ? X : _simplify_data(X)
    model = tm.model(renamed_X, y)
    chns = if tm.multithreaded
        sample_func(c) = sample(model, tm.sampler, tm.n_samples)
        mapreduce(sample_func, chainscat, 1:tm.n_chains)
    else
        sample(model, tm.sampler, MCMCThreads(), tm.n_samples, tm.n_chains)
    end
    renamed_chns = isnothing(tm.renamer) ? chns : replacenames(chns, tm.renamer)
    fitresult = renamed_chns
    cache = nothing
    report = nothing

    return fitresult, cache, report
end

function MMI.predict(tm::TuringModel, fitresult, Xnew)
    model = tm.model(Xnew, missing)
    chns = predict(model, fitresult)
    y_matrix = mean(Array(group(chns, :y)); dims=1)
    y = collect(Iterators.flatten(y_matrix))
    return y
end


text:
using MachineElements
using Documenter

DocMeta.setdocmeta!(MachineElements, :DocTestSetup, :(using MachineElements); recursive=true)

makedocs(;
    modules=[MachineElements],
    authors="Rojesh Man Shikhrakar",
    repo="https://github.com/rojesh-shikhrakar/MachineElements.jl/blob/{commit}{path}#{line}",
    sitename="MachineElements.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://rojesh-shikhrakar.github.io/MachineElements.jl",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
    ],
)

deploydocs(;
    repo="github.com/rojesh-shikhrakar/MachineElements.jl",
    devbranch="main",
)


text:
using Random: randn!

"""
    RandomFriction <: ElectronicFrictionProvider

Provide a random positive semi-definite matrix of friction values.
Used mostly for testing and examples.
"""
struct RandomFriction <: ElectronicFrictionProvider
    ndofs::Int
end

function friction!(::RandomFriction, F::AbstractMatrix, ::AbstractMatrix)
    randn!(F)
    F .= F'F
    F .= (F + F')/2
end

NQCModels.ndofs(model::RandomFriction) = model.ndofs