text:
### A Pluto.jl notebook ###
# v0.18.1

using Markdown
using InteractiveUtils

# ╔═╡ cbc47c58-c2d9-40da-a31f-5545fb470859
begin

	function skip_as_script(m::Module)
		if isdefined(m, :PlutoForceDisplay)
			return getfield(m, :PlutoForceDisplay)
		else
			isdefined(m, :PlutoRunner) && parentmodule(m) === Main
		end
	end

	"""
	@skip_as_script expression
	Marks a expression as Pluto-only, which means that it won't be executed when running outside Pluto. Do not use this for your own projects.
	"""
	macro skip_as_script(ex) skip_as_script(__module__) ? esc(ex) : nothing end

	const developing = skip_as_script(@__MODULE__)

	const PROJECT_ROOT = dirname(@__DIR__)
	const TEST_DIR = joinpath(PROJECT_ROOT, "test")

	if developing
		import Pkg

		new_env = mktempdir()
		cp(TEST_DIR, new_env; force=true)

		copy!(LOAD_PATH, ["@", PROJECT_ROOT])
		Pkg.activate(new_env)
		Pkg.instantiate()

		# development dependencies
		import Downloads
		import JSON
		using BenchmarkTools
		using PlutoTest
	else
		macro test(e...); nothing; end
		macro benchmark(e...); nothing; end
		macro test_broken(e...); nothing; end
		macro test_throws(e...); nothing; end
	end

	using HypertextLiteral
	import AbstractPlutoDingetjes

	const pkg_setup = "done"

	if developing
		html"""

Development environment active 🚀

		"""
	end
end

# ╔═╡ da12a2c8-a631-4da8-be4e-87cc1e1f124c
md"""
# Promises.jl: *JavaScript-inspired async*

> #### Summary:
>
> A **`Promise{T}`** is a container for a value **that will arrive in the future**.
>
> You can **await** Promises, and you can **chain** processing steps with `.then` and `.catch`, each producing a new `Promise`.

"""

# ╔═╡ f0567e34-6fb8-4509-80e7-532e0464f1bd
md"""
Let's look at an example, using Promises.jl to download data in the background:
"""

# ╔═╡ 1cf696fd-6fa4-4e93-8132-63d89d902f95
username = "JuliaLang"

# ╔═╡ 7e24cd7d-6f1c-47e2-b0a3-d8f81a4e7167
md"""
The result is a *pending promise*: it might still running in the background!
"""

# ╔═╡ 82f259ca-0e35-4278-ac46-aed2fdb87857
md"""
You can use `@await` to wait for it to finish, and get its value:
"""

# ╔═╡ d3b5ee70-81d2-425d-9fb0-4c9a42ea4674
md"""
Since the original Promise `download_result` was asynchronous, this newly created `Promise` is also asynchronous! By chaining the operations `read` and `JSON.parse`, you are "queing" them to run in the background.
"""

# ╔═╡ 2614a13c-3454-444f-86ac-fedd6cb58c3c
md"""
If you `@await` a Promise that has rejected, the rejected value will be rethrown as an error:
"""

# ╔═╡ 1d66a02b-5c9c-4c3a-bfbc-b11fc7196c3a
md"""
(A shorthand function is available to create promises that immediately reject or resolve, like we did above: `Promises.resolve(value)` and `Promises.reject(value)`.)
"""

# ╔═╡ 038949f4-3f99-496e-a3c7-f980f2fa92d2
md"""
2. The `.catch` is the opposite of `.then`: it is used to handle rejected values.
"""

# ╔═╡ bdb0e349-b043-4a07-9dc8-1f2ea587ac2f
md"""
Here is a little table:

|  | `.then` | `.catch` |
| --- | --- | --- |
| On a **resolved** Promise: | Runs | *Skipped* |
| On a **rejected** Promise: | *Skipped* | Runs |
"""

# ╔═╡ 74cdad42-7f54-4da3-befe-a67c969217ae
md"""
This information is available to the Julia compiler, which means that it can do smart stuff!
"""

# ╔═╡ ae4e308e-83be-4e0b-a0a4-96677dcffa22
md"""
Trying to resolve to another type will reject the Promise:
"""

# ╔═╡ bcaaf4f0-cb32-4136-aaec-3259ef6b383d
md"""
#### Automatic types

Julia is smart, and it can automatically determine the type of chained Promises using static analysis!
"""

# ╔═╡ 580d9608-fb50-4845-b3b2-4195cdb41d67

# ╔═╡ 530e9bf7-bd09-4978-893a-c945ca15e508
md"""
# Implementation
"""

# ╔═╡ bc985d7d-0e31-4754-84a9-67f653248b7c
cleanup_backtrace(x) = x

# ╔═╡ 3e65077d-8c4e-4531-a6fa-d177fd7ed1ea
function cleanup_backtrace(val::CapturedException)
	try
		stack = [s for (s, _) in val.processed_bt]

		pluto_stuff_index = findfirst(val.processed_bt) do (f,_)
			occursin("function_wrapped_cell", String(f.func))
		end

		stack = if pluto_stuff_index === nothing
			val.processed_bt
		else
			val.processed_bt[begin:pluto_stuff_index-1]
		end

		# here = replace(@__FILE__, r"#==#.*" => "")
		# stack_really = filter(stack) do (f,_)
		# 	!occursin(here, String(f.file))
		# end
		stack_really = stack

		CapturedException(val.ex, stack_really)
	catch e
		val
	end
end

# ╔═╡ 49a8beb7-6a97-4c46-872e-e89822108f39
begin
	Base.@kwdef struct Promise{T}
		resolved_val::Ref{Union{Nothing,Some{T}}}=Ref{Union{Nothing,Some{T}}}(nothing)
		rejected_val::Ref{Union{Nothing,Some{Any}}}=Ref{Union{Nothing,Some{Any}}}(nothing)
		done::Channel{Nothing}=Channel{Nothing}(1)
	end

	function Promise{T}(f::Function) where T
		p = Promise{T}()
		begin
			function resolve(val=nothing)
				if !isready(p.done)
					if val isa Promise
						# TODO: this leaks memory maybe because one will keep hanging?
						val.then(resolve)
						val.catch(reject)
						return val
					elseif val isa T
						p.resolved_val[] = Some{T}(val)
					else
						p.rejected_val[] = Some{Any}(cleanup_backtrace(CapturedException(
							ArgumentError("Can only resolve with values of type $T."),
							backtrace())
						))
					end
					put!(p.done, nothing)
				end
				val
			end
			function reject(val=nothing)
				if !isready(p.done)
					p.rejected_val[] = Some{Any}(val)
					put!(p.done, nothing)
				end
				val
			end
			try
				t = f(resolve, reject)
				# if t isa Task
				# 	wait(t)
				# end
			catch e
				reject(cleanup_backtrace(CapturedException(e, catch_backtrace())))
			end
		end
		p
	end
	function Promise(f::Function)
		Promise{Any}(f)
	end

	Base.eltype(p::Promise{T}) where T = T

	isresolved(p::Promise) = isready(p) && p.resolved_val[] !== nothing
	isrejected(p::Promise) = isready(p) && p.rejected_val[] !== nothing

	Base.isready(p::Promise) = isready(p.done)

	function promise_then(p::Promise{T1}, f::Function) where T1
		if isready(p.done)
			if isresolved(p)
				try
					r = f(something(p.resolved_val[]))
					promise_resolved(r)
				catch e
					promise_rejected(cleanup_backtrace(CapturedException(e, catch_backtrace())))
				end
			else
				p
			end
		else
			T2 = Core.Compiler.return_type(f, (T1,))
			T3 = promise_eltype_recursive(T2)

			Promise{T3}() do resolve, reject
				@async begin
					wait(p.done)
					rv = p.resolved_val[]
					if rv !== nothing
						try
							resolve(f(something(rv)))
						catch e
							reject(cleanup_backtrace(CapturedException(e, catch_backtrace())))
						end
					else
						reject(something(p.rejected_val[]))
					end
				end
			end
		end
	end

	function promise_catch(p::Promise{T1}, f::Function) where T1
		T2 = Core.Compiler.return_type(f, (Any,))

		Promise{Union{T1,T2}}() do resolve, reject
			isready(p.done) || wait(p.done)
			if isresolved(p)
				resolve(something(p.resolved_val[]))
			else
				resolve(f(something(p.rejected_val[])))
			end
		end
	end

	function Base.fetch(p::Promise{T})::T where T
		isready(p.done) || wait(p.done)
		if (rv = p.resolved_val[]) !== nothing
			something(rv)
		else
			throw(something(p.rejected_val[]))
		end
	end

	function Base.wait(p::Promise)
		isready(p.done) || wait(p.done)
		if p.rejected_val[] !== nothing
			throw(something(p.rejected_val[]))
		end
		nothing
	end

	function Base.:(==)(a::Promise{T}, b::Promise{T}) where T
		isready(a) == isready(b) &&
		a.resolved_val[] == b.resolved_val[] &&
		a.rejected_val[] == b.rejected_val[]
	end

	Base.hash(p::Promise) = hash((
		typeof(p),
		isready(p),
		p.resolved_val[],
		p.rejected_val[],
	))

	Base.promote_rule(::Type{Promise{T}}, ::Type{Promise{S}}) where {T,S} = Promise{promote_type(T,S)}
	function Base.convert(PT::Type{Promise{T}}, p::Promise{S}) where {T,S}
		PT() do res, rej
			p.then(val -> res(convert(T, val)))
			p.catch(rej)
		end
	end

	function done_channel()
		c = Channel{Nothing}(1)
		put!(c, nothing)
		c
	end

	promise_resolved(p::Promise) = p
	promise_resolved(x::T) where T =
		Promise{T}(
			resolved_val=Ref{Union{Nothing,Some{T}}}(Some{T}(x)),
			done=done_channel(),
		)
	promise_rejected(x) =
		Promise{Any}(
			rejected_val=Ref{Union{Nothing,Some{Any}}}(Some{Any}(x)),
			done=done_channel(),
		)

	# promise_value_recursive(x) = x
	# promise_value_recursive(p::Promise) =

	promise_eltype_recursive(T::Type) =
		(T <: Promise) ? promise_eltype_recursive(eltype(T)) : T

	function Base.getproperty(p::Promise{T1}, name::Symbol) where T1
		if name === :then
			f -> promise_then(p, f)
		elseif name === :catch
			f -> promise_catch(p, f)
		else
			getfield(p, name)
		end
	end

	function Base.show(io::IO, m::MIME"text/plain", p::Promise)
		summary(io, p)
		if isresolved(p)
			write(io, "( <resolved>: ")
			show(io, m, fetch(p))
			write(io, " )")
		elseif isrejected(p)
			write(io, "( <rejected>: ")
			rej_val = something(p.rejected_val[])
			if rej_val isa CapturedException
				println(io)
				showerror(io, rej_val)
				println(io)
			else
				show(io, m, rej_val)
			end
			write(io, " )")
		else
			write(io, "( <pending> )")
		end
	end

	function Base.show(io::IO, m::MIME"text/html", p::Promise)
		typestr = sprint(summary, p; context=io)
		state, display = if isresolved(p)
			"resolved", p.resolved_val[]
		elseif isrejected(p)
			"rejected", p.rejected_val[]
		else
			"pending", nothing
		end

		show(io, m, @htl(
			"
$(display === nothing ?
				@htl(
				"""$(typestr)( $(
					"<$(state)>"
				) )""") :

				@htl("""$(typestr)( $(
					"<$(state)>: "
				)$(
					(
						AbstractPlutoDingetjes.is_inside_pluto(io) ?
						Main.PlutoRunner.embed_display :
						identity
					)(something(display))
				) )""")
			)
"
		))
	end

	@doc """
	```julia
	Promise{T=Any}((resolve, reject) -> begin
		...

		resolve(value)
	end)
	```

	Run code asynchronously, and keep a reference to its future value.

	Based on [`Promise` in JavaScript](https://javascript.info/promise-basics)!
	""" Promise
end

# ╔═╡ 418f24b9-a86d-45d5-bba1-5dcd188ffafa
yay_result = Promise((resolve, reject) -> resolve("🌟 yay!"))

# ╔═╡ d8aa3fed-78f0-417a-8e47-849ec62fa056
oopsie_result = Promise((res, rej) -> rej("oops!"))

# ╔═╡ acfae6b5-947a-4648-99ba-bcd2dd3afbca
Promise((res, rej) -> rej("oops!")).then(x -> x + 10).then(x -> x / 100)

# ╔═╡ 66b2b18a-2afe-4607-8982-647681ff9816
Promise((res, rej) -> rej("oops!")).then(x -> x + 10).catch(x -> 123)

# ╔═╡ 959d2e3e-1ef6-4a97-a748-31b0b5ece938
Promise{String}((res,rej) -> res("asdf"))

# ╔═╡ f0b73769-dea5-4dfa-8a39-ebf6584abbf5
Core.Compiler.return_type(fetch, (Promise{String},))

# ╔═╡ 9d9179de-19b1-4f40-b816-454a8c071c3d
Promise{String}((res,rej) -> res(12341234))

# ╔═╡ db33260f-e3c9-4184-a4aa-1836e5f4201e
typeof(
	Promise{String}((res,rej) -> res("asdf")).then(first)
)

# ╔═╡ 06a3eb82-0ffd-4c89-8161-d0f385c2a32e
md"""
# `async`/`await`
"""

# ╔═╡ 939c6e86-ded8-4b15-890b-80207e8d692a
macro await(expr)
	:(Base.fetch($(esc(expr))))
end

# ╔═╡ 5a5f26dd-f0df-4dc7-be26-6bb23cc27fb2
function wrap_output_type_in_promise(e)
	if Meta.isexpr(e, :where)
		Expr(e.head, wrap_output_type_in_promise(e.args[1]), e.args[2:end]...)
	elseif Meta.isexpr(e, Symbol("::"), 2)
		Expr(Symbol("::"), e.args[1], Expr(:curly, Promise, e.args[2]))
	else
		e
	end
end

# ╔═╡ 09661381-baec-4e1a-8f4f-dac7aeb4ea3c
function async_promise_core(expr)
	Promise
	cleanup_backtrace
	quote
		Promise() do res, rej
			task = Task() do
				try
					value = $(esc(expr))
					res(value)
				catch e
					rej(cleanup_backtrace(CapturedException(e, catch_backtrace())))
				end
			end
			schedule(task)
		end
	end
end

# ╔═╡ 70ff0b9b-3aa2-4f3f-ab1b-b0a7072e7ffd
function async_promise(expr)
	if Meta.isexpr(expr, :block, 1) ||
		(Meta.isexpr(expr, :block, 2) && expr.args[1] isa LineNumberNode)

		Expr(:block,
			expr.args[1:end-1]...,
			async_promise(expr.args[end])
		)
	else
		if Meta.isexpr(expr, :(->)) || Meta.isexpr(expr, :function)
			# TODO: we could automatically get the promise resolve type?

			if length(expr.args) == 1
				expr
			elseif length(expr.args) == 2
				Expr(
					expr.head,
					esc(wrap_output_type_in_promise(expr.args[1])),
					async_promise_core(expr.args[2]),
				)
			else
				throw(ArgumentError("Don't know what kind of function definition this is.\n\n$(expr)"))
			end
		else
			async_promise_core(expr)
		end
	end
end

# ╔═╡ a854b9e6-1a82-401e-90d5-f05ffaadae61
macro async_promise(expr)
	async_promise(expr)
end

# ╔═╡ 7aef0b5c-dd09-47d3-a08f-81cce84d7ca6
@skip_as_script download_result = @async_promise begin

	# This will download the data,
	#  write the result to a file,
	#  and return the filename.
	Downloads.download("https://api.github.com/users/$(username)")
end

# ╔═╡ d22278fd-33cb-4dad-ad5f-d6d067c33403
@skip_as_script download_result

# ╔═╡ f9fad7ff-cf6f-43eb-83bd-efc0cb6cde65
@skip_as_script @await download_result

# ╔═╡ 42c6edee-d43a-40cd-af4f-3d572a6b5e9a
@skip_as_script download_result.then(
	filename -> read(filename, String)
).then(
	str -> JSON.parse(str)
)

# ╔═╡ 34364f4d-e257-4c22-84ee-d8786a2c377c
@skip_as_script bad_result = download_result.then(d -> sqrt(-1))

# ╔═╡ cfcbf74b-08fe-4be2-a7ac-2a0272a41922
@skip_as_script @await bad_result

# ╔═╡ 2d116910-eb94-49b0-9e51-03fc9e57aebb
macroexpand(@__MODULE__, :(@async_promise function f(a::A, b::B)::C
	sleep(1)
	123
end), recursive=false) |> Base.remove_linenums!

# ╔═╡ 2f7147f0-6b4a-4364-8aab-07f8e150b720
macroexpand(@__MODULE__, :(@async_promise (a,b) -> begin
	sleep(1)
	123
end), recursive=false) |> Base.remove_linenums!

# ╔═╡ 4568aa57-7440-41a0-9eba-4050ce778ebd
macroexpand(@__MODULE__, :(@async_promise begin
	sleep(1)
	123
end), recursive=false) |> Base.remove_linenums!

# ╔═╡ 8e13e697-e29a-473a-ac11-30e0199be5bb
md"""
### Behaviour
"""

# ╔═╡ b9368cf7-cbcd-4b54-9390-78e8c88f064c

# ╔═╡ 26f26b5d-7352-421c-a20b-9ec37dfe3eb6

# ╔═╡ a8a07647-2b61-4401-a04d-0921a6bcec76

# ╔═╡ 51ef3992-d6a7-4b46-970c-6b075d14fb71

# ╔═╡ 5d943937-2271-431c-8fc0-4f963aa4dda0

# ╔═╡ 40c9f96a-41e9-496a-b174-490b72927626
# let
# 	c = Channel(1)
# 	p = Promise((r,_) -> r(take!(c)))
# 	sleep(.1)
# 	@assert !isready(p)
# 	xs = []
# 	ps = map(1:20) do i
# 		p.then(v -> push!(xs, i))
# 	end
# 	put!(c, 123)
# 	wait(p)
# 	wait.(ps)
# 	@test xs == 1:20
# end

# ╔═╡ 3f97f5e7-208a-44dc-9726-1923fd8c824b

# ╔═╡ c68ab4c1-6384-4802-a9a6-697a63d3488e
macro testawait(x)
	# fetch = Base.fetch
	fetch = Expr(:., :Base, QuoteNode(:fetch))
	wrap(x::Expr) = Expr(:call, fetch, x)
	wrap(x::Any) = x

	e = if Meta.isexpr(x, :call, 3) && (
			x.args[1] === :(==) || x.args[1] === :(===)
		)
		Expr(:call, x.args[1],
			wrap(x.args[2]),
			wrap(x.args[3]),
		)
	else
		error("Don't know how to await this expression.")
	end
	:(@test $(e))
end

# ╔═╡ 8ac00844-24e5-416d-aa31-28242e4ee6a3
@testawait nothing === Promise() do res, rej
	sleep(.05)
	sqrt(-1)
	res(5)
end.then(x -> x* 10).catch(e -> nothing)

# ╔═╡ 9ee2e123-7a24-46b2-becf-2d011abdcb19
md"""
### Types
"""

# ╔═╡ 58533024-ea65-4bce-b32a-727a804d1f4d
@test promote_type(Promise{Int64},Promise{Float64}) === Promise{Float64}

# ╔═╡ 5447d12d-7aa5-47f3-bf04-2516a8974bb9
md"""
### Benchmark

(Could be better 😅)
"""

# ╔═╡ 55fb60c1-b48b-4f0a-a24c-dcc2d7f0af4b
Promise((res,rej) -> res(-50)).then(sqrt)

# ╔═╡ 10bfce78-782d-49a1-9fc8-6b2ac5d16831
Promise((res,rej) -> res(-50)).then(sqrt).catch(e -> 0)

# ╔═╡ 5bb55103-bd26-4f30-bed6-026b003617b7
@benchmark(
	fetch(Promise((res,rej) -> res(-50)).then(sqrt).catch(e -> 0)),
	seconds=3
)

# ╔═╡ 74896c89-d332-4f99-aeda-d429fa4ece2e
let
	c = Channel(1)
	put!(c, 1)
	isready(c)
	# @benchmark isready($c) || wait($c)
end

# ╔═╡ 287f91b6-a602-457a-b32b-e0c22f15d514
@benchmark(
	fetch(Promise((res,rej) -> res(50)).then(sqrt).catch(e -> 0).then(sqrt)),
	seconds=3
)

# ╔═╡ fb11d74a-d13c-4459-a4b5-dbc23174dfd4
@benchmark(
	fetch(Promise((res,rej) -> res(50)).then(sqrt).then(sqrt)),
	seconds=3
)

# ╔═╡ ef2a034d-5e33-46c2-a627-0721170b5b34
@benchmark(
	fetch(Promise{Int64}((res,rej) -> res(50)).then(sqrt).then(sqrt)),
	seconds=3
)

# ╔═╡ 788a27b3-aab0-42cc-8197-3cc5b3b875d1
# @benchmark(
# 	let
# 		c = Channel(1)
# 		for x in 1:100
# 			t = @async begin
# 				take!(c)
# 			end
# 			put!(c, nothing)
# 		end
# 	end,
# 	seconds=3
# )

# ╔═╡ 836bf28f-caa5-44e0-ac8c-44a722a9063b
# @benchmark(
# 	let
# 		a = fetch(@async 50)
# 		for x in 1:100
# 			a = fetch(@async sqrt(a))
# 		end
# 	end,
# 	seconds=3
# )

# ╔═╡ 371cede0-6f01-496a-8059-e110dbfc8d05
# @benchmark(
# 	sqrt(sqrt(50)),
# 	seconds=3
# )

# ╔═╡ eb4e90d9-0e21-4f06-842d-4260f074f097
md"""
# Wait for settled
"""

# ╔═╡ 8f37aee7-b5e0-44e3-a6d0-fbbb5b88f3ef
md"""
## `Resolved{T}` and `Rejected{T}` types
"""

# ╔═╡ 7c9b31e6-cb90-4734-bf7b-6c7f0337ac62
begin
	abstract type PromiseSettledResult{T} end

	Base.promote_rule(
		::Type{<:PromiseSettledResult{T}},
		::Type{<:PromiseSettledResult{S}}
	) where {T,S} = PromiseSettledResult{promote_type(T,S)}

	PromiseSettledResult
end

# ╔═╡ 27876191-a023-49e9-bb3a-d3b3f10090d8
begin
	struct Resolved{T} <: PromiseSettledResult{T}
		value::T
	end

	struct Rejected{T} <: PromiseSettledResult{T}
		value::T
	end

	for RT in subtypes(PromiseSettledResult)
		@eval Base.only(a::$RT) = a.value

		@eval Base.:(==)(a::$RT, b::$RT) = a.value == b.value
		@eval Base.hash(a::$RT) = hash(a.value, hash($RT))

		@eval Base.promote_rule(
			t1::Type{$RT{T}},
			t2::Type{$RT{S}}
		) where {T,S} = $RT{promote_type(T,S)}

		@eval function Base.convert(
			::Type{<:$PromiseSettledResult{T}},
			r::$RT{S}
		) where {T,S}
			$RT{T}(convert(T, r.value))
		end
	end

	md"""
	```julia
	struct Resolved{T} <: PromiseSettledResult{T}
		value::T
	end

	struct Rejected{T} <: PromiseSettledResult{T}
		value::T
	end
	```
	"""
end

# ╔═╡ a5b9e007-0282-4eb6-88dd-34855fe42fa4
function await_settled(p::Promise{T})::PromiseSettledResult where T
	wait(p.done)
	isresolved(p) ?
		Resolved{T}(something(p.resolved_val[])) :
		Rejected(something(p.rejected_val[]))
end

# ╔═╡ cb47c8c9-2872-4e35-9939-f953319e1acb
@test Promise{Int64}() do res, rej
	res(1)
	res(2)
end |> await_settled === Resolved(1)

# ╔═╡ 6a84cdd0-f57e-4535-bc16-24bc40018033
@test Promise{Int64}() do res, rej
	rej("asdf")
	rej("wow")
end |> await_settled === Rejected("asdf")

# ╔═╡ a0534c86-5cd6-456a-93a6-19292b5879d6
@test Promise{Int64}() do res, rej
	rej("asdf")
	sqrt(-1)
	rej("wow")
end |> await_settled === Rejected("asdf")

# ╔═╡ be56fd49-7898-4171-8837-8c1b251cdeba
@test Promise{Int64}() do res, rej
	sqrt(-1)
	rej("asdf")
	rej("wow")
end |> await_settled isa Rejected{CapturedException}

# ╔═╡ 6bb08e1c-bfe9-40c7-92b5-5c71aba040dd
@test Promise{Int64}() do res, rej
	res(1)
	sqrt(-1)
	rej("asdf")
end |> await_settled === Resolved(1)

# ╔═╡ 9aa052ef-5f60-4935-94d1-a4cbc5096d46
@skip_as_script let
	p1 = Promise{Int64}() do res, rej
		res(1)
	end

	p2 = Promise{Int64}() do res, rej
		p1.then(res)
		await_settled(p1)
		yield()
		res(-100)
	end

	@test await_settled(p2) === Resolved(1)
end

# ╔═╡ 2b6e41af-c9e9-4774-a6f5-51c301705a10
@skip_as_script let
	p1 = Promise{Int64}() do res, rej
		rej(-1)
	end

	p2 = Promise{Int64}() do res, rej
		p1.catch(res)
		await_settled(p1)
		yield()
		res(-100)
	end

	@test await_settled(p2) === Resolved(-1)
end

# ╔═╡ 1ef3378e-62ac-463b-b8c0-dfb6f46f956b
@skip_as_script let
	p1 = Promise{Int64}() do res, rej
		sqrt(-1)
	end

	p2 = Promise{Any}() do res, rej
		p1.catch(res)
		await_settled(p1)
		yield()
		res(2)
	end

	@test fetch(p2) isa CapturedException
end

# ╔═╡ 5869262c-40fe-4752-856d-1da536e3e11a
@skip_as_script let
	p1 = Promise{Int64}() do res, rej
		sqrt(-1)
	end

	p2 = Promise{Any}() do res, rej
		p1.catch(res)
		await_settled(p1)
		yield()
		res(-100)
	end

	@test fetch(p2) isa CapturedException
end

# ╔═╡ 3cb7964a-45bb-471e-9fca-c390e06b0fee
@test Promise{Int64}() do res, rej
	res("asdf")
end |> await_settled isa Rejected{CapturedException}

# ╔═╡ 9e27473e-91b3-4261-8033-5295d4a94426
@test Promise{Int64}() do res, rej
	@async res("asdf")
end |> await_settled isa Rejected{CapturedException}

# ╔═╡ 4d661d30-6522-4e7d-895a-786d2d776809
@test await_settled(Promise{String}((r,_) -> (sleep(.1); r("asdf")))) ===
	Resolved{String}("asdf")

# ╔═╡ fa4d6805-8b15-4c24-991c-6762d2701932
@test await_settled(Promise{Int64}((res,rej) -> rej("asdf"))) ===
	Rejected{String}("asdf")

# ╔═╡ 56e274e8-7523-45f1-bd44-5ef71d2feaf2
md"""
### Promotion, conversion
"""

# ╔═╡ 75de613a-3eb8-49e8-9f71-fc55b76cef00
@test promote_rule(Resolved{Int64}, Rejected{Float64}) ==
	PromiseSettledResult{Float64}

# ╔═╡ 497a01eb-7cf9-47b9-95bf-75f59829be36
@test promote_rule(PromiseSettledResult{Int64}, Resolved{Float64}) ==
	PromiseSettledResult{Float64}

# ╔═╡ c7ec7091-a9b3-46c8-8eaf-222b5eb7ebc2
@test promote_rule(Resolved{Int64}, Resolved{Float64}) == Resolved{Float64}

# ╔═╡ 44769580-1983-45ac-b1d5-d5ddb252f7bf
@test convert(PromiseSettledResult{Float64}, Resolved(1)) === Resolved(1.0)

# ╔═╡ 3b11ace6-cdf7-4c90-a96c-f804c3cb4e2f
md"""
### Equality, hash
"""

# ╔═╡ c9bc257b-b204-4019-ab65-9d9489cee16d
@test Resolved(1) == Resolved(1.0)

# ╔═╡ ee4c0c55-bf1d-42c8-8de1-350dd17dff7d
@test Rejected(1) == Rejected(1.0)

# ╔═╡ cc244956-78e0-4804-bc14-91c629bdf28f
@test hash(Resolved(1)) == hash(Resolved(1.0))

# ╔═╡ aa22fed7-bb22-4d52-9e64-3f4a27597f93
@test hash(Rejected(1)) == hash(Rejected(1.0))

# ╔═╡ 02edf8aa-10b3-4da9-a097-3b9dc9a7302d
@test hash(Resolved(1)) != hash(Rejected(1.0))

# ╔═╡ a0c7275b-8fcb-4c0b-b724-aa29f0b878e8
md"""
# Combining promises

We also have ports of the JavaScript functions: `Promise.all`, `Promise.any` and `Promise.reject`.
"""

# ╔═╡ 0dcba3ea-1884-4136-b9a6-42b4cbdf0c50
fetch_all(ps) = fetch.(ps)

# ╔═╡ e36ae108-ab09-4e9c-a6a1-9e596408fda0
fetch_all(ps::AbstractSet) = Set(fetch(p) for p in ps)

# ╔═╡ a0a5f687-56a6-4bc0-9e0a-6d22d0d2de47
"""
```julia
all(promises::AbstractVector{Promise})::Promise
all(promises::AbstractSet{Promise, ...})::Promise
all(promises::Tuple{Promise, Promise, ...})::Promise
```

Create a new Promise that waits for all given `promises` to resolved, and then resolves to a single vector, tuple or set with all the values.

If any of the `promises` rejects, the new Promise will also reject.

See also:
- [`any`](@ref), which only rejects if every one of the `promises` rejects.
- [`race`](@ref), which rejects immediately if one of the `promises` rejects.

"""
function promise_all(ps)
	T = Core.Compiler.return_type(fetch_all, (typeof(ps),))

	Promise{T}() do res, rej
		# Attach early exit hooks to all promises:
		for p in ps
			if !isresolved(p) # (optimization)
				p.catch(rej)
			end
		end

		# Wait for all promises to finish
		for p in ps
			wait(p.done)
		end

		yield() # to allow the early exit catch hooks to fire first

		res(fetch_all(ps))
	end
end

# ╔═╡ 940d2947-d18c-4e1e-bc6f-0fcfd6bba63d
@test Core.Compiler.return_type(fetch_all, (Tuple{Promise{Int64},Promise{String}},)) === Tuple{Int64,String}

# ╔═╡ 860132cd-86ea-4a1d-b435-a4f8ff7672ac
@test Core.Compiler.return_type(fetch_all, (Vector{Promise{String}},)) === Vector{String}

# ╔═╡ 8c7599b5-7dd3-4a14-ae1d-e24ba6c7a0d3
@test Core.Compiler.return_type(fetch_all, (Set{Promise{String}},)) === Set{String}

# ╔═╡ ef4bf6e9-06c4-4568-b765-25107c9b994b
md"""
### Examples
"""

# ╔═╡ 6b6caae2-2aa1-424b-83df-70cb6256eef3
md"""
#### Behaviour
"""

# ╔═╡ 47ceb1bc-c95d-472b-9f08-313937ffe14b
md"""
#### Types
"""

# ╔═╡ 4383a75f-cd86-487f-a2a1-6817b5e5bdaa
md"""
## `any` & `race`
"""

# ╔═╡ fec7b7b4-d483-4f92-ac60-3a61edba1075
union_type(ps::AbstractVector{Promise{T}}) where T = T

# ╔═╡ 5da62d01-75af-487c-9727-8c924fbfe26b
union_type(ps::AbstractSet{Promise{T}}) where T = T

# ╔═╡ 8d57a295-0bd3-4a68-acbc-4069b61eb8ed
union_type(ps::Tuple) = Core.Compiler.typejoin((eltype(p) for p in ps)...)

# ╔═╡ 495ebb66-1632-4015-81db-aa7911bcbe14
union_type(ps) = Any

# ╔═╡ a881d3ee-8e26-4aba-b694-5a4a429a941c
"""
```julia
any(promises::AbstractVector{Promise})::Promise
any(promises::AbstractSet{Promise, ...})::Promise
any(promises::Tuple{Promise, Promise, ...})::Promise
```

Create a new Promise that waits for any of the given `promises` to resolve, and then resolves to that value.

If every one of the `promises` rejects, the new Promise will also reject.

See also:
- [`race`](@ref), which rejects immediately if one of the `promises` rejects.
- [`all`](@ref), which waits for all `promises` to resolve.
"""
function promise_any(ps)
	T = union_type(ps)
	Promise{T}() do res, rej
		for p in ps
			@async p.then(res)
		end

		for p in ps
			wait(p.done)
		end
		yield()
		rej(ErrorException("All promises rejected"))
	end
end

# ╔═╡ 09fc563e-3339-4e63-89e6-4f523d201d99
"""
```julia
race(promises::AbstractVector{Promise})::Promise
race(promises::AbstractSet{Promise, ...})::Promise
race(promises::Tuple{Promise, Promise, ...})::Promise
```

Create a new Promise that waits for any of the given `promises` to **resolve or reject**, and then resolves or rejects to that value.

See also:
- [`any`](@ref), which only rejects if every one of the `promises` rejects.
- [`all`](@ref), which waits for all `promises` to resolve.
"""
function promise_race(ps)
	T = union_type(ps)
	Promise{T}() do res, rej
		for p in ps
			@async p.then(res)
			@async p.catch(rej)

			# (optimization)
			if isready(p.done)
				break
			end
		end
	end
end

# ╔═╡ 627b5eac-9cd9-42f4-a7bf-6b7e5b09fd33
const Promises = (;
	resolve = promise_resolved,
	reject = promise_rejected,
	all = promise_all,
	any = promise_any,
	race = promise_race,
	delay = function(delay::Real, val::T=nothing) where T
		Promise{T}() do res, rej
			@async begin
				sleep(delay)
				res(val)
			end
		end
	end,
)

# ╔═╡ 649be363-e5dd-4c76-ae82-83e28e62b4f9
@testawait Promises.resolve(5).then(x -> x // 5) == 1

# ╔═╡ c4158166-b5ed-46aa-93c5-e95c77c57c6c
@testawait 50 == Promises.resolve(5.0).then(x -> x*10).catch(e -> "oops")

# ╔═╡ 4beec0e9-4c1e-4b25-9651-e00c798ed823
@testawait Promises.resolve(4) == 4

# ╔═╡ 1657bdf9-870c-4b7b-a5c4-57b53a3e1b13
let
	a, b = promote(
		Promises.resolve(1),
		Promises.resolve(3.0),
	)
	@test fetch.((a,b)) === (1.0, 3.0)
end

# ╔═╡ f0c68f85-a55d-4823-a699-ce064af29ff4
@testawait Promises.resolve(0.1).
				then(sleep).
				then(n -> n isa Nothing ? 0.2 : "what") == 0.2

# ╔═╡ 6233ed1e-af35-47c6-8645-3906377b029c
@test fetch(Promises.resolve(0.1).
				then(sleep).
				then(n -> n isa Nothing ? 0.2 : "what")) == 0.2

# ╔═╡ 541742f3-df63-4790-b7e0-82c8a78df8c3
@test Promise((r,_) -> r(Promises.resolve(1))) |> await_settled == Resolved(1)

# ╔═╡ cd08bb7e-afc4-4105-ad6a-5444893d7196
@test Promise(
	(r1,_) -> r1(Promise(
		(r2,_) -> r2(Promise(
			(r3,_) -> r3(Promises.resolve(2))
		))
	))
) |> await_settled == Resolved(2)

# ╔═╡ d7fae064-38e1-4b54-81d8-3803dce5ef5a
@test Promise((r,_) -> r(Promises.reject(1))) |> await_settled == Rejected(1)

# ╔═╡ 5d7d93a7-db41-4fa0-b043-9689996bc6af
@test Promises.resolve(Promises.resolve(1)) |> await_settled == Resolved(1)

# ╔═╡ cba01f4e-6628-49cb-a41b-743cc6e89b37
@test Promises.reject(Promises.reject(1)) |> await_settled isa Rejected{<:Promise}

# ╔═╡ 17c7f0ab-169c-4798-8f6d-afe950d10715
@test !isready(Promises.resolve(.1).then(sleep))

# ╔═╡ f0b70a1f-48d1-4593-8e36-092aebb4c92f
@test nothing === @await Promises.resolve(.1).then(sleep)

# ╔═╡ bcbb5f22-02b6-4ff6-a690-46112e25be87
@test typeof([
	Promises.resolve(1),
	Promises.resolve(3.0),
]) === Vector{Promise{Float64}}

# ╔═╡ 2efae81f-78fb-4d9f-ada6-f22beebf0f1e
@test Promises.resolve(4.0) isa Promise{Float64}

# ╔═╡ f0d96abb-16d5-45b1-a3bf-2d6e7ab96549
@test eltype(Promises.resolve(4.0)) === Float64

# ╔═╡ 3388a6bf-7718-485f-83eb-5c2bab93d283
@test await_settled(Promises.reject(1)) === Rejected{Int64}(1)

# ╔═╡ d349e559-8ad2-48a6-b949-b514399553dc
@test await_settled(Promises.resolve(1)) === Resolved{Int64}(1)

# ╔═╡ 03006230-3654-4372-9f49-367d1689c551
@skip_as_script let
	p = Promises.all((
		Promises.resolve(1),
		Promises.reject(2),
	))
	@test await_settled(p) === Rejected(2)
end

# ╔═╡ 120e3b56-c5db-43d4-b165-de77299582e0
@skip_as_script let
	p = Promises.all((
		Promises.delay(0.01, 1),
		Promises.delay(0.05, 2),
	))
	@test await_settled(p) === Resolved((1,2))
end

# ╔═╡ 54b64da2-bce4-4b7d-bfae-394a93996cf2
@skip_as_script let
	p = Promises.all((
		Promises.delay(0.01, 1),
		Promise((res,rej) -> Promises.delay(0.05, 2).then(rej)),
	))
	@test await_settled(p) isa Rejected
end

# ╔═╡ cddeb7fe-4132-4663-bfe0-8fb529278490
pall1 = Promises.all((
	Promises.resolve(1),
	Promises.resolve(2),
	Promises.resolve(3.0),
))

# ╔═╡ b47b7e63-d640-459e-bb72-d51ac9eb71a9
@test pall1 isa Promise{Tuple{Int64,Int64,Float64}}

# ╔═╡ 7f2b582f-2ce9-4240-bf6b-cd2e5a139d03
@test fetch(pall1) === (1,2,3.0)

# ╔═╡ cc54d67d-ab0e-4ef8-afe9-d830ef5cc3ff
pall2 = Promises.all([
	Promises.resolve(1),
	Promises.resolve(2),
	Promises.resolve(3.0),
])

# ╔═╡ 648975e1-0791-407a-881a-c60cce7b69b0
@test pall2 isa Promise{Vector{Float64}}

# ╔═╡ 77ec04ca-c691-4ef5-9661-a9bd37b452a3
pall3 = Promises.all(Set([
	Promises.resolve(1),
	Promises.resolve(2),
	Promises.resolve(3.0),
]))

# ╔═╡ a5c33dca-d24d-41fe-9cf8-b0435fc5cf3f
@test pall3 isa Promise{Set{Float64}}

# ╔═╡ a07a14c9-13dc-43f5-9300-5358356ce1a4

# ╔═╡ aad170a6-c7e3-43c2-bac5-3cd0ff4bf770
md"""
### Examples
"""

# ╔═╡ 0e7fa174-8865-4d75-9c8c-e939165bcd66
md"""
#### Behaviour
"""

# ╔═╡ 81238018-e5dc-491d-8a8f-0964c9a76845
@testawait Promises.any((
	Promises.reject(1),
	Promises.resolve(2),
)) === 2

# ╔═╡ d1e0d545-f5d8-4fb3-866b-810e021f4a45
@test Promises.race((
	Promises.reject(1),
	Promises.resolve(2),
)) |> await_settled == Rejected(1)

# ╔═╡ 08f280ed-e949-4491-8ccc-473c519291dc
@testawait Promises.race((
	Promises.resolve(2),
	Promises.reject(1),
)) === 2

# ╔═╡ 5bafaea8-4b70-4ad6-98df-e2ea6f6e078e
@testawait Promises.any((
	Promises.delay(0.01, 1),
	Promises.delay(0.05, 2),
)) === 1

# ╔═╡ 0a61ae15-f011-49c9-8f3c-aaa01369490f
@testawait Promises.any((
	Promises.delay(0.05, 1),
	Promises.delay(0.01, 2),
)) === 2

# ╔═╡ 298da21f-1719-4635-b10a-7a879cd7fd62
@testawait Promises.any((
	Promises.delay(0.01, 1),
	Promises.delay(0.05, -2).then(sqrt),
)) === 1

# ╔═╡ 93a6be91-31af-43a7-a7b3-1e509acac2e9
@testawait Promises.any((
	Promises.delay(0.05, 1),
	Promises.delay(0.01, -2).then(sqrt),
)) === 1

# ╔═╡ 5bb6467c-8116-4c8b-8182-e246d7b96ea1
@skip_as_script let
	p = Promises.race((
		Promises.delay(0.5, 1),
		Promises.delay(0.01, -2).then(sqrt),
	))

	@test await_settled(p) isa Rejected{CapturedException}
end

# ╔═╡ ec30ab9b-d573-449d-9e0e-18d8c75c694f
Promises.delay(0.5, -2)

# ╔═╡ 53c28e9b-d80e-4c58-ad97-16f28fee80f9
md"""
#### Types
"""

# ╔═╡ f9bde599-294b-48fc-b9fc-acd32dfcdf2a
pany1 = Promises.any((
	Promises.resolve(1),
	Promises.resolve(2),
	Promises.resolve(3.0),
))

# ╔═╡ 8c12ef4b-b5e0-4931-8717-821705567e52
pany2 = Promises.any([
	Promises.resolve(1),
	Promises.resolve(2),
	Promises.resolve(3.0),
])

# ╔═╡ 602b0ca7-f658-417f-aecf-df976122acac
pany3 = Promises.any(Set([
	Promises.resolve(1),
	Promises.resolve(2),
	Promises.resolve(3.0),
]))

# ╔═╡ 1eba6534-f9fb-49b4-bba6-bb6ff6edb616
@test pany1 isa Promise{Real}

# ╔═╡ f73c6b96-de7e-4b9e-945f-4882615b34a3
@test pany2 isa Promise{Float64}

# ╔═╡ 2403cff6-1aa7-47a6-a030-693dd0b89921
@test pany3 isa Promise{Float64}

# ╔═╡ 490ff94f-07e5-435e-8269-bdc2c917bce0
@test fetch(pany1) === 1

# ╔═╡ faca7d88-bcb8-43e3-9cce-20a65061b8d6
@test fetch(pany2) === 1.0

# ╔═╡ b0e0fdf9-7535-45b0-ac85-2c9fdaa28677
@test fetch(pany3) ∈ 1:3

# ╔═╡ aa46c0ef-ab6e-4eba-86c9-e090320ed3f0
md"""
# Appendix
"""

# ╔═╡ 0464056e-9d08-415c-9a77-fcd4676b2167
const br = begin
	struct BR end
	Base.show(io::IO, ::MIME"text/html", b::BR) = write(io, "
")
	Base.show(io::IO, ::MIME"text/markdown", b::BR) = write(io, "
")
	Base.show(io::IO, ::MIME"text/plain", b::BR) = write(io, "
\n")
	BR()
end

# ╔═╡ 8a1a621d-b94c-45ee-87b9-6ac2faa3f877
md"""
$(br)
## Chaining with `then`

One cool feature of promises is **chaining**! Every promise has a `then` function, which can be used to add a new transformation to the chain, returning a new `Promise`.
"""

# ╔═╡ 704d4278-5f7f-40bb-893e-2391a5004279
md"""
$(br)
## Error handling: rejected Promises

A Promise can finish in two ways: it can **✓ resolve** or it can **✗ reject**. In both cases, the `Promise{T}` will store a value, either the *resolved value* (of type `T`) or the *rejected value* (often an error message).

When an error happens inside a Promise handler, it will reject:
"""

# ╔═╡ ab37c026-963b-46d2-bc51-56e36eb3b06b
md"""
$(br)
## The `Promise` constructor

Remember that a promise can finish in two ways: it can **✓ resolve** or it can **✗ reject**. When creating a Promise by hand, this corresponds to the two functions passed in by the constructor, `resolve` and `reject`:

```julia
Promise{T=Any}(resolve, reject) -> begin

	if condition
		# Resolve the promise:
		resolve("Success!")
	else
		# Reject the promise
		reject("Something went wrong...")
	end
end)
```

"""

# ╔═╡ f14c4a43-2f2f-4390-ad4d-940b1926cfb3
md"""
$(br)
### Chaining errors with `.catch`

There are two special things about rejected values in chains:
1. The `.then` function of a rejected Promise will *immediately reject*, passing the value along.
"""

# ╔═╡ 34d6da04-daa8-484b-bb30-2bf2ee55da9d
md"""
$(br)
### `Promise{T}` is a parametric type

Like in TypeScript, the `Promise{T}` can specify its **resolve type**. For example, `Promise{String}` is guaranteed to resolve to a `String`.
"""

# ╔═╡ Cell order:
# ╟─da12a2c8-a631-4da8-be4e-87cc1e1f124c
# ╟─f0567e34-6fb8-4509-80e7-532e0464f1bd
# ╠═7aef0b5c-dd09-47d3-a08f-81cce84d7ca6
# ╟─1cf696fd-6fa4-4e93-8132-63d89d902f95
# ╟─7e24cd7d-6f1c-47e2-b0a3-d8f81a4e7167
# ╠═d22278fd-33cb-4dad-ad5f-d6d067c33403
# ╟─82f259ca-0e35-4278-ac46-aed2fdb87857
# ╠═f9fad7ff-cf6f-43eb-83bd-efc0cb6cde65
# ╟─8a1a621d-b94c-45ee-87b9-6ac2faa3f877
# ╠═42c6edee-d43a-40cd-af4f-3d572a6b5e9a
# ╟─d3b5ee70-81d2-425d-9fb0-4c9a42ea4674
# ╟─704d4278-5f7f-40bb-893e-2391a5004279
# ╠═34364f4d-e257-4c22-84ee-d8786a2c377c
# ╟─2614a13c-3454-444f-86ac-fedd6cb58c3c
# ╠═cfcbf74b-08fe-4be2-a7ac-2a0272a41922
# ╟─ab37c026-963b-46d2-bc51-56e36eb3b06b
# ╠═418f24b9-a86d-45d5-bba1-5dcd188ffafa
# ╠═d8aa3fed-78f0-417a-8e47-849ec62fa056
# ╟─1d66a02b-5c9c-4c3a-bfbc-b11fc7196c3a
# ╟─f14c4a43-2f2f-4390-ad4d-940b1926cfb3
# ╠═acfae6b5-947a-4648-99ba-bcd2dd3afbca
# ╟─038949f4-3f99-496e-a3c7-f980f2fa92d2
# ╠═66b2b18a-2afe-4607-8982-647681ff9816
# ╟─bdb0e349-b043-4a07-9dc8-1f2ea587ac2f
# ╟─34d6da04-daa8-484b-bb30-2bf2ee55da9d
# ╠═959d2e3e-1ef6-4a97-a748-31b0b5ece938
# ╟─74cdad42-7f54-4da3-befe-a67c969217ae
# ╠═f0b73769-dea5-4dfa-8a39-ebf6584abbf5
# ╟─ae4e308e-83be-4e0b-a0a4-96677dcffa22
# ╠═9d9179de-19b1-4f40-b816-454a8c071c3d
# ╟─bcaaf4f0-cb32-4136-aaec-3259ef6b383d
# ╠═db33260f-e3c9-4184-a4aa-1836e5f4201e
# ╟─580d9608-fb50-4845-b3b2-4195cdb41d67
# ╟─530e9bf7-bd09-4978-893a-c945ca15e508
# ╟─cbc47c58-c2d9-40da-a31f-5545fb470859
# ╠═49a8beb7-6a97-4c46-872e-e89822108f39
# ╠═627b5eac-9cd9-42f4-a7bf-6b7e5b09fd33
# ╠═bc985d7d-0e31-4754-84a9-67f653248b7c
# ╠═3e65077d-8c4e-4531-a6fa-d177fd7ed1ea
# ╟─06a3eb82-0ffd-4c89-8161-d0f385c2a32e
# ╠═939c6e86-ded8-4b15-890b-80207e8d692a
# ╟─5a5f26dd-f0df-4dc7-be26-6bb23cc27fb2
# ╠═09661381-baec-4e1a-8f4f-dac7aeb4ea3c
# ╟─70ff0b9b-3aa2-4f3f-ab1b-b0a7072e7ffd
# ╠═a854b9e6-1a82-401e-90d5-f05ffaadae61
# ╠═2d116910-eb94-49b0-9e51-03fc9e57aebb
# ╠═2f7147f0-6b4a-4364-8aab-07f8e150b720
# ╠═4568aa57-7440-41a0-9eba-4050ce778ebd
# ╟─8e13e697-e29a-473a-ac11-30e0199be5bb
# ╟─649be363-e5dd-4c76-ae82-83e28e62b4f9
# ╟─c4158166-b5ed-46aa-93c5-e95c77c57c6c
# ╟─4beec0e9-4c1e-4b25-9651-e00c798ed823
# ╟─1657bdf9-870c-4b7b-a5c4-57b53a3e1b13
# ╟─f0c68f85-a55d-4823-a699-ce064af29ff4
# ╟─6233ed1e-af35-47c6-8645-3906377b029c
# ╟─8ac00844-24e5-416d-aa31-28242e4ee6a3
# ╟─b9368cf7-cbcd-4b54-9390-78e8c88f064c
# ╟─541742f3-df63-4790-b7e0-82c8a78df8c3
# ╟─cd08bb7e-afc4-4105-ad6a-5444893d7196
# ╟─d7fae064-38e1-4b54-81d8-3803dce5ef5a
# ╟─5d7d93a7-db41-4fa0-b043-9689996bc6af
# ╟─cba01f4e-6628-49cb-a41b-743cc6e89b37
# ╟─26f26b5d-7352-421c-a20b-9ec37dfe3eb6
# ╟─cb47c8c9-2872-4e35-9939-f953319e1acb
# ╟─6a84cdd0-f57e-4535-bc16-24bc40018033
# ╟─a0534c86-5cd6-456a-93a6-19292b5879d6
# ╟─be56fd49-7898-4171-8837-8c1b251cdeba
# ╟─6bb08e1c-bfe9-40c7-92b5-5c71aba040dd
# ╟─a8a07647-2b61-4401-a04d-0921a6bcec76
# ╟─9aa052ef-5f60-4935-94d1-a4cbc5096d46
# ╟─2b6e41af-c9e9-4774-a6f5-51c301705a10
# ╟─1ef3378e-62ac-463b-b8c0-dfb6f46f956b
# ╟─5869262c-40fe-4752-856d-1da536e3e11a
# ╟─51ef3992-d6a7-4b46-970c-6b075d14fb71
# ╟─3cb7964a-45bb-471e-9fca-c390e06b0fee
# ╟─9e27473e-91b3-4261-8033-5295d4a94426
# ╟─5d943937-2271-431c-8fc0-4f963aa4dda0
# ╠═40c9f96a-41e9-496a-b174-490b72927626
# ╟─3f97f5e7-208a-44dc-9726-1923fd8c824b
# ╠═17c7f0ab-169c-4798-8f6d-afe950d10715
# ╟─f0b70a1f-48d1-4593-8e36-092aebb4c92f
# ╟─c68ab4c1-6384-4802-a9a6-697a63d3488e
# ╟─9ee2e123-7a24-46b2-becf-2d011abdcb19
# ╟─58533024-ea65-4bce-b32a-727a804d1f4d
# ╟─bcbb5f22-02b6-4ff6-a690-46112e25be87
# ╟─2efae81f-78fb-4d9f-ada6-f22beebf0f1e
# ╟─f0d96abb-16d5-45b1-a3bf-2d6e7ab96549
# ╟─5447d12d-7aa5-47f3-bf04-2516a8974bb9
# ╠═55fb60c1-b48b-4f0a-a24c-dcc2d7f0af4b
# ╠═10bfce78-782d-49a1-9fc8-6b2ac5d16831
# ╠═5bb55103-bd26-4f30-bed6-026b003617b7
# ╠═74896c89-d332-4f99-aeda-d429fa4ece2e
# ╠═287f91b6-a602-457a-b32b-e0c22f15d514
# ╠═fb11d74a-d13c-4459-a4b5-dbc23174dfd4
# ╠═ef2a034d-5e33-46c2-a627-0721170b5b34
# ╠═788a27b3-aab0-42cc-8197-3cc5b3b875d1
# ╠═836bf28f-caa5-44e0-ac8c-44a722a9063b
# ╠═371cede0-6f01-496a-8059-e110dbfc8d05
# ╟─eb4e90d9-0e21-4f06-842d-4260f074f097
# ╟─a5b9e007-0282-4eb6-88dd-34855fe42fa4
# ╟─3388a6bf-7718-485f-83eb-5c2bab93d283
# ╟─d349e559-8ad2-48a6-b949-b514399553dc
# ╟─4d661d30-6522-4e7d-895a-786d2d776809
# ╟─fa4d6805-8b15-4c24-991c-6762d2701932
# ╟─8f37aee7-b5e0-44e3-a6d0-fbbb5b88f3ef
# ╟─7c9b31e6-cb90-4734-bf7b-6c7f0337ac62
# ╟─27876191-a023-49e9-bb3a-d3b3f10090d8
# ╟─56e274e8-7523-45f1-bd44-5ef71d2feaf2
# ╟─75de613a-3eb8-49e8-9f71-fc55b76cef00
# ╟─497a01eb-7cf9-47b9-95bf-75f59829be36
# ╟─c7ec7091-a9b3-46c8-8eaf-222b5eb7ebc2
# ╟─44769580-1983-45ac-b1d5-d5ddb252f7bf
# ╟─3b11ace6-cdf7-4c90-a96c-f804c3cb4e2f
# ╟─c9bc257b-b204-4019-ab65-9d9489cee16d
# ╟─ee4c0c55-bf1d-42c8-8de1-350dd17dff7d
# ╟─cc244956-78e0-4804-bc14-91c629bdf28f
# ╟─aa22fed7-bb22-4d52-9e64-3f4a27597f93
# ╟─02edf8aa-10b3-4da9-a097-3b9dc9a7302d
# ╟─a0c7275b-8fcb-4c0b-b724-aa29f0b878e8
# ╟─a0a5f687-56a6-4bc0-9e0a-6d22d0d2de47
# ╟─0dcba3ea-1884-4136-b9a6-42b4cbdf0c50
# ╟─e36ae108-ab09-4e9c-a6a1-9e596408fda0
# ╟─940d2947-d18c-4e1e-bc6f-0fcfd6bba63d
# ╟─860132cd-86ea-4a1d-b435-a4f8ff7672ac
# ╟─8c7599b5-7dd3-4a14-ae1d-e24ba6c7a0d3
# ╟─ef4bf6e9-06c4-4568-b765-25107c9b994b
# ╟─6b6caae2-2aa1-424b-83df-70cb6256eef3
# ╟─03006230-3654-4372-9f49-367d1689c551
# ╟─120e3b56-c5db-43d4-b165-de77299582e0
# ╟─54b64da2-bce4-4b7d-bfae-394a93996cf2
# ╟─47ceb1bc-c95d-472b-9f08-313937ffe14b
# ╠═cddeb7fe-4132-4663-bfe0-8fb529278490
# ╠═cc54d67d-ab0e-4ef8-afe9-d830ef5cc3ff
# ╠═77ec04ca-c691-4ef5-9661-a9bd37b452a3
# ╟─b47b7e63-d640-459e-bb72-d51ac9eb71a9
# ╟─648975e1-0791-407a-881a-c60cce7b69b0
# ╟─a5c33dca-d24d-41fe-9cf8-b0435fc5cf3f
# ╟─7f2b582f-2ce9-4240-bf6b-cd2e5a139d03
# ╟─4383a75f-cd86-487f-a2a1-6817b5e5bdaa
# ╠═a881d3ee-8e26-4aba-b694-5a4a429a941c
# ╠═09fc563e-3339-4e63-89e6-4f523d201d99
# ╟─fec7b7b4-d483-4f92-ac60-3a61edba1075
# ╟─5da62d01-75af-487c-9727-8c924fbfe26b
# ╟─8d57a295-0bd3-4a68-acbc-4069b61eb8ed
# ╟─495ebb66-1632-4015-81db-aa7911bcbe14
# ╟─a07a14c9-13dc-43f5-9300-5358356ce1a4
# ╟─aad170a6-c7e3-43c2-bac5-3cd0ff4bf770
# ╟─0e7fa174-8865-4d75-9c8c-e939165bcd66
# ╟─81238018-e5dc-491d-8a8f-0964c9a76845
# ╟─d1e0d545-f5d8-4fb3-866b-810e021f4a45
# ╟─08f280ed-e949-4491-8ccc-473c519291dc
# ╟─5bafaea8-4b70-4ad6-98df-e2ea6f6e078e
# ╟─0a61ae15-f011-49c9-8f3c-aaa01369490f
# ╠═298da21f-1719-4635-b10a-7a879cd7fd62
# ╟─93a6be91-31af-43a7-a7b3-1e509acac2e9
# ╠═5bb6467c-8116-4c8b-8182-e246d7b96ea1
# ╠═ec30ab9b-d573-449d-9e0e-18d8c75c694f
# ╟─53c28e9b-d80e-4c58-ad97-16f28fee80f9
# ╠═f9bde599-294b-48fc-b9fc-acd32dfcdf2a
# ╠═8c12ef4b-b5e0-4931-8717-821705567e52
# ╠═602b0ca7-f658-417f-aecf-df976122acac
# ╟─1eba6534-f9fb-49b4-bba6-bb6ff6edb616
# ╟─f73c6b96-de7e-4b9e-945f-4882615b34a3
# ╟─2403cff6-1aa7-47a6-a030-693dd0b89921
# ╟─490ff94f-07e5-435e-8269-bdc2c917bce0
# ╟─faca7d88-bcb8-43e3-9cce-20a65061b8d6
# ╟─b0e0fdf9-7535-45b0-ac85-2c9fdaa28677
# ╟─aa46c0ef-ab6e-4eba-86c9-e090320ed3f0
# ╠═0464056e-9d08-415c-9a77-fcd4676b2167


text:
@testset "Recipes Unit Tests:" begin

end

ext: jl


text:
### A Pluto.jl notebook ###
# v0.16.3

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ ea8e44f2-3308-11ec-37a1-57fa7c71b851
using Flux, CUDA, PlutoUI

# ╔═╡ fe9e97e7-0fd2-47ce-9dd8-fb557eb5343c
using Flux: onehotbatch, onecold, crossentropy

# ╔═╡ ed838cf8-e927-4508-b999-ced96a2e7942
using Flux: DataLoader

# ╔═╡ fb4b1563-5a04-47b4-bf14-72849c21cb8b
using StatsBase: mean

# ╔═╡ 02a76384-3e59-436b-966f-90140b5f226c
using Images: Gray

# ╔═╡ 1ffe6485-1787-4366-b46c-d5e7809474f4
md"# Basic multi-layer perceptron"

# ╔═╡ f39297bf-aeec-4a3d-a5b7-8daf91851af8
import MLDatasets

# ╔═╡ 1ff19be0-3259-4f27-9e96-05908ae2f9b5
md"Convenience struct for our options"

# ╔═╡ c2156f38-3399-4ac7-9562-250c478f24be
Base.@kwdef struct Args
	η = 1e-3
	batchsize = 128
	epochs = 10
	usecuda = true
	infotime = 2
end

# ╔═╡ d9c2df85-b291-45d5-ad90-beed17fc6548
md"This function *just* downloads the data"

# ╔═╡ 897718a3-e665-489d-b320-529f2f512df8
function getdata()
	ENV["DATADEPS_ALWAYS_ACCEPT"] = "true"

	xtrain, ytrain = MLDatasets.MNIST.traindata(Float32)
	xtest, ytest = MLDatasets.MNIST.testdata(Float32)

	ytrain = onehotbatch(ytrain, sort(unique(ytrain)))
	ytest = onehotbatch(ytest, sort(unique(ytest)))

	xtrain, ytrain, xtest, ytest
end

# ╔═╡ 342cd3af-09bf-477a-bf5e-f502d30cc07c
md"*This* function puts the data into small-size ```DataLoader```s."

# ╔═╡ a085723a-3676-40b4-a87c-67bcdd51b7dc
function dataloaders(args)
	xtrain, ytrain, xtest, ytest = getdata()

	trainloader = DataLoader((xtrain, ytrain); batchsize=args.batchsize, shuffle=true)
	testloader = DataLoader((xtest, ytest); batchsize=size(xtest, ndims(xtest)))

	trainloader, testloader
end

# ╔═╡ 16f6c9b5-8fa1-48c6-b6e6-945c620e5e9f
md"Accuracy score & confusion functions"

# ╔═╡ 1c16dae5-4697-49a1-8f53-2c8e46be32e2
accuracy(model, x, y) = mean(model(x) |> onecold .== y |> onecold)

# ╔═╡ 6e869cba-cc83-4d43-8505-acb342fc4a6e
md"**Notation:** The transpose of a matrix ```A``` is denoted ```transpose(A)``` or equivalently ```A'```."

# ╔═╡ 1673a7ff-bb0b-44da-ae4f-eb5b8bb6a174
confusion(model, x, y) = (ŷ = onehotbatch(model(x) |> onecold, 1:10); y * ŷ')

# ╔═╡ 154d9bba-ed7c-4973-8eca-3209f0947bdd
md"Building & training our model"

# ╔═╡ 39b3c4a2-e579-41da-9966-96eb88a51b83
function train(; kws...)
	args = Args(; kws...)

	device = args.usecuda && CUDA.functional() ? gpu : cpu

	trainloader, testloader = dataloaders(args)

	xtest, ytest = testloader.data

	m = Chain(flatten, Dense(28*28, 84, relu), Dense(84, 10), softmax) |> device

	ps = params(m)

	loss(x, y) = crossentropy(m(x), y)

	opt = ADAM(args.η)

	report(epoch) = @info "Epoch $epoch\n\tAccuracy: $(accuracy(m, xtest, ytest))"

	@info "Training started."
	report(0)
	for epoch ∈ 1:args.epochs
		for (x′, y′) ∈ trainloader
			x, y = device(x′), device(y′)
			gs = gradient(ps) do
				loss(x, y)
			end
			Flux.Optimise.update!(opt, ps, gs)
		end
		epoch % args.infotime == 0 && report(epoch)
	end

	return cpu(m), xtest, ytest
end

# ╔═╡ a521d498-9cac-41fa-a3a5-ab96b265833c
md"It trains pretty quickly, at least after the first time all of our functions compile or whatever. It's also pretty successful.

Let's check it out:"

# ╔═╡ 3ad28ff9-13f9-4308-873c-c49f5627d1e4
m, x, y = @time train();

# ╔═╡ 633ad30a-dc6d-475f-8675-484dabb9346a
accuracy(m, x, y)

# ╔═╡ 496099b6-3ee9-4a49-87b7-d4a6603963ad
confusion(m, x, y)

# ╔═╡ 51079ca9-984e-45db-a3df-0780932c2215
md"Visually compare the whole test set to our predictions"

# ╔═╡ 61c94951-876c-4218-a47b-7fdd6312916e
@bind N Slider(1:10000)

# ╔═╡ 26b9476a-7479-4098-a6f7-5c82295c3648
md"### Predicted: $(onecold(m(x)[:,N], 0:9)) | Actual: $(onecold(y[:,N], 0:9))"

# ╔═╡ 268b979f-f412-44e0-8b00-104a6a5c5c74
reverse(Gray.(x[:,:,N]), dims=2) |> rotl90

# ╔═╡ 306cd2ba-6839-45c1-9911-3394b6d07b52
md"Here's the thing though...

It's pretty clear to us that this random data isn't a number at all."

# ╔═╡ a791682e-51c2-45c3-b2d2-4a917b122849
randomimg = rand(Float32, 28, 28); Gray.(randomimg)

# ╔═╡ a612bb6b-148e-4826-82a2-b522a7c3e8f0
md"However, our model is relatively sure this random data is a $(onecold(randomimg[:,:,:] |> m, 0:9))..."

# ╔═╡ 7565201e-eadc-46f4-875f-b4cee3f34fc5
heatmap = Gray.(randomimg[:,:,:] |> m)

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"

[compat]
CUDA = "~3.5.0"
Flux = "~0.12.7"
Images = "~0.24.1"
MLDatasets = "~0.5.11"
PlutoUI = "~0.7.16"
StatsBase = "~0.33.12"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

[[AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.0.1"

[[AbstractTrees]]
git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.3.4"

[[Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"

[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[ArrayInterface]]
deps = ["Compat", "IfElse", "LinearAlgebra", "Requires", "SparseArrays", "Static"]
git-tree-sha1 = "1d6835607e9f214cb4210310868f8cf07eb0facc"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "3.1.34"

[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[AxisAlgorithms]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
git-tree-sha1 = "66771c8d21c8ff5e3a93379480a2307ac36863f7"
uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
version = "1.0.1"

[[AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "d127d5e4d86c7680b20c35d40b503c74b9a39b5e"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.4"

[[BFloat16s]]
deps = ["LinearAlgebra", "Printf", "Random", "Test"]
git-tree-sha1 = "a598ecb0d717092b5539dbbe890c98bac842b072"
uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b"
version = "0.2.0"

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[BinDeps]]
deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"]
git-tree-sha1 = "1289b57e8cf019aede076edab0587eb9644175bd"
uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee"
version = "1.0.2"

[[BinaryProvider]]
deps = ["Libdl", "Logging", "SHA"]
git-tree-sha1 = "ecdec412a9abc8db54c0efc5548c64dfce072058"
uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
version = "0.5.10"

[[Blosc]]
deps = ["Blosc_jll"]
git-tree-sha1 = "84cf7d0f8fd46ca6f1b3e0305b4b4a37afe50fd6"
uuid = "a74b3585-a348-5f62-a45c-50e91977d574"
version = "0.7.0"

[[Blosc_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Lz4_jll", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "e747dac84f39c62aff6956651ec359686490134e"
uuid = "0b7ba130-8d10-5ba8-a3d6-c5182647fed9"
version = "1.21.0+0"

[[BufferedStreams]]
deps = ["Compat", "Test"]
git-tree-sha1 = "5d55b9486590fdda5905c275bb21ce1f0754020f"
uuid = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d"
version = "1.0.0"

[[CEnum]]
git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.1"

[[CUDA]]
deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"]
git-tree-sha1 = "2c8329f16addffd09e6ca84c556e2185a4933c64"
uuid = "052768ef-5323-5732-b1bb-66c8b64840ba"
version = "3.5.0"

[[CatIndices]]
deps = ["CustomUnitRanges", "OffsetArrays"]
git-tree-sha1 = "a0f80a09780eed9b1d106a1bf62041c2efc995bc"
uuid = "aafaddc9-749c-510e-ac4f-586e18779b91"
version = "0.2.2"

[[ChainRules]]
deps = ["ChainRulesCore", "Compat", "LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "74c737978316e19e0706737542037c468b21a8d9"
uuid = "082447d4-558c-5d27-93f4-14fc19e9eca2"
version = "1.11.6"

[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "d9e40e3e370ee56c5b57e0db651d8f92bce98fea"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.10.1"

[[CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.0"

[[ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"

[[ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "45efb332df2e86f2cb2e992239b6267d97c9e0b6"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.7"

[[Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"

[[CommonSubexpressions]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
version = "0.3.0"

[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "31d0151f5716b655421d9d75b7fa74cc4e744df2"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.39.0"

[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"

[[ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"

[[Conda]]
deps = ["JSON", "VersionParsing"]
git-tree-sha1 = "299304989a5e6473d985212c28928899c74e9421"
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
version = "1.5.2"

[[CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "6d1c23e740a586955645500bbec662476204a52c"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.1"

[[CustomUnitRanges]]
git-tree-sha1 = "1a3f97f907e6dd8983b744d2642651bb162a3f7a"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.2"

[[DataAPI]]
git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.0"

[[DataDeps]]
deps = ["BinaryProvider", "HTTP", "Libdl", "Reexport", "SHA", "p7zip_jll"]
git-tree-sha1 = "4f0e41ff461d42cfc62ff0de4f1cd44c6e6b3771"
uuid = "124859b0-ceae-595e-8997-d05f6a7a8dfe"
version = "0.7.7"

[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.10"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"

[[DiffResults]]
deps = ["StaticArrays"]
git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805"
uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.0.3"

[[DiffRules]]
deps = ["NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "7220bc21c33e990c14f4a9a319b1d242ebc5b269"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
version = "1.3.1"

[[Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "09d9eaef9ef719d2cd5d928a191dc95be2ec8059"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.5"

[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.8.5"

[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

[[EllipsisNotation]]
deps = ["ArrayInterface"]
git-tree-sha1 = "8041575f021cba5a099a456b4163c9a08b566a02"
uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949"
version = "1.1.0"

[[ExprTools]]
git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92"
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
version = "0.1.6"

[[FFTViews]]
deps = ["CustomUnitRanges", "FFTW"]
git-tree-sha1 = "cbdf14d1e8c7c8aacbe8b19862e0179fd08321c2"
uuid = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
version = "0.3.2"

[[FFTW]]
deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
git-tree-sha1 = "463cb335fa22c4ebacfd1faba5fde14edb80d96c"
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
version = "1.4.5"

[[FFTW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea"
uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
version = "3.3.10+0"

[[FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "3c041d2ac0a52a12a27af2782b34900d9c3ee68c"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.11.1"

[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "8756f9935b7ccc9064c6eef0bff0ad643df733a3"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.7"

[[FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"

[[Flux]]
deps = ["AbstractTrees", "Adapt", "ArrayInterface", "CUDA", "CodecZlib", "Colors", "DelimitedFiles", "Functors", "Juno", "LinearAlgebra", "MacroTools", "NNlib", "NNlibCUDA", "Pkg", "Printf", "Random", "Reexport", "SHA", "SparseArrays", "Statistics", "StatsBase", "Test", "ZipFile", "Zygote"]
git-tree-sha1 = "e4ade0790850bb16b5309945658fa4e7626226f1"
uuid = "587475ba-b771-5e3f-ad9e-33799f191a9c"
version = "0.12.7"

[[ForwardDiff]]
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
git-tree-sha1 = "63777916efbcb0ab6173d09a658fb7f2783de485"
uuid = "f6369f11-7733-5829-9624-2563aa707210"
version = "0.10.21"

[[Functors]]
git-tree-sha1 = "e4768c3b7f597d5a352afa09874d16e3c3f6ead2"
uuid = "d9f16b24-f501-4c13-a1f2-28368ffc5196"
version = "0.2.7"

[[GPUArrays]]
deps = ["Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"]
git-tree-sha1 = "7772508f17f1d482fe0df72cabc5b55bec06bbe0"
uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7"
version = "8.1.2"

[[GPUCompiler]]
deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"]
git-tree-sha1 = "ba475ea91facd7bde9f0f113f60e975882b4f5ca"
uuid = "61eb1bfa-7361-4325-ad38-22787b887f55"
version = "0.13.6"

[[GZip]]
deps = ["Libdl"]
git-tree-sha1 = "039be665faf0b8ae36e089cd694233f5dee3f7d6"
uuid = "92fee26a-97fe-5a0c-ad85-20a5f3185b63"
version = "0.5.1"

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "1c5a84319923bea76fa145d49e93aa4394c73fc2"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.1"

[[HDF5]]
deps = ["Blosc", "Compat", "HDF5_jll", "Libdl", "Mmap", "Random", "Requires"]
git-tree-sha1 = "83173193dc242ce4b037f0263a7cc45afb5a0b85"
uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
version = "0.15.6"

[[HDF5_jll]]
deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "fd83fa0bde42e01952757f01149dd968c06c4dba"
uuid = "0234f1f7-429e-5d53-9886-15a909be8d59"
version = "1.12.0+1"

[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
git-tree-sha1 = "14eece7a3308b4d8be910e265c724a6ba51a9798"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.16"

[[Hyperscript]]
deps = ["Test"]
git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
version = "0.0.4"

[[HypertextLiteral]]
git-tree-sha1 = "f6532909bf3d40b308a0f360b6a0e626c0e263a8"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.1"

[[IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[IRTools]]
deps = ["InteractiveUtils", "MacroTools", "Test"]
git-tree-sha1 = "95215cd0076a150ef46ff7928892bc341864c73c"
uuid = "7869d1d1-7146-5819-86e3-90919afe41df"
version = "0.4.3"

[[IdentityRanges]]
deps = ["OffsetArrays"]
git-tree-sha1 = "be8fcd695c4da16a1d6d0cd213cb88090a150e3b"
uuid = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
version = "0.3.1"

[[IfElse]]
git-tree-sha1 = "28e837ff3e7a6c3cdb252ce49fb412c8eb3caeef"
uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
version = "0.1.0"

[[ImageAxes]]
deps = ["AxisArrays", "ImageCore", "Reexport", "SimpleTraits"]
git-tree-sha1 = "794ad1d922c432082bc1aaa9fa8ffbd1fe74e621"
uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
version = "0.6.9"

[[ImageContrastAdjustment]]
deps = ["ColorVectorSpace", "ImageCore", "ImageTransformations", "Parameters"]
git-tree-sha1 = "2e6084db6cccab11fe0bc3e4130bd3d117092ed9"
uuid = "f332f351-ec65-5f6a-b3d1-319c6670881a"
version = "0.3.7"

[[ImageCore]]
deps = ["AbstractFFTs", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"]
git-tree-sha1 = "db645f20b59f060d8cfae696bc9538d13fd86416"
uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534"
version = "0.8.22"

[[ImageDistances]]
deps = ["ColorVectorSpace", "Distances", "ImageCore", "ImageMorphology", "LinearAlgebra", "Statistics"]
git-tree-sha1 = "6378c34a3c3a216235210d19b9f495ecfff2f85f"
uuid = "51556ac3-7006-55f5-8cb3-34580c88182d"
version = "0.2.13"

[[ImageFiltering]]
deps = ["CatIndices", "ColorVectorSpace", "ComputationalResources", "DataStructures", "FFTViews", "FFTW", "ImageCore", "LinearAlgebra", "OffsetArrays", "Requires", "SparseArrays", "StaticArrays", "Statistics", "TiledIteration"]
git-tree-sha1 = "bf96839133212d3eff4a1c3a80c57abc7cfbf0ce"
uuid = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
version = "0.6.21"

[[ImageIO]]
deps = ["FileIO", "Netpbm", "OpenEXR", "PNGFiles", "TiffImages", "UUIDs"]
git-tree-sha1 = "a2951c93684551467265e0e32b577914f69532be"
uuid = "82e4d734-157c-48bb-816b-45c225c6df19"
version = "0.5.9"

[[ImageMagick]]
deps = ["FileIO", "ImageCore", "ImageMagick_jll", "InteractiveUtils", "Libdl", "Pkg", "Random"]
git-tree-sha1 = "5bc1cb62e0c5f1005868358db0692c994c3a13c6"
uuid = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
version = "1.2.1"

[[ImageMagick_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "ea2b6fd947cdfc43c6b8c15cff982533ec1f72cd"
uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
version = "6.9.12+0"

[[ImageMetadata]]
deps = ["AxisArrays", "ColorVectorSpace", "ImageAxes", "ImageCore", "IndirectArrays"]
git-tree-sha1 = "ae76038347dc4edcdb06b541595268fca65b6a42"
uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
version = "0.9.5"

[[ImageMorphology]]
deps = ["ColorVectorSpace", "ImageCore", "LinearAlgebra", "TiledIteration"]
git-tree-sha1 = "68e7cbcd7dfaa3c2f74b0a8ab3066f5de8f2b71d"
uuid = "787d08f9-d448-5407-9aad-5290dd7ab264"
version = "0.2.11"

[[ImageQualityIndexes]]
deps = ["ColorVectorSpace", "ImageCore", "ImageDistances", "ImageFiltering", "OffsetArrays", "Statistics"]
git-tree-sha1 = "1198f85fa2481a3bb94bf937495ba1916f12b533"
uuid = "2996bd0c-7a13-11e9-2da2-2f5ce47296a9"
version = "0.2.2"

[[ImageShow]]
deps = ["Base64", "FileIO", "ImageCore", "OffsetArrays", "Requires", "StackViews"]
git-tree-sha1 = "832abfd709fa436a562db47fd8e81377f72b01f9"
uuid = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
version = "0.3.1"

[[ImageTransformations]]
deps = ["AxisAlgorithms", "ColorVectorSpace", "CoordinateTransformations", "IdentityRanges", "ImageCore", "Interpolations", "OffsetArrays", "Rotations", "StaticArrays"]
git-tree-sha1 = "e4cc551e4295a5c96545bb3083058c24b78d4cf0"
uuid = "02fcd773-0e25-5acc-982a-7f6622650795"
version = "0.8.13"

[[Images]]
deps = ["AxisArrays", "Base64", "ColorVectorSpace", "FileIO", "Graphics", "ImageAxes", "ImageContrastAdjustment", "ImageCore", "ImageDistances", "ImageFiltering", "ImageIO", "ImageMagick", "ImageMetadata", "ImageMorphology", "ImageQualityIndexes", "ImageShow", "ImageTransformations", "IndirectArrays", "OffsetArrays", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "TiledIteration"]
git-tree-sha1 = "8b714d5e11c91a0d945717430ec20f9251af4bd2"
uuid = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
version = "0.24.1"

[[Imath_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "87f7662e03a649cffa2e05bf19c303e168732d3e"
uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1"
version = "3.1.2+0"

[[IndirectArrays]]
git-tree-sha1 = "c2a145a145dc03a7620af1444e0264ef907bd44f"
uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
version = "0.5.1"

[[Inflate]]
git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c"
uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
version = "0.1.2"

[[IniFile]]
deps = ["Test"]
git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.0"

[[IntelOpenMP_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c"
uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
version = "2018.0.3+2"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[Interpolations]]
deps = ["AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"]
git-tree-sha1 = "61aa005707ea2cebf47c8d780da8dc9bc4e0c512"
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
version = "0.13.4"

[[IntervalSets]]
deps = ["Dates", "EllipsisNotation", "Statistics"]
git-tree-sha1 = "3cc368af3f110a767ac786560045dceddfc16758"
uuid = "8197267c-284f-5f27-9208-e0e47529a953"
version = "0.5.3"

[[InverseFunctions]]
deps = ["Test"]
git-tree-sha1 = "f0c6489b12d28fb4c2103073ec7452f3423bd308"
uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
version = "0.1.1"

[[IrrationalConstants]]
git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.1"

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.0"

[[JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.0+0"

[[Juno]]
deps = ["Base64", "Logging", "Media", "Profile"]
git-tree-sha1 = "07cb43290a840908a771552911a6274bc6c072c7"
uuid = "e5e0dc1b-0480-54bc-9374-aad01c23163d"
version = "0.8.4"

[[LLVM]]
deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"]
git-tree-sha1 = "46092047ca4edc10720ecab437c42283cd7c44f3"
uuid = "929cbde3-209d-540e-8aea-75f648917ca0"
version = "4.6.0"

[[LLVMExtra_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "6a2af408fe809c4f1a54d2b3f188fdd3698549d6"
uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab"
version = "0.0.11+0"

[[LazyArtifacts]]
deps = ["Artifacts", "Pkg"]
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"

[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.3.0+0"

[[LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "6193c3815f13ba1b78a51ce391db8be016ae9214"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.4"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[Lz4_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "5d494bc6e85c4c9b626ee0cab05daa4085486ab1"
uuid = "5ced341a-0733-55b8-9ab6-a4889d929147"
version = "1.9.3+0"

[[MAT]]
deps = ["BufferedStreams", "CodecZlib", "HDF5", "SparseArrays"]
git-tree-sha1 = "5c62992f3d46b8dce69bdd234279bb5a369db7d5"
uuid = "23992714-dd62-5051-b70f-ba57cb901cac"
version = "0.10.1"

[[MKL_jll]]
deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"]
git-tree-sha1 = "5455aef09b40e5020e1520f551fa3135040d4ed0"
uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
version = "2021.1.1+2"

[[MLDatasets]]
deps = ["BinDeps", "ColorTypes", "DataDeps", "DelimitedFiles", "FixedPointNumbers", "GZip", "MAT", "PyCall", "Requires"]
git-tree-sha1 = "0e9a2eb57eee5e21a4f1503bb63d152376ff90d2"
uuid = "eb30cadb-4394-5ae3-aed4-317e484a6458"
version = "0.5.11"

[[MacroTools]]
deps = ["Markdown", "Random"]
git-tree-sha1 = "5a5bc6bf062f0f95e62d0fe0a2d99699fed82dd9"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.8"

[[MappedArrays]]
git-tree-sha1 = "e8b359ef06ec72e8c030463fe02efe5527ee5142"
uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
version = "0.4.1"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.0.3"

[[MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"

[[Media]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "75a54abd10709c01f1b86b84ec225d26e840ed58"
uuid = "e89f7d12-3494-54d1-8411-f7d8b9ae1f27"
version = "0.5.0"

[[Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[MosaicViews]]
deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"]
git-tree-sha1 = "b34e3bc3ca7c94914418637cb10cc4d1d80d877d"
uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389"
version = "0.3.3"

[[MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"

[[NNlib]]
deps = ["Adapt", "ChainRulesCore", "Compat", "LinearAlgebra", "Pkg", "Requires", "Statistics"]
git-tree-sha1 = "5203a4532ad28c44f82c76634ad621d7c90abcbd"
uuid = "872c559c-99b0-510c-b3b7-b6c96a88d5cd"
version = "0.7.29"

[[NNlibCUDA]]
deps = ["CUDA", "LinearAlgebra", "NNlib", "Random", "Statistics"]
git-tree-sha1 = "04490d5e7570c038b1cb0f5c3627597181cc15a9"
uuid = "a00861dc-f156-4864-bf3c-e6376f28a68d"
version = "0.1.9"

[[NaNMath]]
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
version = "0.3.5"

[[Netpbm]]
deps = ["ColorVectorSpace", "FileIO", "ImageCore"]
git-tree-sha1 = "09589171688f0039f13ebe0fdcc7288f50228b52"
uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
version = "1.0.1"

[[NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"

[[OffsetArrays]]
deps = ["Adapt"]
git-tree-sha1 = "c0e9e582987d36d5a61e650e6e543b9e44d9914b"
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
version = "1.10.7"

[[OpenEXR]]
deps = ["Colors", "FileIO", "OpenEXR_jll"]
git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633"
uuid = "52e1d378-f018-4a11-a4be-720524705ac7"
version = "0.3.2"

[[OpenEXR_jll]]
deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "923319661e9a22712f24596ce81c54fc0366f304"
uuid = "18a262bb-aa17-5467-a713-aee519bc75cb"
version = "3.1.1+0"

[[OpenLibm_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+0"

[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.5+0"

[[OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[PNGFiles]]
deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"]
git-tree-sha1 = "85e3436b18980e47604dd0909e37e2f066f54398"
uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
version = "0.3.10"

[[PaddedViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "646eed6f6a5d8df6708f15ea7e02a7a2c4fe4800"
uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
version = "0.5.10"

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.3"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "f19e978f81eca5fd7620650d7dbea58f825802ee"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.1.0"

[[Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[PkgVersion]]
deps = ["Pkg"]
git-tree-sha1 = "a7a7e1a88853564e551e4eba8650f8c38df79b37"
uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
version = "0.1.1"

[[PlutoUI]]
deps = ["Base64", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "4c8a7d080daca18545c56f1cac28710c362478f3"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.16"

[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[Profile]]
deps = ["Printf"]
uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"

[[ProgressMeter]]
deps = ["Distributed", "Printf"]
git-tree-sha1 = "afadeba63d90ff223a6a48d2009434ecee2ec9e8"
uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
version = "1.7.1"

[[PyCall]]
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
git-tree-sha1 = "169bb8ea6b1b143c5cf57df6d34d022a7b60c6db"
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
version = "1.92.3"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[Random123]]
deps = ["Libdl", "Random", "RandomNumbers"]
git-tree-sha1 = "0e8b146557ad1c6deb1367655e052276690e71a3"
uuid = "74087812-796a-5b5d-8853-05524746bad3"
version = "1.4.2"

[[RandomNumbers]]
deps = ["Random", "Requires"]
git-tree-sha1 = "043da614cc7e95c703498a491e2c21f58a2b8111"
uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143"
version = "1.5.3"

[[RangeArrays]]
git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
version = "0.3.2"

[[Ratios]]
deps = ["Requires"]
git-tree-sha1 = "01d341f502250e81f6fec0afe662aa861392a3aa"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.2"

[[Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.1.3"

[[Rotations]]
deps = ["LinearAlgebra", "StaticArrays", "Statistics"]
git-tree-sha1 = "2ed8d8a16d703f900168822d83699b8c3c1a5cd8"
uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
version = "1.0.2"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[SharedArrays]]
deps = ["Distributed", "Mmap", "Random", "Serialization"]
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"

[[SimpleTraits]]
deps = ["InteractiveUtils", "MacroTools"]
git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
version = "0.9.4"

[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[SortingAlgorithms]]
deps = ["DataStructures"]
git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508"
uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
version = "1.0.1"

[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[SpecialFunctions]]
deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
git-tree-sha1 = "2d57e14cd614083f132b6224874296287bfa3979"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.8.0"

[[StackViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c"
uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15"
version = "0.1.1"

[[Static]]
deps = ["IfElse"]
git-tree-sha1 = "a8f30abc7c64a39d389680b74e749cf33f872a70"
uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
version = "0.3.3"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "3c76dde64d03699e074ac02eb2e8ba8254d428da"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.13"

[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[[StatsAPI]]
git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.0.0"

[[StatsBase]]
deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "eb35dcc66558b2dda84079b9a1be17557d32091a"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.12"

[[TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[[Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"

[[TensorCore]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
version = "0.1.1"

[[Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[TiffImages]]
deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"]
git-tree-sha1 = "016185e1a16c1bd83a4352b19a3b136224f22e38"
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
version = "0.5.1"

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "5683455224ba92ef59db72d10690690f4a8dc297"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.1"

[[TimerOutputs]]
deps = ["ExprTools", "Printf"]
git-tree-sha1 = "7cb456f358e8f9d102a8b25e8dfedf58fa5689bc"
uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
version = "0.5.13"

[[TranscodingStreams]]
deps = ["Random", "Test"]
git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c"
uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.6"

[[URIParser]]
deps = ["Unicode"]
git-tree-sha1 = "53a9f49546b8d2dd2e688d216421d050c9a31d0d"
uuid = "30578b45-9adc-5946-b283-645ec420af67"
version = "0.4.1"

[[URIs]]
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.3.0"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[VersionParsing]]
git-tree-sha1 = "80229be1f670524750d905f8fc8148e5a8c4537f"
uuid = "81def892-9a0e-5fdd-b105-ffc91e053289"
version = "1.2.0"

[[WoodburyMatrices]]
deps = ["LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "9398e8fefd83bde121d5127114bd3b6762c764a6"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "0.5.4"

[[ZipFile]]
deps = ["Libdl", "Printf", "Zlib_jll"]
git-tree-sha1 = "3593e69e469d2111389a9bd06bac1f3d730ac6de"
uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"
version = "0.9.4"

[[Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"

[[Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.0+0"

[[Zygote]]
deps = ["AbstractFFTs", "ChainRules", "ChainRulesCore", "DiffRules", "Distributed", "FillArrays", "ForwardDiff", "IRTools", "InteractiveUtils", "LinearAlgebra", "MacroTools", "NaNMath", "Random", "Requires", "SpecialFunctions", "Statistics", "ZygoteRules"]
git-tree-sha1 = "0fc9959bcabc4668c403810b4e851f6b8962eac9"
uuid = "e88e6eb3-aa80-5325-afca-941959d7151f"
version = "0.6.29"

[[ZygoteRules]]
deps = ["MacroTools"]
git-tree-sha1 = "8c1a8e4dfacb1fd631745552c8db35d0deb09ea0"
uuid = "700de1a5-db45-46bc-99cf-38207098b444"
version = "0.2.2"

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"

[[p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
"""

# ╔═╡ Cell order:
# ╟─1ffe6485-1787-4366-b46c-d5e7809474f4
# ╠═ea8e44f2-3308-11ec-37a1-57fa7c71b851
# ╠═fe9e97e7-0fd2-47ce-9dd8-fb557eb5343c
# ╠═ed838cf8-e927-4508-b999-ced96a2e7942
# ╠═fb4b1563-5a04-47b4-bf14-72849c21cb8b
# ╠═02a76384-3e59-436b-966f-90140b5f226c
# ╠═f39297bf-aeec-4a3d-a5b7-8daf91851af8
# ╟─1ff19be0-3259-4f27-9e96-05908ae2f9b5
# ╠═c2156f38-3399-4ac7-9562-250c478f24be
# ╟─d9c2df85-b291-45d5-ad90-beed17fc6548
# ╠═897718a3-e665-489d-b320-529f2f512df8
# ╟─342cd3af-09bf-477a-bf5e-f502d30cc07c
# ╠═a085723a-3676-40b4-a87c-67bcdd51b7dc
# ╟─16f6c9b5-8fa1-48c6-b6e6-945c620e5e9f
# ╠═1c16dae5-4697-49a1-8f53-2c8e46be32e2
# ╟─6e869cba-cc83-4d43-8505-acb342fc4a6e
# ╠═1673a7ff-bb0b-44da-ae4f-eb5b8bb6a174
# ╟─154d9bba-ed7c-4973-8eca-3209f0947bdd
# ╠═39b3c4a2-e579-41da-9966-96eb88a51b83
# ╟─a521d498-9cac-41fa-a3a5-ab96b265833c
# ╠═3ad28ff9-13f9-4308-873c-c49f5627d1e4
# ╠═633ad30a-dc6d-475f-8675-484dabb9346a
# ╠═496099b6-3ee9-4a49-87b7-d4a6603963ad
# ╟─51079ca9-984e-45db-a3df-0780932c2215
# ╟─61c94951-876c-4218-a47b-7fdd6312916e
# ╟─26b9476a-7479-4098-a6f7-5c82295c3648
# ╠═268b979f-f412-44e0-8b00-104a6a5c5c74
# ╟─306cd2ba-6839-45c1-9911-3394b6d07b52
# ╠═a791682e-51c2-45c3-b2d2-4a917b122849
# ╟─a612bb6b-148e-4826-82a2-b522a7c3e8f0
# ╠═7565201e-eadc-46f4-875f-b4cee3f34fc5
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

max_stars_repo_name: benharmonics/ModelZoo

max_issues_repo_name: benharmonics/ModelZoo

max_forks_repo_name: benharmonics/ModelZoo


text:
#
# Compute Polar Decomposition via SVD
#

immutable SVDAlg <: PolarAlg end

function solve!{T}(alg::SVDAlg,
                X::Matrix{T}, U::Matrix{T}, H::Matrix{T})

    F = svdfact(X, thin = true)
    PQt = Array(T, size(U))

    A_mul_Bt!(PQt, F[:U], F[:V])
    copy!(U, PQt)
    copy!(H, F[:V] * diagm(F[:S]) * F[:Vt])
    H = 0.5 * (H + H')

    return SVDResult{T}(U, H)
end


text:
# Chemfiles.jl, a modern library for chemistry file reading and writing
# Copyright (C) Guillaume Fraux and contributors -- BSD license

export ChemfilesError

struct ChemfilesError <: Exception
    message::AbstractString
end

Base.show(io::IO, e::ChemfilesError) = show(io, "Chemfiles error: $(e.message)")

"""
Get the last error message from the chemfiles runtime.
"""
function last_error()
    unsafe_string(lib.chfl_last_error())
end

"""
Clear any error messages stored by the chemfiles runtime.
"""
function clear_errors()
    __check(lib.chfl_clear_errors())
end

# Default to the standard logging infrastructure
function __default_warning_callback(message::String)
    @warn "$message"
end

# Store the warning callback in a global to prevent garbage collection
WARNING_CALLBACK = nothing

function _warning_callback_adaptator(message)
    try
        WARNING_CALLBACK(unsafe_string(message))
    catch error
        @warn "caught $error in warning callback"
    end
end

"""
Set the global warning `callback` to be used for each warning event.

The `callback` function must take a `String` and return nothing.
"""
function set_warning_callback(callback::Function)
    global WARNING_CALLBACK
    WARNING_CALLBACK = callback
    ptr = @cfunction(_warning_callback_adaptator, Cvoid, (Ptr{UInt8},))
    __check(lib.chfl_set_warning_callback(ptr))
end

"""
Read configuration data from the file at `path`.

By default, chemfiles reads configuration from any file name `.chemfilesrc`
in the current directory or any parent directory. This function can be used
to add data from another configuration file.

This function will fail if there is no file at `path`, or if the file is
incorectly formatted. Data from the new configuration file will overwrite
any existing data.
"""
function add_configuration(path::String)
    __check(lib.chfl_add_configuration(pointer(path)))
end

"""
Metadata associated with one of the format Chemfiles can read/write

$(TYPEDFIELDS)
"""
struct FormatMetadata
    """Name of the format"""
    name::String
    """Extension associated with the format, or `nothing` if there is no extension
    associated."""
    extension::Union{String,Nothing}
    """Extended user-facing description of the format"""
    description::String
    """URL pointing to the format definition/reference"""
    reference::String

    """Is reading files in this format implemented?"""
    read::Bool
    """Is writing files in this format implemented?"""
    write::Bool
    """Does this format support in-memory IO?"""
    memory::Bool

    """Does this format support storing atomic positions?"""
    positions::Bool
    """Does this format support storing atomic velocities?"""
    velocities::Bool
    """Does this format support storing unit cell information?"""
    unit_cell::Bool
    """Does this format support storing atom names or types?"""
    atoms::Bool
    """Does this format support storing bonds between atoms?"""
    bonds::Bool
    """Does this format support storing residues?"""
    residues::Bool
end

function Base.show(io::IO, metadata::FormatMetadata)
    print(io, "FormatMetadata(")
    print(io, "name=\"$(metadata.name)\", ")

    if metadata.extension === nothing
        print(io, "extension=nothing, ")
    else
        print(io, "extension=\"$(metadata.extension)\", ")
    end
    print(io, "description=\"$(metadata.description)\", ")
    print(io, "reference=\"$(metadata.reference)\", ")

    print(io, "read=$(metadata.read), "),
    print(io, "write=$(metadata.write), "),
    print(io, "memory=$(metadata.memory), "),
    print(io, "positions=$(metadata.positions), "),
    print(io, "velocities=$(metadata.velocities), "),
    print(io, "unit_cell=$(metadata.unit_cell), "),
    print(io, "atoms=$(metadata.atoms), "),
    print(io, "bonds=$(metadata.bonds), "),
    print(io, "residues=$(metadata.residues)"),

    print(io, ")")
end

function FormatMetadata(metadata::lib.chfl_format_metadata)
    name = unsafe_string(metadata.name)
    if metadata.extension == C_NULL
        extension = nothing
    else
        extension = unsafe_string(metadata.extension)
    end
    description = unsafe_string(metadata.description)
    reference = unsafe_string(metadata.reference)

    return FormatMetadata(
        name,
        extension,
        description,
        reference,
        convert(Bool, metadata.read[]),
        convert(Bool, metadata.write[]),
        convert(Bool, metadata.memory[]),
        convert(Bool, metadata.positions[]),
        convert(Bool, metadata.velocities[]),
        convert(Bool, metadata.unit_cell[]),
        convert(Bool, metadata.atoms[]),
        convert(Bool, metadata.bonds[]),
        convert(Bool, metadata.residues[]),
    )
end

"""
Get the full list of formats supported by Chemfiles, and associated metadata
"""
function format_list()
    metadata = Ref{Ptr{lib.chfl_format_metadata}}()
    count = Ref{UInt64}(0)
    lib.chfl_formats_list(metadata, count)

    all = FormatMetadata[]
    for i in 1:count[]
        push!(all, FormatMetadata(unsafe_load(metadata[], i)))
    end
    lib.chfl_free(Ptr{Cvoid}(metadata[]))
    return all
end

"""
Get the format that chemfiles would use to read a file at the given `path`.

The format is mostly guessed from the path extension, chemfiles only tries to
read the file to distinguish between CIF and mmCIF files. Opening the file using
the returned format string might still fail. For example, it will fail if the
file is not actually formatted according to the guessed format; or the
format/compression combination is not supported (e.g. `XTC / GZ` will not work
since the XTC reader does not support compressed files).

The returned format is represented in a way compatible with the various
`Trajectory` constructors, i.e. `"<format name> [/ <compression>]"`, where
compression is optional.
"""
function guess_format(path::String)
    buffer = repeat("\0", 64)
    lib.chfl_guess_format(pointer(path), pointer(buffer), UInt64(length(buffer)))
    return __strip_null(buffer)
end


text:
module Transport
"""
"""

using ArchGDAL

const agd = ArchGDAL

                      # top          bottom          q                      phead
function run_transport( aquifer_top, aquifer_bottom, concentration_sources, piezomatic_head, status, source, srs, tensor_x::Real, tensor_y::Real, porosity::Real, dispersiveness_t::Real,
                        dispersiveness_l::Real, storativity::Real=0.0001, break_error::Real=0.000001, delay::Real=1.0, time::Integer, resolution::Integer, output_path::AbstractString=".\\" )

 #= E' UN RASTER MA LO PONE A ZERO
    messaggio_q=''
    if self.text_q!='No file':
        self.q=self.listalayers[self.text_q]
        messaggio_q="Mappa concentration sources and skins: "+str(self.q)+"\n"
        if not self.q.isValid():
            QMessageBox.warning(self,"Warning", u"Il file raster concentration sources and skins non è valido" )
            return
    else:
        self.q=0.0
 =#

  #= COSA SONO QUESTI PARAMETRI
    self.text_campophead = str(self.combo_campophead.currentText())
    self.text_campostatus = str(self.combo_campostatus.currentText())
    self.text_solver = str(self.combo_solver.currentText())
    self.text_conc = str(self.combo_conc.currentText())
 =#

    src_geom = agd.getgeom(collect(agd.getlayer(source, 0))[1])

    if agd.geomdim(source) != 0
        throw(DomainError(source, "`source` must be a point"))
    end

   time *= 3600

 """ PRINT DI COSE
    messaggio+=messaggio_q
    messaggio+=messaggio_mappatop
    messaggio+=messaggio_mappabottom
    # messaggio+=messaggio_status
    messaggio+="VARIABILI:\n"
    messaggio+=u"Concentrazione inquinante: da shapefile"
    messaggio+=u"Solver: "+str(self.combo_solver.currentText())+"\n"
    messaggio+=messaggio_top
    messaggio+=messaggio_bottom
    messaggio+='ALGORITMO UTILIZZATO: modello realizzato sfruttando r.gwflow e r.solute.transport di Grass GIS v.7 (Neteler and Mitasova, 2008). Per i dettagli matematici degli algoritmi vedere https://grass.osgeo.org/gdp/hydrology/gebbert2007_diplom_stroemung_grass_gis.pdf\n\n'
 """

   # start_time = time.time()

   self.areastudio_path=self.areastudio.dataProvider().dataSourceUri().split('|')
   self.phead_path=self.phead.dataProvider().dataSourceUri().split('|')
   self.source_path=self.source.dataProvider().dataSourceUri().split('|')
   self.status_path=self.status.dataProvider().dataSourceUri().split('|')

   #g.proj -c epsg=3003
   run_command("g.proj", epsg=self.myepsg, flags = 'c')

   # Carica e trasforma in raster "area"
    # Inporta "areastudio" come vettoriale
   run_command("v.in.ogr", input=self.areastudio_path[0], output="areastudio",overwrite=True, flags = 'o')
    # Definisce la regione di "areastudio"
   run_command("g.region", res=self.res, vector="areastudio")
    # Trasforma "areastudio" in raster
   run_command("v.to.rast", input="areastudio", output="areastudio",overwrite=True, use = 'val')

 """ Carica e trasforma in raster il vettore di "top"
  # Inporta "phead" come vettoriale
    run_command("v.in.ogr", input=self.phead_path[0], output="phead",overwrite=True, flags = 'o')
  # Trasforma "phead" in raster
    run_command("v.to.rast", input="phead", output="phead",overwrite=True, use = 'attr', attribute_column=self.text_campophead)
 """
   pheadband = agd.getband(piezomatic_head, 1)

 """ Carica e trasforma in raster "status"
    run_command("v.in.ogr", input=self.status_path[0], output="status",overwrite=True, flags = 'o')
    run_command("v.to.rast", input="status", output="status",overwrite=True, use = 'attr', attribute_column=self.text_campostatus)
 """
   statusband = agd.getband(status, 1)

 """ Carica source lo trasforma in raster e setta la cella corrispondente a 0/null
  # Inporta "source" come vettoriale
    run_command("v.in.ogr", input=self.source_path[0], output="source",overwrite=True, flags = 'o')
  # Trasforma "source" in raster
    run_command("v.to.rast", input="source", output="source",overwrite=True, use = 'attr', attribute_column=self.text_conc)
  # Rimpiazza i null value in "source" con 0
    run_command("r.null", map="source", null=0)
 """
   x_source = agd.getx(src_geom, 0)
   y_source = agd.gety(src_geom, 0)
   r_source, c_source = toIndexes(dtm, x_source, y_source)

 """ CREDO STIA SOLO SETTANDO DEI VALORI
    ex_hydx="hydcondx = "+'{0:.10f}'.format(self.tensorx)
    ex_hydy="hydcondy = "+'{0:.10f}'.format(self.tensory)
    run_command("r.mapcalc", expression=ex_hydx,overwrite=True)
    run_command("r.mapcalc", expression=ex_hydy,overwrite=True)
    run_command("r.mapcalc", expression="recharge = 0",overwrite=True)

    run_command("r.mapcalc", expression="top_conf = "+str(self.top),overwrite=True)
    run_command("r.mapcalc", expression="bottom = "+str(self.bottom),overwrite=True)

    run_command("r.mapcalc", expression="poros = "+str(self.porosity),overwrite=True)
    run_command("r.mapcalc", expression="syield = "+str(self.storativity),overwrite=True)
    run_command("r.mapcalc", expression="null = 0.0",overwrite=True)
    run_command("r.mapcalc", expression="cs = 0.0",overwrite=True)
    run_command("r.mapcalc", expression="diff = 0.0000001",overwrite=True)
    run_command("r.mapcalc", expression="R = "+str(self.delay),overwrite=True)
 """

   # create solv les
    # setup
   res = cg( phead, status )

    run_command("r.gwflow", solver="cg", top="top_conf", bottom="bottom", phead="phead",\
      status="status", hc_x="hydcondx", hc_y="hydcondy", q="well", s="syield",\
      recharge="recharge", output="gwresult_conf", dt=self.time, type="confined",overwrite=True)

    run_command("r.solute.transport", solver=self.text_solver, top="top_conf",\
      bottom="bottom", phead="gwresult_conf", status="status", hc_x="hydcondx", hc_y="hydcondy",\
      rd="R", cs="cs", q="well", nf="poros", output="stresult_conf", dt=self.time, diff_x="diff",\
      diff_y="diff", c="source", al=self.dispersiveness_l, at=self.dispersiveness_t,overwrite=True)

    run_command("r.out.gdal", input="gwresult_conf", output=self.path_output+"/gwflow.tif",overwrite=True,format = 'GTiff')
    run_command("r.out.gdal", input="stresult_conf", output=self.path_output+"/solute.tif",overwrite=True,format = 'GTiff')

    base_gwflow_name=os.path.basename(self.path_output+"/gwflow.tif")
    gwflow_name=os.path.splitext(base_gwflow_name)[0]
    gwflow_file=self.iface.addRasterLayer(self.path_output+"/gwflow.tif", gwflow_name)

    base_solute_name=os.path.basename(self.path_output+"/solute.tif")
    solute_name=os.path.splitext(base_solute_name)[0]
    solute_file=self.iface.addRasterLayer(self.path_output+"/solute.tif", solute_name)
    # max_progress=rows*cols
    # self.progressBar.setMaximum(max_progress)

    #   tempoanalisi=time.time() - start_time

end

end # module

max_stars_repo_name: DavideFavaro/Tirocinio

max_issues_repo_name: DavideFavaro/Tirocinio

max_forks_repo_name: DavideFavaro/Tirocinio


text:
asvec(mask, x::AbstractVec) = x
asvec(mask, x) = vect(ntuple(_ -> x, length(mask))...)

function spmdmap(mask, f, args...)
  vargs = map(x -> asvec(mask, x), args)
  vect(map((m, a...) -> m ? f(a...) : nothing, mask, vargs...)...)
end

@spmd println(xs...) = spmdmap(__mask__, println, xs...)
@spmd print(xs...) = spmdmap(__mask__, print, xs...)
@spmd repr(xs...) = spmdmap(__mask__, repr, xs...)

@spmd function iterate(iter::AbstractVec{T,N}) where {T, N}
  splat_and_vect(xs) = vect(xs...)
  res = map(zip(__mask__, iter)) do x
    if x[1]
      iterate(x[2])
    else
      nothing
    end
  end |> splat_and_vect

  # println(res)
  res
end

@spmd function iterate(iter::AbstractVec{T,N}, state::AbstractVec{S,N}) where {T, S, N}
  splat_and_vect(xs) = vect(xs...)
  res = map(zip(__mask__, iter, state)) do x
    if x[1]
      iterate(x[2], x[3])
    else
      nothing
    end
  end |> splat_and_vect

  # println(res)
  res
end

function put_zero_value(mask, xs::Union{Nothing, T}...) where {T}
  not_nothing_indeces = findall(x->x!=nothing, xs)
  if length(not_nothing_indeces) == 0
    return xs
  end
  map(x->x==nothing ? zero(xs[not_nothing_indeces[1]]) : x,xs)
end

function masked_getfield(mask, xs::Vec{T,N}, names) where {T, N}
  splat_and_vect(xs) = vect(put_zero_value(mask, xs...)...)
  map(zip(mask, xs, names)) do (m, x, name)
    if m
      getfield(x, name)
    else
      nothing
    end
  end |> splat_and_vect
end

function masked_getindex(mask, xs::Vec{T,N}, indices) where {T, N}
  splat_and_vect(xs) = vect(put_zero_value(mask, xs...)...)
  map(zip(mask, xs, indices)) do (m, x, index)
    if m
      getindex(x, index)
    else
      nothing
    end
  end |> splat_and_vect
end

@spmd function getfield(x::AbstractVec{T,N}, name::S) where {S,T,N}
  vect(getfield.(x,name)...)
end
@spmd getfield(x::AbstractVec{T,N}, name::AbstractVec{S,N}) where {S,T,N} = vect(getfield.(x, name)...)

@spmd getfield(x::Vec{T,N}, name::S) where {S,T,N} = begin println(name); masked_getfield(__mask__, x, Vec{S,N}(name)) end
@spmd getfield(x::Vec{T,N}, name::Vec{S,N}) where {S,T,N} = masked_getfield(__mask__, x, name)
@spmd length(x::Vec{T,N}) where {T <: AbstractArray, N} = vect(length.(data(x))...)
@spmd getindex(x::Vec{T,N}, index::S) where {T <: AbstractArray, N, S} = masked_getindex(__mask__, x, Vec{S,N}(index))


text:
"""
GAR fileformat
See EUROCONTROL NEST Manual for Gar fileformat description

Dictionary with point list (latitude + longitude [deg]) for each airblock ID (key)
"""
module Gar

export read

using Format
using CSV

const fileformat = Dict(1=>String, 2=>String, 3=>String)

const header_format = ["AorP", "LAT_DEG", "LON_DEG"]

function read(file)
    csviterator = CSV.File(file, delim=";", types=fileformat, header=header_format,
    datarow=2)
    return splitintodict(csviterator)
end

function splitintodict(iterator)
    dict = Dict{String, Matrix{Float64}}()
    points = Matrix{Float64}(undef, 0, 2)
    j = 1
    n_points = 1000
    airblockname = ""
    for (i, row) in enumerate(iterator)
        if row.AorP == "P"
            points = vcat(points,[parse(Float64, row.LAT_DEG) parse(Float64, row.LON_DEG)])
            if j == n_points
                dict[airblockname] = points
            end
            j += 1
        elseif row.AorP == "A"
            airblockname = row.LAT_DEG
            n_points = parse(Int64, row.LON_DEG)
            points = Matrix{Float64}(undef, 0, 2)
            j = 1
        else
        end
    end
    return dict
end

end # module


text:
using Combinatorics
open("load_combos.sh", "w") do f
    for (a,b,c,d) in permutations(ARGS)
        write(f, "julia --project -e 'using $a; using $b; using $c; using $d' && \\\n")
    end
    write(f, "echo -e \\\\nDone!\n")
end