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

using Markdown
using InteractiveUtils

# ╔═╡ d7d4b845-aa4a-4474-a37a-50802a45e6ce
import Pkg

# ╔═╡ c93316ed-62b4-4183-a824-c45d2be09dff
module MyModule
    using Hygienic

    module_value = :magic_module_value
    macro my_macro(x)
        @hygienize quote
            $(esc(x)) = $(QuoteNode(module_value))
        end
    end
end

# ╔═╡ af463c18-e808-4228-af5b-5ae4b49cac98
read("../README.md", String) |> Markdown.parse

# ╔═╡ ee64697c-fc22-466d-9249-43e720eb4fe0
md"""
## Pluto package manager tricks for this test notebook
"""

# ╔═╡ 0bd507f0-a183-11ec-2252-3fd74991bba4
begin
	import Pluto: ReactiveNode, ExpressionExplorer
	import PlutoTest, Test
	import PlutoLinks: @revise
end

# ╔═╡ 52448f61-0ef7-4ac9-909d-2ca3e2841851
begin
	# we use the pkg_str macro here to keep using the Pluto package manager.
	Pkg.pkg"develop .."

	@revise using Hygienic
end

# ╔═╡ edc472b6-be97-403b-95df-0e24b1f3b320
md"""
## A Simple testset implementation

While waiting for [PlutoTest#17](https://github.com/JuliaPluto/PlutoTest.jl).
"""

# ╔═╡ 191512e4-c25f-4b35-b5ed-17943f5f0c54
macro dummy_testset(name, block)
	results = []
	map!(block.args, block.args) do ex
		if !Meta.isexpr(ex, :macrocall) || ex.args[1] != Symbol("@test")
			return ex
		end
		new_name = gensym(:result)
		push!(results, new_name)
		Expr(:local, Expr(:(=), new_name, ex))
	end
	quote
		$(esc(block))
		TestSet($(esc(name)), $(esc(Expr(:vect, results...))))
	end
end

# ╔═╡ 8c0869cd-a59c-4d92-99f7-c18a9752a002
struct TestSet
	name::String

	results::Vector
end

# ╔═╡ c2833c1d-7c48-4dd1-97cd-fbb3457a34d6
function Base.show(io::IO, m::MIME"text/html", ts::TestSet)
	text = md"""
	###### $(ts.name)
	"""
	show(io, m, text)
	for result in ts.results
		show(io, m, result)
	end
end

# ╔═╡ f1e61118-d1ad-4c05-9c24-6b10102084f0
md"""
## Assign the macros depending on whether or not we are in Pluto or not

The PlutoTest test checks for `isdefined(Main, :PlutoRunner)` which is the case in this test case because we are using Pluto inside the tests.
"""

# ╔═╡ 0be72453-abc1-46e5-aac4-c41cf99f731f
is_pluto = startswith(string(nameof(@__MODULE__)), "workspace#")

# ╔═╡ acbf4573-3f77-4a7a-8945-0403ee7daa2e
var"@testset" = is_pluto ? var"@dummy_testset" : Test.var"@testset"

# ╔═╡ ccbdb97b-bd12-4040-969b-c62c8169903c
var"@test" = is_pluto ?
	PlutoTest.var"@test" : Test.var"@test"

# ╔═╡ 3522296b-adaf-41a0-abe2-8256ab27115b
@dummy_testset "A Test set" begin
	@test 1 + 1 == 2
    @test -cos(π) == 1.
end

# ╔═╡ 87d5721d-7909-464e-932d-b8beb105ca23
md"""
## The actual tests
"""

# ╔═╡ 8b1bd9e5-2783-4bdc-9976-3bf4355ab31f
rnode = ReactiveNode ∘ ExpressionExplorer.try_compute_symbolreferences;

# ╔═╡ 611b1029-7beb-48db-a888-239fe3b80e95
@testset "Test 1" begin
	@test :x ∉ rnode(@hygienize quote
		x = 1
	end).definitions
end

# ╔═╡ 115ccb93-5a61-420c-b88d-2c1261b4eb85
@testset "Another one" begin
	@test :x ∉ rnode(@hygienize quote
		x = 1
	end).references
end

# ╔═╡ bebf115e-fa32-4bc0-84c6-a1244c606b27
@testset "Test 2" begin
	@test :y ∈ rnode(@hygienize quote
		x = 1
		y + x
	end).references
end

# ╔═╡ 5d9caed8-1860-4c58-a09d-1191ae8374f7
macro a()
    @hygienize quote
        x = :a
    end
end

# ╔═╡ 58e3db66-d015-4adf-a36e-cee2852c90c3
macro b()
    quote
        x = :b
        @a()
        if x == :a
            error("x is :a")
        end
        x
    end
end

# ╔═╡ 38fcaa73-8644-4eb0-ada6-6148dd0144f5
@testset "Macro stacking" begin
	@test @b() == :b
end

# ╔═╡ 53496afd-2e53-451a-a2f2-65b2c956dfdb
let
    @MyModule.my_macro(x)

    @testset "Macro interpolation" begin
		@test x == :magic_module_value
	end
end

# ╔═╡ 95c9aaef-4e46-405e-9fef-ee1507c35dff
let
   ex = @hygienize quote
	  x, y = t
	  x + y
   end
	x, y = ex.args[2].args[1].args

   node = rnode(ex)
   @testset "Simple expr" begin
	   @test x ∈ node.definitions
	   @test x ∉ node.references
	   @test y ∈ node.definitions
	   @test y ∉ node.references
   end
end

# ╔═╡ 60198b32-c2c5-4d61-9aad-dffb58b8ed41
let
	local ex = @hygienize quote
		x = 1
		x + y
	end
	x, y = ex.args[4].args[2:end]
	node = rnode(ex)

	@testset "Another simple expr" begin
		@test x ∈ node.definitions
		@test :y ∈ node.references
		@test y == :y
	end
end

# ╔═╡ 192ece6e-18f9-4538-a9ed-840f8c7a7fae
md"""
### Utils
"""

# ╔═╡ 40e5018b-cef4-4dbb-8d92-3b7b5fa6bac9
"""
	unsymify(s::Symbol)

Turn a gensymed symbol into its unsymed counterpart.

```julia
unsymify(gensym(:x)) == :x
```
"""
function unsymify(s::Symbol)
	s = string(s)[3:end]
	split(s, "#") |> first |> Symbol
end

# ╔═╡ 8a56cbc4-ba35-41a3-b25d-69b9595dbddb
let
	@testset "Macro Call" begin
		local ex = @hygienize quote
			state, set_state = blahblah()
			@use_effect([state,]) do
				set_state(y)
			end
			set_state(state)
		end

		@test ex.args[2].args[1].args[1] |> unsymify == :state
		@test ex.args[2].args[1].args[2] |> unsymify == :set_state
		@test ex.args[end].args[1] |> unsymify == :set_state
		@test ex.args[end].args[2] |> unsymify == :state
	end
end

# ╔═╡ 33a54530-7219-44c2-8f8d-f1afe295517b
@testset "Selector" begin
	local ex = @hygienize quote
		A = blahblah()

		A.B.C
		B.C.D
	end

	@test ex.args[end-2].args[1].args[1] |> unsymify == :A
	@test ex.args[end].args[1].args[1] == :B
end

# ╔═╡ e033d308-070e-4114-8ad2-b3eb5a6929de
@testset "Vect/Tuple" begin
	local ex = @hygienize quote
		x, y, z = 1:3

		[x,y,z]
		(x,y,z)
	end

	@test ex.args[4].args .|> unsymify == [:x, :y, :z]
	@test ex.args[end].args .|> unsymify == [:x, :y, :z]
end

# ╔═╡ e967a1f8-9fe4-4c5f-94f8-bed03ca8046a
@testset "Unsymify" begin
	@test unsymify(gensym(:x)) == :x
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Hygienic = "60a53d29-03fa-4035-8e80-3746437aa372"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
PlutoLinks = "0ff47ea0-7a50-410d-8455-4348d5de0420"
PlutoTest = "cb4044da-4d16-4ffa-a6a3-8cad7f73ebdc"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
Hygienic = "~0.0.1"
Pluto = "~0.18.1"
PlutoLinks = "~0.1.5"
PlutoTest = "~0.2.2"
"""

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

julia_version = "1.7.1"
manifest_format = "2.0"

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

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

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

[[deps.CodeTracking]]
deps = ["InteractiveUtils", "UUIDs"]
git-tree-sha1 = "759a12cefe1cd1bb49e477bc3702287521797483"
uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
version = "1.0.7"

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

[[deps.Configurations]]
deps = ["ExproniconLite", "OrderedCollections", "TOML"]
git-tree-sha1 = "ab9b7c51e8acdd20c769bccde050b5615921c533"
uuid = "5218b696-f38b-4ac9-8b61-a12ec717816d"
version = "0.17.3"

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

[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

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

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

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

[[deps.ExproniconLite]]
git-tree-sha1 = "8b08cc88844e4d01db5a2405a08e9178e19e479e"
uuid = "55351af7-c7e9-48d6-89ff-24e801d99491"
version = "0.6.13"

[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"

[[deps.FuzzyCompletions]]
deps = ["REPL"]
git-tree-sha1 = "efd6c064e15e92fcce436977c825d2117bf8ce76"
uuid = "fb4132e2-a121-4a70-b8a1-d5b831dcdcc2"
version = "0.5.0"

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

[[deps.Hygienic]]
path = "../../home/paul/Projects/Hygienic.jl"
uuid = "60a53d29-03fa-4035-8e80-3746437aa372"
version = "0.0.1"

[[deps.HypertextLiteral]]
git-tree-sha1 = "2b078b5a615c6c0396c77810d92ee8c6f470d238"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.3"

[[deps.IniFile]]
git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.1"

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

[[deps.IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[deps.JuliaInterpreter]]
deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"]
git-tree-sha1 = "007ab1efbda85da785caf1943d401a6e7556fc9a"
uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
version = "0.9.9"

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

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

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

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

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

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

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

[[deps.LoweredCodeUtils]]
deps = ["JuliaInterpreter"]
git-tree-sha1 = "6b0440822974cab904c8b14d79743565140567f6"
uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b"
version = "2.2.1"

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

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

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

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

[[deps.MsgPack]]
deps = ["Serialization"]
git-tree-sha1 = "a8cbf066b54d793b9a48c5daa5d586cf2b5bd43d"
uuid = "99f44e22-a591-53d1-9472-aa23ef4bd671"
version = "1.1.0"

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

[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"

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

[[deps.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"

[[deps.Pluto]]
deps = ["Base64", "Configurations", "Dates", "Distributed", "FileWatching", "FuzzyCompletions", "HTTP", "InteractiveUtils", "Logging", "Markdown", "MsgPack", "Pkg", "REPL", "RelocatableFolders", "Sockets", "Tables", "UUIDs"]
git-tree-sha1 = "c97f4548e903d132342a6f5998554f507d0b2578"
uuid = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
version = "0.18.1"

[[deps.PlutoHooks]]
deps = ["InteractiveUtils", "Markdown", "UUIDs"]
git-tree-sha1 = "072cdf20c9b0507fdd977d7d246d90030609674b"
uuid = "0ff47ea0-7a50-410d-8455-4348d5de0774"
version = "0.0.5"

[[deps.PlutoLinks]]
deps = ["FileWatching", "InteractiveUtils", "Markdown", "PlutoHooks", "Revise", "UUIDs"]
git-tree-sha1 = "0e8bcc235ec8367a8e9648d48325ff00e4b0a545"
uuid = "0ff47ea0-7a50-410d-8455-4348d5de0420"
version = "0.1.5"

[[deps.PlutoTest]]
deps = ["HypertextLiteral", "InteractiveUtils", "Markdown", "Test"]
git-tree-sha1 = "17aa9b81106e661cffa1c4c36c17ee1c50a86eda"
uuid = "cb4044da-4d16-4ffa-a6a3-8cad7f73ebdc"
version = "0.2.2"

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

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

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

[[deps.RelocatableFolders]]
deps = ["SHA", "Scratch"]
git-tree-sha1 = "cdbd3b1338c72ce29d9584fdbe9e9b70eeb5adca"
uuid = "05181044-ff0b-4ac5-8273-598c1e38db00"
version = "0.1.3"

[[deps.Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.3.0"

[[deps.Revise]]
deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"]
git-tree-sha1 = "4d4239e93531ac3e7ca7e339f15978d0b5149d03"
uuid = "295af30f-e4ad-537b-8983-00126c2a3abe"
version = "3.3.3"

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

[[deps.Scratch]]
deps = ["Dates"]
git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda"
uuid = "6c6a2e73-6563-6170-7368-637461726353"
version = "1.1.0"

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

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

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

[[deps.TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[deps.Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"]
git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.7.0"

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

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

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

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

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

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

[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl", "OpenBLAS_jll"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"

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

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

# ╔═╡ Cell order:
# ╟─af463c18-e808-4228-af5b-5ae4b49cac98
# ╟─ee64697c-fc22-466d-9249-43e720eb4fe0
# ╠═d7d4b845-aa4a-4474-a37a-50802a45e6ce
# ╠═0bd507f0-a183-11ec-2252-3fd74991bba4
# ╠═52448f61-0ef7-4ac9-909d-2ca3e2841851
# ╟─edc472b6-be97-403b-95df-0e24b1f3b320
# ╠═191512e4-c25f-4b35-b5ed-17943f5f0c54
# ╟─3522296b-adaf-41a0-abe2-8256ab27115b
# ╠═8c0869cd-a59c-4d92-99f7-c18a9752a002
# ╠═c2833c1d-7c48-4dd1-97cd-fbb3457a34d6
# ╟─f1e61118-d1ad-4c05-9c24-6b10102084f0
# ╠═0be72453-abc1-46e5-aac4-c41cf99f731f
# ╠═acbf4573-3f77-4a7a-8945-0403ee7daa2e
# ╠═ccbdb97b-bd12-4040-969b-c62c8169903c
# ╟─87d5721d-7909-464e-932d-b8beb105ca23
# ╠═8b1bd9e5-2783-4bdc-9976-3bf4355ab31f
# ╟─611b1029-7beb-48db-a888-239fe3b80e95
# ╟─115ccb93-5a61-420c-b88d-2c1261b4eb85
# ╟─bebf115e-fa32-4bc0-84c6-a1244c606b27
# ╠═5d9caed8-1860-4c58-a09d-1191ae8374f7
# ╠═58e3db66-d015-4adf-a36e-cee2852c90c3
# ╟─38fcaa73-8644-4eb0-ada6-6148dd0144f5
# ╠═c93316ed-62b4-4183-a824-c45d2be09dff
# ╟─53496afd-2e53-451a-a2f2-65b2c956dfdb
# ╟─95c9aaef-4e46-405e-9fef-ee1507c35dff
# ╟─60198b32-c2c5-4d61-9aad-dffb58b8ed41
# ╟─8a56cbc4-ba35-41a3-b25d-69b9595dbddb
# ╟─33a54530-7219-44c2-8f8d-f1afe295517b
# ╟─e033d308-070e-4114-8ad2-b3eb5a6929de
# ╟─192ece6e-18f9-4538-a9ed-840f8c7a7fae
# ╟─40e5018b-cef4-4dbb-8d92-3b7b5fa6bac9
# ╟─e967a1f8-9fe4-4c5f-94f8-bed03ca8046a
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
## Double Pendulum
# TODO test
import TrajectoryOptimization
traj_folder = joinpath(dirname(pathof(TrajectoryOptimization)),"..")
urdf_folder = joinpath(traj_folder, "dynamics/urdf")
urdf_doublependulum = joinpath(urdf_folder, "doublependulum.urdf")
#isfile(urdf_doublependulum)

model = Model(urdf_doublependulum)
n = model.n; # dimensions of system
m = model.m; # dimensions of control

# initial and goal states
x0 = [0.;0.;0.;0.]
xf = [pi;0.;0.;0.]

# costs
Q = 0.0001*Diagonal(I,n)
Qf = 250.0*Diagonal(I,n)
R = 0.0001*Diagonal(I,m)

# simulation
tf = 5.0

obj = LQRObjective(Q,R,Qf,tf,x0,xf)
doublependulum = [model,obj]

ext: jl


text:
# Satisfy column-indexable AbstractTable Query interface

AbstractTables.default(::Table) = Table()


text:
@testset "erf" begin
    for T in (Float32,Float64)
        @test isnan(Libm.erf(T(NaN)))
        @test Libm.erf(T(Inf)) == 1
        @test Libm.erf(T(-Inf)) == -1
        s = linspace(T(-0.84375),T(0.84375),100)
        @test_approx_eq Base.erf.(s) Libm.erf.(s)
        s = linspace(T(-2e-28),T(2e-28),100)
        @test_approx_eq Base.erf.(s) Libm.erf.(s)
        s = linspace(T(-0.84375),T(0.84375),100)
        @test_approx_eq Base.erf.(s) Libm.erf.(s)
        s = linspace(T(0.84375), T(6), 100)
        @test_approx_eq Base.erf.(s) Libm.erf.(s)
        s = linspace(T(-6),T(-0.84375), 100);
        @test_approx_eq Base.erf.(s) Libm.erf.(s)
    end
end

@testset "erfc" begin
    for T in (Float32,Float64)
        @test isnan(Libm.erfc(T(NaN)))
        @test Libm.erfc(T(Inf)) == 0
        @test Libm.erfc(T(-Inf)) == 2
        s = linspace(T(-0.84375),T(0.84375),100)
        @test_approx_eq Base.erfc.(s) Libm.erfc.(s)
        s = linspace(T(-2e-56),T(2e-56),100)
        @test_approx_eq Base.erfc.(s) Libm.erfc.(s)
        s = linspace(T(-0.25),T(0.25),100)
        @test_approx_eq Base.erfc.(s) Libm.erfc.(s)
        s = linspace(T(0.84375), T(28), 100)
        @test_approx_eq Base.erfc.(s) Libm.erfc.(s)
        s = linspace(T(-28),T(-0.84375), 100);
        @test_approx_eq Base.erfc.(s) Libm.erfc.(s)
    end
end


text:
export Entity, update!
export ECS, XYZ, accumulate_XYZ, get_entity_row, get_entity_by_id
export get_entity_row_by_id, get_df_row_prop, set_df_row_prop!
export ECSIterator, ECSIteratorState, Level
export Root, instantiate!, destroy!
export ecs, lvl

abstract type Entity <: System end

awake!(e::Entity) = true
shutdown!(e::Entity) = false
update!(e, Δ) = nothing

# magic symbols that getproperty and setproperty! (and end users) care about
const ENT = :ent
const TYPE = :type
const ID = :id
const PARENT = :parent
const CHILDREN = :children
const POSITION = :pos
const ROTATION = :rot
const ABSOLUTE_POSITION = :abs_pos
const ABSOLUTE_ROTATION = :abs_rot
const ACTIVE = :active
const HIDDEN = :hidden
const PROPS = :props

# position, rotation, velocity, acceleration, whatever
mutable struct XYZ
  x::Number
  y::Number
  z::Number
  XYZ(x=0, y=0, z=0) = new(x, y, z)
end

import Base.+, Base.-, Base.*, Base.÷, Base./, Base.%
+(a::XYZ, b::XYZ) = XYZ(a.x+b.x,a.y+b.y,a.z+b.z)
-(a::XYZ, b::XYZ) = XYZ(a.x-b.x,a.y-b.y,a.z-b.z)
*(a::XYZ, b::Number) = XYZ(a.x*b,a.y*b,a.z*b)
÷(a::XYZ, b::Number) = XYZ(a.x÷b,a.y÷b,a.z÷b)
/(a::XYZ, b::Number) = XYZ(a.x/b,a.y/b,a.z/b)
%(a::XYZ, b::Number) = XYZ(a.x%b,a.y%b,a.z%b)

# columns of the in-memory database
const components = Dict(
  ENT=>Entity,
  TYPE=>DataType,
  ID=>Int,
  PARENT=>Int,
  CHILDREN=>Set{Int},
  POSITION=>XYZ,
  ROTATION=>XYZ,
  ACTIVE=>Bool,
  HIDDEN=>Bool,
  PROPS=>Dict{Symbol, Any}
)

mutable struct ECS <: System
  df::DataFrame
  awoken::Bool
  function ECS()
    df = DataFrame(
      NamedTuple{Tuple(keys(components))}(
        t[] for t in values(components)
      ))
    return new(df, false)
  end
end

const ecs = ECS()

# internally uses Base.getproperty directly so
# as to not break if the symbol values change
get_entity_row(ent::Entity) = @view ecs.df[getproperty(ecs.df, ENT) .== [ent], :]
get_entity_by_id(id::Int) = ecs.df[getproperty(ecs.df, ID) .== [id], ENT][1]
get_entity_row_by_id(id::Int) = @view ecs.df[getproperty(ecs.df, ID) .== [id], :]
get_df_row_prop(r, s) = r[!, s][1]
set_df_row_prop!(r, s, x) = r[!, s][1] = x

function Base.propertynames(ent::Entity)
  return (
    ENT,
    TYPE,
    ID,
    CHILDREN,
    PARENT,
    POSITION,
    ROTATION,
    ABSOLUTE_POSITION,
    ABSOLUTE_ROTATION,
    ACTIVE,
    HIDDEN,
    PROPS,
    [n for n in keys(getproperty(ent, PROPS))]...
  )
end

function Base.hasproperty(ent::Entity, s::Symbol)
  return s in Base.propertynames(ent)
end

function accumulate_XYZ(r, s)
  acc = XYZ()
  while true
    inc = get_df_row_prop(r, s)
    acc += inc
    r = get_entity_row_by_id(get_df_row_prop(r, PARENT))
    get_df_row_prop(r, PARENT) != 0 || return acc
  end
end

function Base.getproperty(ent::Entity, s::Symbol)
  e = get_entity_row(ent)
  if s == ABSOLUTE_POSITION return accumulate_XYZ(e, POSITION)
  elseif s == ABSOLUTE_ROTATION return accumulate_XYZ(e, ROTATION)
  elseif s in keys(components) return get_df_row_prop(e, s)
  elseif s in keys(get_df_row_prop(e, PROPS)) return get_df_row_prop(e, PROPS)[s]
  else return getfield(ent, s)
  end
end

const ecs_lock = ReentrantLock()

function Base.setproperty!(ent::Entity, s::Symbol, x)
  e = get_entity_row(ent)
  if s in [
    ENT, # immutable
    TYPE, # automatically set
    ID, # automatically set
    ABSOLUTE_POSITION, # computed
    ABSOLUTE_ROTATION # computed
  ]
    error("cannot set property $(s) on Entity")
  end

  lock(ecs_lock)
  if s == PARENT
    par = get_entity_by_id(get_df_row_prop(e, PARENT))
    push!(getproperty(par, CHILDREN), get_df_row_prop(e, ID))
  elseif s in keys(components) && s != PROPS
    set_df_row_prop!(e, s, x)
  else
    get_df_row_prop(e, PROPS)[s] = x
  end
  unlock(ecs_lock)
end

Base.length(e::ECS) = size(e.df)[1]

# can define multiple iteration types using
# multiple dispatch thanks to all the global
# constants, and by defining them as structs
# rather than enums we can pass arbitrary
# parameters to the iterator
abstract type ECSIterator <: System end

# refers to tree level, i.e. breadth-first,
# nothing special to see here
struct Level <: ECSIterator end
const lvl = Level()
Base.length(l::Level) = length(ecs)

mutable struct ECSIteratorState
  root::Int
  q::Queue{Int}
  root_visited::Bool
  index::Int
  ECSIteratorState(; root=0, q=Queue{Int}(), root_visited=false, index=1) = new(root, q, root_visited, index)
end

function Base.iterate(l::Level, state::ECSIteratorState=ECSIteratorState())
  if isempty(state.q)
    if !state.root_visited # just started
      enqueue!(state.q, state.root)
      state.root_visited = true
    else # just finished
      return nothing
    end
  end

  ent = get_entity_by_id(dequeue!(state.q))

  for c in getproperty(ent, CHILDREN)
    enqueue!(state.q, c)
  end

  return (ent, state)
end

listenFor(ecs, TICK)

function handleMessage(e::ECS, m::TICK)
  @debug "ECS tick"
  function _update!(ent::Entity)
    if getproperty(ent, ACTIVE) update!(ent, m.Δ) end
  end
  try
    map(_update!, lvl) # TODO investigate parallelization
  catch
    handleException()
  end
end

awake!(e::ECS) = e.awoken = all(map(awake!, lvl))
shutdown!(e::ECS) = e.awoken = all(map(shutdown!, lvl))

next_id = 0

function instantiate!(e::Entity; kw...)

  lock(ecs_lock)

  global next_id
  id = next_id
  next_id += 1

  # update ecs
  # allows invalid parents and children for now
  push!(ecs.df, Dict(
    ENT=>e,
    TYPE=>typeof(e),
    ID=>id,
    CHILDREN=>get(kw, :children, Set{Int}()),
    PARENT=>get(kw, :pid, 0),
    POSITION=>get(kw, :pos, XYZ()),
    ROTATION=>get(kw, :rot, XYZ()),
    ACTIVE=>get(kw, :active, true),
    HIDDEN=>get(kw, :hidden, false),
    PROPS=>merge(get(kw, :props, Dict{Symbol, Any}()),
      Dict(k=>v for (k,v) in kw if k ∉
      [:children, :pid, :pos, :rot, :active, :hidden, :props]))
  ))

  if id != 0 # root has no parent but itself
    par = get_entity_by_id(get(kw, :pid, 0))
    push!(getproperty(par, CHILDREN), id)
  end

  unlock(ecs_lock)

  if ecs.awoken awake!(e) end

  return e
end

function destroy!(e::Entity)
  shutdown!(e)

  lock(ecs_lock)

  p = get_entity_by_id(getproperty(e, PARENT))

  # if not root
  if getproperty(p, ID) != getproperty(e, ID)
    # update parent
    delete!(getproperty(p, CHILDREN), getproperty(e, ID))
    # update dataframe
    deleteat!(ecs.df, getproperty(ecs.df, ENT) .== [e])
  end

  unlock(ecs_lock)
end

function destroy!(es...)
  map(destroy!, es) # TODO investigate parallelization
end

mutable struct Root <: Entity end # mutate at your own peril
# also, user can define update!(r::Root, Δ) if they want

# root pid is 0 (default) indicating "here and no further",
# instantiate in library because always needed, also it will
# always have id of 0, technically parent of itself
instantiate!(Root())


text:
using Base.Meta
using Plots
using BenchmarkTools
using Statistics
using DataFrames

using Test

# Needed to override operator on Approximation{T} type.
import Base.-
import Base.+
import Base./
import Base.*
import Base.convert

greet() = print("Welcome to AppoximateComputations!\n
This is a growing project as part of work ongoing at the DTG Research Group of Cambridge University's Computer Laboratory.
We hope to grow this package with many usable implementations of approximate software techniques.
This package is maintained by Nicholas Timmons under an MIT license.
Any queries please feel free to get in contact by email: ngt26 at cam.ac.uk\n We hope this package is useful!")

struct ErrorResultsContainer
    absDif::Array{Float64,1}
    absError::Float64
    meanDif::Float64
    medianDif::Float64
    benchmarkRes::Tuple{BenchmarkTools.Trial, BenchmarkTools.Trial}
end

struct GeneratedFunctionType
    generatedFunction::Any
    fitType::String
    precision::Any
    order::Any
    errordata::ErrorResultsContainer
    targetFunctionName::Symbol
    range::Tuple{Float64, Float64}
	textstring::String
end

function GetAbsoluteError(arr::GeneratedFunctionType)        arr.errordata.absError;                      end
function GetMeanDifference(arr::GeneratedFunctionType)       arr.errordata.meanDif;                       end
function GetMedianDifference(arr::GeneratedFunctionType)     arr.errordata.medianDif;                     end
function GetMedianBenchmarkTime(arr::GeneratedFunctionType)  median(arr.errordata.benchmarkRes[2].times); end
function GetMeanBenchmarkTime(arr::GeneratedFunctionType)    mean(arr.errordata.benchmarkRes[2].times);   end

#Struct wrapper definition
struct Approximation{T}
    value::T
end

#Function to convert out of approximation space back to fixed precision space
Get(x::Approximation{T}) where {T} = x.value
Get(x) where {T} = x

# Mathematics with an approximation type will return an approximation type
+(x::Approximation{T}, y) where T<:Number = Approximation(+(x.value, y))
-(x::Approximation{T}, y) where T<:Number = Approximation(-(x.value, y))
*(x::Approximation{T}, y) where T<:Number = Approximation(*(x.value, y))
/(x::Approximation{T}, y) where T<:Number = Approximation(/(x.value, y))

## Approximation Tests
#a = Approximation(6.0)
#b = 4.0
#
#@test_throws MethodError c = a - b
#@test_throws MethodError c = a + b
#@test_throws MethodError c = a * b
#@test_throws MethodError c = a / b
#
#@test typeof(convert(Float32, a)) == Float32
#@test typeof(convert(Float64, a)) == Float64

# From CurveFit.jl (and slightly tweaked). CurveFit.jl is currently not upgraded to Julia 1.0
#=
Copyright (c) 2014: Paulo Jabardo.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=#
function polyfit(x, y, order::Integer)
    inputCount = length(x)
    A = ones(eltype(x), inputCount, order+1)
    for i in 1:order
        for k in 1:inputCount
            A[k,i+1] = A[k,i] * x[k]
        end
    end
    A \ y
end

function pateLinearRationalFit(x::AbstractVector{T}, y::AbstractVector{T}, p, q) where T<:Number
    n = size(x,1)
    A = zeros(T, n, q+p+1)
    for i = 1:n
        A[i,1] = one(T)
        for k = 1:p
            A[i,k+1] = x[i]^k
        end
        for k = 1:q
            A[i, p+1+k] = -y[i] * x[i]^k
        end
    end

    A \ y
end

## End CurveFit.jl functions

function GetAbsoluteError(inputDiff)
   dif = abs.(inputDiff)
   sum = Base.sum(dif,dims=1)[1,1]
end

function GetAbsoluteError(input0, input1)
   dif = abs.(input0-input1)
   sum = Base.sum(dif,dims=1)[1,1]
end

function PadArray(input, length)
    arr = zeros(length)
    len = size(input)[1]
    for i in 1:len
        arr[i] = input[i]
    end
    arr
end

function GetErrorDataForFunction(targetFunction, generatedFunction, inputs)
    # Test compile to ensure it is "warmed up"
    # Alternatively could use precompile(f, args::Tuple{Vararg{Any}})
    for i in 0.0f0:0.1f0:3.140f0
        yRes = generatedFunction(i)
    end

    targetRes       = targetFunction.(inputs)
    generatedRes    = Get.(generatedFunction.(inputs))

    absDif          = abs.(generatedRes-targetRes)
    meanDif         = mean(absDif)
    medianDif       = median(absDif)

    absError        = GetAbsoluteError(targetRes, generatedRes)
    normabsError    = meanDif

    targetBench     = @benchmark $targetFunction.(convert(Array{Float64}, $inputs))
    generatedBench  = @benchmark $generatedFunction.($inputs)

    ErrorResultsContainer(absDif, absError, meanDif, medianDif, (targetBench, generatedBench))
end

function TestFunction(fn, targetfn, inputs64)
   float64letFunctionData      = GetErrorDataForFunction(targetfn, fn, inputs64)
end

function CreateDataFrameRow(functionData::GeneratedFunctionType)
    DataFrame( Name      = functionData.targetFunctionName,
               Precision = functionData.precision,
               FitType   = functionData.fitType,
               Order     = functionData.order,
               abserror  = functionData.errordata.absError,
               mean      = functionData.errordata.meanDif,
               median    = functionData.errordata.medianDif,
               benchmark = functionData.errordata.benchmarkRes[2],
               range     = functionData.range)
end

function GetFunctionName(approxData::GeneratedFunctionType)
    string(approxData.targetFunctionName,"_",approxData.fitType,approxData.order,"_", approxData.precision,"bit")
end

function FilterFunctionList(arr;
                            filterType = Any,
                            maxMeanError   = 99999.0 , minMeanError   = 0.0,
                            maxMedianError = 99999.0 , minMedianError = 0.0,
                            maxMeanTime    = 999999.0, minMeanTime    = 0.0,
                            maxMedianTime  = 999999.0, minMedianTime  = 0.0)
    filteredList = []
    for entry in arr
        if(  filterType == Any || (entry.precision == filterType))
            if(GetMedianDifference(entry) > minMedianError)
               if( GetMedianDifference(entry) < maxMedianError)
                    if( GetMeanDifference(entry) < maxMeanError)
                        if(GetMeanDifference(entry) > minMeanError)
                            if(GetMeanBenchmarkTime(entry) < maxMeanTime)
                                if(GetMeanBenchmarkTime(entry) > minMeanTime)
                                    if(GetMedianBenchmarkTime(entry) < maxMedianTime)
                                        if(GetMedianBenchmarkTime(entry) > minMedianTime)
                                            push!(filteredList, entry)
                                        end
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    filteredList
end

function GetFastestAcceptable(arr; targetMode="Time", meanErrorLimit=99999.0, maxTimeLimit=999999.0, verbose=true)
    printText(x) = verbose ? println(x) : nothing

    # Filter out results which have too much error or take too long
    shortlist = FilterFunctionList(arr, maxMeanError=meanErrorLimit, maxMedianTime=maxTimeLimit)

    # Select the most optimal function to choose, based on the current mode.
    if(targetMode == "Time")
        minTimeEntry = 0
        minTime      = 999999999999
        printText("Selecting Function with minimum time...")
        for entry in shortlist
            t = median(entry.errordata.benchmarkRes[2].times)
            if( t < minTime)
                minTimeEntry = entry
                minTime = t
                printText(string("Current Best:",GetFunctionName(minTimeEntry)))
            end
        end
        return minTimeEntry
    elseif(targetMode == "ErrorMean")
        minErrorEntry = 0
        minError      = 999999999999

        for entry in shortlist
            e =(entry.errordata.meanDif)
            if( e < minError)
                minErrorEntry = entry
                minError = e
            end
        end
        return minErrorEntry
    end

    return nothing
end

#### Plotting functions for generated function data structures
#### ==========================================================
# Plots the returned value for each input.
function PlotApproximationFunctionResults(arr, inputs, targetFunctionResults; filterType = Any, maxMeanError = 99999.0, minMeanError = 0.0, maxMedianError = 99999.0, minMedianError = 0.0  ) #
    p = plot(inputs, targetFunctionResults, label="Target")
    for func in arr
        testRes = func.generatedFunction.(inputs)

        if( (func.errordata.medianDif <= maxMedianError) && (func.errordata.medianDif >= minMedianError))
        if( (func.errordata.meanDif   <= maxMeanError)   && (func.errordata.meanDif   >= minMeanError))
        if(  filterType == Any                           || (func.precision           == filterType))
            plot!(inputs, Get.(testRes), label=string(func.fitType, "_", func.precision, "_", string(func.order)))
        end
        end
        end
    end
    p
end

# Plots the error relative to the high-resolution implementation
function PlotApproximationFunctionDiff(arr, inputs; filterType = Any, maxMeanError = 99999.0, minMeanError = 0.0, maxMedianError = 99999.0, minMedianError = 0.0 ) #
    p = plot()
    for func in arr
        if( (func.errordata.meanDif <= maxMeanError) && (func.errordata.meanDif >= minMeanError))
        if( (func.errordata.medianDif <= maxMedianError) && (func.errordata.medianDif >= minMedianError))
        if(  filterType == Any || (func.precision == filterType))
            plot!(inputs, func.errordata.absDif, label=string(func.fitType, "_", func.precision, "_", string(func.order)))
        end
        end
        end
    end
    p
end

# Plots the frequency of error ranges
function PlotApproximationFunctionDiffHist(arr; filterType = Any, maxMeanError = 99999.0, minMeanError = 0.0, maxMedianError = 99999.0, minMedianError = 0.0 ) #
    p = []
    for func in arr
        if( (func.errordata.medianDif <= maxMedianError) && (func.errordata.medianDif >= minMedianError))
        if( (func.errordata.meanDif <= maxMeanError) && (func.errordata.meanDif >= minMeanError))
        if(  filterType == Any || (func.precision == filterType))
            histogram(func.errordata.absDif, ylabel="Frequency", xlabel="ErrorValue", label=string(func.fitType, "_", func.precision, "_", string(func.order)))
            q = title!(string(func.fitType, "_", func.precision, "_", string(func.order)))
            push!(p, q)
        end
        end
        end
    end
    plot(p...,layout=(length(p),1),legend=false, size = (1000, 300*length(p)))
end

function PlotMedianError(arr; logerror = true)
    medianError = abs.((p->p.errordata.medianDif).(arr))
    orders = string.((p->p.order).(arr), (p->p.precision).(arr))
    b1 = 0
    if(logerror)
        b1 = bar(orders, medianError, yscale = :log10, rotation=45)
        b1 = ylabel!("Log Error (absolute)")
    else
        b1 = bar(orders, medianError)
        b1 = ylabel!("Error (absolute)")
    end

    b1 = title!("Median Error")
end

function PlotMedianRuntime(arr; targetLine = [0.0])
    # Extracting performance times from array to array
    trials = (p->p.errordata.benchmarkRes).(arr)
    timings = (q->q[2].times).(trials)
    medRes = abs.(median.(timings))
    orders = string.((p->p.order).(arr), (p->p.precision).(arr))
    b2 = bar(orders, medRes, rotation=45)

    if(targetLine[1] > 0.0)
        b2 = hline!(targetLine)
    end

    b2 = title!("Median Runtime")
    b2 = ylabel!("Time (ns)")
end

function PlotRuntimeErrorPair(arr, ; logScaleY = true, targetRuntime = [0.0])
    b1 = PlotMedianError(arr, logerror = logScaleY)
    b2 = PlotMedianRuntime(arr, targetLine = targetRuntime)
    plot(b1,b2, layout=(2,1), xlabel="Order", legend=false, size = (900, 800) )
end

# Implementation of Pate function generation using 'Eval'. This is much cleaner, but produces less optimal code.
function GetApproximatePateFunction_Eval(targetFunction, inputs64, highResResults, orderPFn, orderQFn, precision=Float64)

    params64       = pateLinearRationalFit(inputs64, highResResults, orderPFn(), orderQFn())
    params         = convert(Array{precision},params64)

    nparams64      = params[1:orderPFn()+1]
    nparams64      = PadArray(nparams64,20)

    dparams64      = params[orderPFn()+2:end]
    dparams64      = PadArray(dparams64,20)

    orderEitherFn = ()-> max(orderPFn(), orderQFn())
	fitfast_let_float64_string = "function(x)
                                    if $(orderEitherFn()) >= 2 xx       = x*x        end
                                    if $(orderEitherFn()) >= 3 xxx      = xx*x       end
                                    if $(orderEitherFn()) >= 4 xxxx     = xx*xx      end
                                    if $(orderEitherFn()) >= 8 xxxxxxxx = xxxx*xxxx  end

                                    numerator =  $(nparams64[1])
                                    if $(orderPFn()) >= 1  numerator+= $(nparams64[2] )* x            end
                                    if $(orderPFn()) >= 2  numerator+= $(nparams64[3] )* xx           end
                                    if $(orderPFn()) >= 3  numerator+= $(nparams64[4] )* xxx          end
                                    if $(orderPFn()) >= 4  numerator+= $(nparams64[5] )* xxxx         end
                                    if $(orderPFn()) >= 5  numerator+= $(nparams64[6] )* xxxx*x       end
                                    if $(orderPFn()) >= 6  numerator+= $(nparams64[7] )* xxxx*xx      end
                                    if $(orderPFn()) >= 7  numerator+= $(nparams64[8] )* xxxx*xxx     end
                                    if $(orderPFn()) >= 8  numerator+= $(nparams64[9] )* xxxxxxxx     end
                                    if $(orderPFn()) >= 9  numerator+= $(nparams64[10])* xxxxxxxx*x   end
                                    if $(orderPFn()) >= 10 numerator+= $(nparams64[11])* xxxxxxxx*xx  end

                                    denom = 1.0
                                    if $(orderQFn()) >= 1  denom    += $(dparams64[1] )* x            end
                                    if $(orderQFn()) >= 2  denom    += $(dparams64[2] )* xx           end
                                    if $(orderQFn()) >= 3  denom    += $(dparams64[3] )* xxx          end
                                    if $(orderQFn()) >= 4  denom    += $(dparams64[4] )* xxxx         end
                                    if $(orderQFn()) >= 5  denom    += $(dparams64[5] )* xxxx*x       end
                                    if $(orderQFn()) >= 6  denom    += $(dparams64[6] )* xxxx*xx      end
                                    if $(orderQFn()) >= 7  denom    += $(dparams64[7] )* xxxx*xxx     end
                                    if $(orderQFn()) >= 8  denom    += $(dparams64[8] )* xxxxxxxx     end
                                    if $(orderQFn()) >= 9  denom    += $(dparams64[9] )* xxxxxxxx*x   end
                                    if $(orderQFn()) >= 10 denom    += $(dparams64[10])* xxxxxxxx*xx  end

                                    (numerator/denom)
                                end"
    fitfast_let_float64 =   @eval function(x)
                                    if $(orderEitherFn()) >= 2 xx       = x*x        end
                                    if $(orderEitherFn()) >= 3 xxx      = xx*x       end
                                    if $(orderEitherFn()) >= 4 xxxx     = xx*xx      end
                                    if $(orderEitherFn()) >= 8 xxxxxxxx = xxxx*xxxx  end

                                    numerator =  $(nparams64[1])
                                    if $(orderPFn()) >= 1  numerator+= $(nparams64[2] )* x            end
                                    if $(orderPFn()) >= 2  numerator+= $(nparams64[3] )* xx           end
                                    if $(orderPFn()) >= 3  numerator+= $(nparams64[4] )* xxx          end
                                    if $(orderPFn()) >= 4  numerator+= $(nparams64[5] )* xxxx         end
                                    if $(orderPFn()) >= 5  numerator+= $(nparams64[6] )* xxxx*x       end
                                    if $(orderPFn()) >= 6  numerator+= $(nparams64[7] )* xxxx*xx      end
                                    if $(orderPFn()) >= 7  numerator+= $(nparams64[8] )* xxxx*xxx     end
                                    if $(orderPFn()) >= 8  numerator+= $(nparams64[9] )* xxxxxxxx     end
                                    if $(orderPFn()) >= 9  numerator+= $(nparams64[10])* xxxxxxxx*x   end
                                    if $(orderPFn()) >= 10 numerator+= $(nparams64[11])* xxxxxxxx*xx  end

                                    denom = 1.0
                                    if $(orderQFn()) >= 1  denom    += $(dparams64[1] )* x            end
                                    if $(orderQFn()) >= 2  denom    += $(dparams64[2] )* xx           end
                                    if $(orderQFn()) >= 3  denom    += $(dparams64[3] )* xxx          end
                                    if $(orderQFn()) >= 4  denom    += $(dparams64[4] )* xxxx         end
                                    if $(orderQFn()) >= 5  denom    += $(dparams64[5] )* xxxx*x       end
                                    if $(orderQFn()) >= 6  denom    += $(dparams64[6] )* xxxx*xx      end
                                    if $(orderQFn()) >= 7  denom    += $(dparams64[7] )* xxxx*xxx     end
                                    if $(orderQFn()) >= 8  denom    += $(dparams64[8] )* xxxxxxxx     end
                                    if $(orderQFn()) >= 9  denom    += $(dparams64[9] )* xxxxxxxx*x   end
                                    if $(orderQFn()) >= 10 denom    += $(dparams64[10])* xxxxxxxx*xx  end

                                    Approximation(numerator/denom)
                                end

    return (fitfast_let_float64, fitfast_let_float64_string)
end

# Implementation of Pate function generation using 'let'. This is a bit messy but generates code as optimally as if it was hard-coded
function GetApproximatePateFunction(targetFunction, inputs64, highResResults, orderPFn, orderQFn, precision=Float64)

    params64       = pateLinearRationalFit(inputs64, highResResults, orderPFn(), orderQFn())
    params         = convert(Array{precision},params64)

    nparams64      = params[1:orderPFn()+1]
    dparams64      = params[orderPFn()+2:end]
    nparams64      = PadArray(nparams64,20)
    dparams64      = PadArray(dparams64,20)

    orderEitherFn = ()-> max(orderPFn(), orderQFn())

    #How we are forced to do it by compilers not catching on to what we are doing
    (fitfast_let_float64) =
    let num1  = nparams64[1]
    let num2  = nparams64[2]
    let num3  = nparams64[3]
    let num4  = nparams64[4]
    let num5  = nparams64[5]
    let num6  = nparams64[6]
    let num7  = nparams64[7]
    let num8  = nparams64[8]
    let num9  = nparams64[9]
    let num10 = nparams64[10]
    let num11 = nparams64[11]
    let den1  = dparams64[1]
    let den2  = dparams64[2]
    let den3  = dparams64[3]
    let den4  = dparams64[4]
    let den5  = dparams64[5]
    let den6  = dparams64[6]
    let den7  = dparams64[7]
    let den8  = dparams64[8]
    let den9  = dparams64[9]
    let den10 = dparams64[10]
        funca = function(x)
            if orderEitherFn() >= 2 xx       = x*x        end
            if orderEitherFn() >= 3 xxx      = xx*x       end
            if orderEitherFn() >= 4 xxxx     = xx*xx      end
            if orderEitherFn() >= 8 xxxxxxxx = xxxx*xxxx  end

            numerator = num1
            if orderPFn() >= 1  numerator+= num2 * x            end
            if orderPFn() >= 2  numerator+= num3 * xx           end
            if orderPFn() >= 3  numerator+= num4 * xxx          end
            if orderPFn() >= 4  numerator+= num5 * xxxx         end
            if orderPFn() >= 5  numerator+= num6 * xxxx*x       end
            if orderPFn() >= 6  numerator+= num7 * xxxx*xx      end
            if orderPFn() >= 7  numerator+= num8 * xxxx*xxx     end
            if orderPFn() >= 8  numerator+= num9 * xxxxxxxx     end
            if orderPFn() >= 9  numerator+= num10* xxxxxxxx*x   end
            if orderPFn() >= 10 numerator+= num11* xxxxxxxx*xx  end

            denom = 1.0
            if orderQFn() >= 1  denom    += den1 * x            end
            if orderQFn() >= 2  denom    += den2 * xx           end
            if orderQFn() >= 3  denom    += den3 * xxx          end
            if orderQFn() >= 4  denom    += den4 * xxxx         end
            if orderQFn() >= 5  denom    += den5 * xxxx*x       end
            if orderQFn() >= 6  denom    += den6 * xxxx*xx      end
            if orderQFn() >= 7  denom    += den7 * xxxx*xxx     end
            if orderQFn() >= 8  denom    += den8 * xxxxxxxx     end
            if orderQFn() >= 9  denom    += den9 * xxxxxxxx*x   end
            if orderQFn() >= 10 denom    += den10* xxxxxxxx*xx  end

            Approximation(numerator/denom)
        end
    end    end    end    end    end    end    end    end    end    end    end
    end    end    end    end    end    end    end    end    end   end

	fitfast_let_float64_string ="
    let num1  = $(nparams64[1]  )
    let num2  = $(nparams64[2]  )
    let num3  = $(nparams64[3]  )
    let num4  = $(nparams64[4]  )
    let num5  = $(nparams64[5]  )
    let num6  = $(nparams64[6]  )
    let num7  = $(nparams64[7]  )
    let num8  = $(nparams64[8]  )
    let num9  = $(nparams64[9]  )
    let num10 = $(nparams64[10] )
    let num11 = $(nparams64[11] )
    let den1  = $(dparams64[1]  )
    let den2  = $(dparams64[2]  )
    let den3  = $(dparams64[3]  )
    let den4  = $(dparams64[4]  )
    let den5  = $(dparams64[5]  )
    let den6  = $(dparams64[6]  )
    let den7  = $(dparams64[7]  )
    let den8  = $(dparams64[8]  )
    let den9  = $(dparams64[9]  )
    let den10 = $(dparams64[10] )
        funca = function(x)
            if $(orderEitherFn()) >= 2 xx       = x*x        end
            if $(orderEitherFn()) >= 3 xxx      = xx*x       end
            if $(orderEitherFn()) >= 4 xxxx     = xx*xx      end
            if $(orderEitherFn()) >= 8 xxxxxxxx = xxxx*xxxx  end

            numerator = num1
            if $(orderPFn()) >= 1  numerator+= num2 * x            end
            if $(orderPFn()) >= 2  numerator+= num3 * xx           end
            if $(orderPFn()) >= 3  numerator+= num4 * xxx          end
            if $(orderPFn()) >= 4  numerator+= num5 * xxxx         end
            if $(orderPFn()) >= 5  numerator+= num6 * xxxx*x       end
            if $(orderPFn()) >= 6  numerator+= num7 * xxxx*xx      end
            if $(orderPFn()) >= 7  numerator+= num8 * xxxx*xxx     end
            if $(orderPFn()) >= 8  numerator+= num9 * xxxxxxxx     end
            if $(orderPFn()) >= 9  numerator+= num10* xxxxxxxx*x   end
            if $(orderPFn()) >= 10 numerator+= num11* xxxxxxxx*xx  end

            denom = 1.0
            if $(orderQFn()) >= 1  denom    += den1 * x            end
            if $(orderQFn()) >= 2  denom    += den2 * xx           end
            if $(orderQFn()) >= 3  denom    += den3 * xxx          end
            if $(orderQFn()) >= 4  denom    += den4 * xxxx         end
            if $(orderQFn()) >= 5  denom    += den5 * xxxx*x       end
            if $(orderQFn()) >= 6  denom    += den6 * xxxx*xx      end
            if $(orderQFn()) >= 7  denom    += den7 * xxxx*xxx     end
            if $(orderQFn()) >= 8  denom    += den8 * xxxxxxxx     end
            if $(orderQFn()) >= 9  denom    += den9 * xxxxxxxx*x   end
            if $(orderQFn()) >= 10 denom    += den10* xxxxxxxx*xx  end

            (numerator/denom)
        end
    end    end    end    end    end    end    end    end    end    end    end
    end    end    end    end    end    end    end    end    end   end   "

    return (fitfast_let_float64, fitfast_let_float64_string)
end

# Implementation of a Polynomial function using 'let'.
# This is a bit messy but generates code as optimally as if it was hard-coded
function GetApproximatePolyFunction(targetFunction, inputs64, highResResults, orderFn, precision=Float64)
    params64       = polyfit(inputs64, highResResults, orderFn())
    params64       = PadArray(params64,21)
    params         = convert(Array{precision},params64)

	fitfast_let_float64_string ="
    let x1  = $(params[1]  )
    let x2  = $(params[2]  )
    let x3  = $(params[3]  )
    let x4  = $(params[4]  )
    let x5  = $(params[5]  )
    let x6  = $(params[6]  )
    let x7  = $(params[7]  )
    let x8  = $(params[8]  )
    let x9  = $(params[9]  )
    let x10 = $(params[10] )
    let x11 = $(params[11] )
    let x12 = $(params[12] )
    let x13 = $(params[13] )
    let x14 = $(params[14] )
    let x15 = $(params[15] )
    let x16 = $(params[16] )
    let x17 = $(params[17] )
    let x18 = $(params[18] )
    let x19 = $(params[19] )
    let x20 = $(params[20] )
        funca = function(x)
            if $(orderFn()) >= 2 xx       = x*x        end
            if $(orderFn()) >= 3 xxx      = xx*x       end
            if $(orderFn()) >= 4 xxxx     = xx*xx      end
            if $(orderFn()) >= 8 xxxxxxxx = xxxx*xxxx  end

            a = x1
            if $(orderFn()) >= 1  a += x2 *  x                     end
            if $(orderFn()) >= 2  a += x3 *  xx                    end
            if $(orderFn()) >= 3  a += x4 *  xxx                   end
            if $(orderFn()) >= 4  a += x5 *  xxxx                  end
            if $(orderFn()) >= 5  a += x6 *  xxxx*x                end
            if $(orderFn()) >= 6  a += x7 *  xxxx*xx               end
            if $(orderFn()) >= 7  a += x8 *  xxxx*xxx              end
            if $(orderFn()) >= 8  a += x9 *  xxxxxxxx              end
            if $(orderFn()) >= 9  a += x10*  xxxxxxxx*x            end
            if $(orderFn()) >= 10 a += x11*  xxxxxxxx*xx           end  #checkpoint
            if $(orderFn()) >= 11 a += x12*  xxxxxxxx*xxx          end
            if $(orderFn()) >= 12 a += x13*  xxxxxxxx*xxxx         end
            if $(orderFn()) >= 13 a += x14*  xxxxxxxx*xx*xxx       end
            if $(orderFn()) >= 14 a += x15*  xxxxxxxx*xxxx*xx      end
            if $(orderFn()) >= 15 a += x16*  xxxxxxxx*xxxx*xxx     end
            if $(orderFn()) >= 16 a += x17*  xxxxxxxx*xxxxxxxx     end
            if $(orderFn()) >= 17 a += x18*  xxxxxxxx*xxxxxxxx*x   end
            if $(orderFn()) >= 18 a += x19*  xxxxxxxx*xxxxxxxx*xx  end
            if $(orderFn()) >= 19 a += x20*  xxxxxxxx*xxxxxxxx*xxx end
            (a)
        end
    end    end    end    end    end    end    end    end    end    end    end
    end    end    end    end    end    end    end    end    end       "
    (fitfast_let_float64) =
    let x1  = params[1]
    let x2  = params[2]
    let x3  = params[3]
    let x4  = params[4]
    let x5  = params[5]
    let x6  = params[6]
    let x7  = params[7]
    let x8  = params[8]
    let x9  = params[9]
    let x10 = params[10]
    let x11 = params[11]
    let x12 = params[12]
    let x13 = params[13]
    let x14 = params[14]
    let x15 = params[15]
    let x16 = params[16]
    let x17 = params[17]
    let x18 = params[18]
    let x19 = params[19]
    let x20 = params[20]
        funca = function(x)
            if orderFn() >= 2 xx       = x*x        end
            if orderFn() >= 3 xxx      = xx*x       end
            if orderFn() >= 4 xxxx     = xx*xx      end
            if orderFn() >= 8 xxxxxxxx = xxxx*xxxx  end

            a = x1
            if orderFn() >= 1  a += x2*  x                     end
            if orderFn() >= 2  a += x3*  xx                    end
            if orderFn() >= 3  a += x4*  xxx                   end
            if orderFn() >= 4  a += x5*  xxxx                  end
            if orderFn() >= 5  a += x6*  xxxx*x                end
            if orderFn() >= 6  a += x7*  xxxx*xx               end
            if orderFn() >= 7  a += x8*  xxxx*xxx              end
            if orderFn() >= 8  a += x9*  xxxxxxxx              end
            if orderFn() >= 9  a += x10* xxxxxxxx*x            end
            if orderFn() >= 10 a += x11* xxxxxxxx*xx           end  #checkpoint
            if orderFn() >= 11 a += x12* xxxxxxxx*xxx          end
            if orderFn() >= 12 a += x13* xxxxxxxx*xxxx         end
            if orderFn() >= 13 a += x14* xxxxxxxx*xx*xxx       end
            if orderFn() >= 14 a += x15* xxxxxxxx*xxxx*xx      end
            if orderFn() >= 15 a += x16* xxxxxxxx*xxxx*xxx     end
            if orderFn() >= 16 a += x17* xxxxxxxx*xxxxxxxx     end
            if orderFn() >= 17 a += x18* xxxxxxxx*xxxxxxxx*x   end
            if orderFn() >= 18 a += x19* xxxxxxxx*xxxxxxxx*xx  end
            if orderFn() >= 19 a += x20* xxxxxxxx*xxxxxxxx*xxx end
            Approximation(a)
        end
    end    end    end    end    end    end    end    end    end    end    end
    end    end    end    end    end    end    end    end    end

    return (fitfast_let_float64, fitfast_let_float64_string)
end

# Implementation of Pate function generation using 'Eval'. This is much cleaner, but produces less optimal code.
function GetApproximatePolyFunction_Eval(targetFunction, inputs64, highResResults, orderFn, precision=Float64)
    params64       = polyfit(inputs64, highResResults, orderFn())
    params64       = PadArray(params64,21)
    params         = convert(Array{precision},params64)

    fitfast_let_float64_string = "function(x)
                                if $(orderFn()) >= 2 xx       = x*x        end
                                if $(orderFn()) >= 3 xxx      = xx*x       end
                                if $(orderFn()) >= 4 xxxx     = xx*xx      end
                                if $(orderFn()) >= 8 xxxxxxxx = xxxx*xxxx  end

                                a = $(params[1])
                                if $(orderFn()) >= 1  a += $(params[2] ) * x                     end
                                if $(orderFn()) >= 2  a += $(params[3] ) * xx                    end
                                if $(orderFn()) >= 3  a += $(params[4] ) * xxx                   end
                                if $(orderFn()) >= 4  a += $(params[5] ) * xxxx                  end
                                if $(orderFn()) >= 5  a += $(params[6] ) * xxxx*x                end
                                if $(orderFn()) >= 6  a += $(params[7] ) * xxxx*xx               end
                                if $(orderFn()) >= 7  a += $(params[8] ) * xxxx*xxx              end
                                if $(orderFn()) >= 8  a += $(params[9] ) * xxxxxxxx              end
                                if $(orderFn()) >= 9  a += $(params[10]) * xxxxxxxx*x            end
                                if $(orderFn()) >= 10 a += $(params[11]) * xxxxxxxx*xx           end
                                if $(orderFn()) >= 11 a += $(params[12]) * xxxxxxxx*xxx          end
                                if $(orderFn()) >= 12 a += $(params[13]) * xxxxxxxx*xxxx         end
                                if $(orderFn()) >= 13 a += $(params[14]) * xxxxxxxx*xx*xxx       end
                                if $(orderFn()) >= 14 a += $(params[15]) * xxxxxxxx*xxxx*xx      end
                                if $(orderFn()) >= 15 a += $(params[16]) * xxxxxxxx*xxxx*xxx     end
                                if $(orderFn()) >= 16 a += $(params[17]) * xxxxxxxx*xxxxxxxx     end
                                if $(orderFn()) >= 17 a += $(params[18]) * xxxxxxxx*xxxxxxxx*x   end
                                if $(orderFn()) >= 18 a += $(params[19]) * xxxxxxxx*xxxxxxxx*xx  end
                                if $(orderFn()) >= 19 a += $(params[20]) * xxxxxxxx*xxxxxxxx*xxx end
                                (a)
                            end"

	fitfast_let_float64 = @eval function(x)
                                if $(orderFn()) >= 2 xx       = x*x        end
                                if $(orderFn()) >= 3 xxx      = xx*x       end
                                if $(orderFn()) >= 4 xxxx     = xx*xx      end
                                if $(orderFn()) >= 8 xxxxxxxx = xxxx*xxxx  end

                                a = $(params[1])
                                if $(orderFn()) >= 1  a += $(params[2] ) * x                     end
                                if $(orderFn()) >= 2  a += $(params[3] ) * xx                    end
                                if $(orderFn()) >= 3  a += $(params[4] ) * xxx                   end
                                if $(orderFn()) >= 4  a += $(params[5] ) * xxxx                  end
                                if $(orderFn()) >= 5  a += $(params[6] ) * xxxx*x                end
                                if $(orderFn()) >= 6  a += $(params[7] ) * xxxx*xx               end
                                if $(orderFn()) >= 7  a += $(params[8] ) * xxxx*xxx              end
                                if $(orderFn()) >= 8  a += $(params[9] ) * xxxxxxxx              end
                                if $(orderFn()) >= 9  a += $(params[10]) * xxxxxxxx*x            end
                                if $(orderFn()) >= 10 a += $(params[11]) * xxxxxxxx*xx           end
                                if $(orderFn()) >= 11 a += $(params[12]) * xxxxxxxx*xxx          end
                                if $(orderFn()) >= 12 a += $(params[13]) * xxxxxxxx*xxxx         end
                                if $(orderFn()) >= 13 a += $(params[14]) * xxxxxxxx*xx*xxx       end
                                if $(orderFn()) >= 14 a += $(params[15]) * xxxxxxxx*xxxx*xx      end
                                if $(orderFn()) >= 15 a += $(params[16]) * xxxxxxxx*xxxx*xxx     end
                                if $(orderFn()) >= 16 a += $(params[17]) * xxxxxxxx*xxxxxxxx     end
                                if $(orderFn()) >= 17 a += $(params[18]) * xxxxxxxx*xxxxxxxx*x   end
                                if $(orderFn()) >= 18 a += $(params[19]) * xxxxxxxx*xxxxxxxx*xx  end
                                if $(orderFn()) >= 19 a += $(params[20]) * xxxxxxxx*xxxxxxxx*xxx end
                                Approximation(a)
                            end
    return (fitfast_let_float64, fitfast_let_float64_string)
end

function GenerateAllApproximationFunctions(targetfunction, minValue, maxValue;
				sampleCount = 1000, retDataFrame=false, testTypes=[Float32, Float64],
				polynomialLet_Enabled     	= true,
				polynomialEval_Enabled    	= false,
				pateFunctionsLet_Enabled  	= false,
				pateFunctionsEval_Enabled 	= false,
				orderFunArray     			= [()->7, ()->9, ()->11, ()->13, ()->15, ()->17, ()->19],
				orderFunPateArray 			= [()->2, ()->3, ()->4, ()->5] )

    # Generation parameters and dataframe for evaluating the results.
    df = DataFrame( Name      = String[],
                    Precision = Int64[],
                    FitType   = String[],
                    Order     = Int64[],
                    abserror  = Float64[],
                    mean      = Float64[],
                    median    = Float64[],
                    benchmark = Float64[],
                    range     = Tuple{Float64,Float64}[] )

    inputs64       = Float64.(range(minValue, stop=maxValue, length=sampleCount))
    highResResults = targetfunction.(inputs64)

    typelist          = testTypes

    #orderFunArray     = [()->7, ()->9, ()->11, ()->13, ()->15, ()->17, ()->19]
    #orderFunPateArray = [()->2, ()->3, ()->4, ()->5]

    polynomialFunctions       = []
    polynomialFunctions_eval  = []
    pateFunctions_eval        = []
    pateFunctions             = []

    # Generating the performance and results for each precision of the source function.
    sourceFunctions = []
    for t in typelist
        input     = t.(range(minValue, stop=maxValue, length=sampleCount))
        errorData = GetErrorDataForFunction(targetfunction, targetfunction, input)
        targetRow = GeneratedFunctionType(targetfunction, "Source", typeof(input[1]), 0 , errorData, typeof(targetfunction).name.mt.name, (minValue, maxValue), "")
        df        = vcat(df, CreateDataFrameRow(targetRow))
        push!(sourceFunctions, targetRow)
    end

    # Generating the polynomial approximations
    if(polynomialLet_Enabled)
        for t in typelist
            for orderFunction in orderFunArray
                    newFunc,functext   = GetApproximatePolyFunction(targetfunction, inputs64, highResResults, orderFunction, t )
                    errorresults = TestFunction(newFunc, targetfunction, convert(Array{t},inputs64))

                    generatedFunctionData = GeneratedFunctionType(newFunc, "PolyLet", t,
                                                                  (orderFunction()),
                                                                  errorresults,
                                                                  typeof(targetfunction).name.mt.name,
                                                                  (minValue,maxValue), functext
                                                                )
                    df = vcat(df, CreateDataFrameRow(generatedFunctionData))
                    push!(polynomialFunctions, generatedFunctionData)
            end
        end
    end

    if(polynomialEval_Enabled)
        for t in typelist
            for orderFunction in orderFunArray
                    newFunc,functext      = GetApproximatePolyFunction_Eval(targetfunction, inputs64, highResResults, orderFunction, t )
                    errorresults = TestFunction(newFunc, targetfunction, convert(Array{t},inputs64))

                    generatedFunctionData = GeneratedFunctionType(newFunc, "PolyEval", t,
                                                                  (orderFunction()),
                                                                  errorresults,
                                                                  typeof(targetfunction).name.mt.name,
                                                                  (minValue,maxValue), functext
                                                                )
                    df = vcat(df, CreateDataFrameRow(generatedFunctionData))
                    push!(polynomialFunctions, generatedFunctionData)
            end
        end
    end

    # Generating the Pate (Eval) approximations at different precisions and orders.
    if(pateFunctionsEval_Enabled)
        for t in typelist
            for orderPFunction in orderFunPateArray
                for orderQFunction in orderFunPateArray
                    newFunc,functext   = GetApproximatePateFunction_Eval(targetfunction, inputs64, highResResults, orderPFunction, orderQFunction, t )
                    errorresults = TestFunction(newFunc, targetfunction, inputs64)

                    generatedFunctionData = GeneratedFunctionType(newFunc, "PateEval",                                                         t,
                                                                  (orderPFunction(),orderQFunction()),
                                                                  errorresults,
                                                                  typeof(targetfunction).name.mt.name,
                                                                  (minValue,maxValue), functext
                                                                )
                    df = vcat(df, CreateDataFrameRow(generatedFunctionData))
                    push!(pateFunctions_eval, generatedFunctionData)
                end
            end
        end
    end

    if(pateFunctionsLet_Enabled)
        # Generating the Pate (let) approximations at different precisions and orders.
        for t in typelist
            for orderPFunction in orderFunPateArray
                for orderQFunction in orderFunPateArray
                    newFunc,functext  = GetApproximatePateFunction(targetfunction, inputs64, highResResults, orderPFunction, orderQFunction, t )
                    errorresults = TestFunction(newFunc, targetfunction, inputs64)

                    generatedFunctionData = GeneratedFunctionType(newFunc, "PateLet", t,
                                                                  (orderPFunction(),orderQFunction()),
                                                                  errorresults,
                                                                  typeof(targetfunction).name.mt.name,
                                                                  (minValue,maxValue), functext
                                                                )
                    df = vcat(df, CreateDataFrameRow(generatedFunctionData))
                    push!(pateFunctions, generatedFunctionData)
                end
            end
        end
    end

    #return the array of all functions
    allres = vcat(pateFunctions,polynomialFunctions, polynomialFunctions_eval, pateFunctions_eval)

    if(retDataFrame)
        return (allres,df)
    else
        return allres
    end
end


text:
#!/usr/bin/env julia

using ArgParse: ArgParseSettings, @add_arg_table, parse_args
using JuliaFormatter: format

function main()
  # Parse command line arguments
  args = parse_commandline_arguments()

  # Call formatter with our default options
  format(args["path"],
         overwrite = true,
         verbose = true,
         indent = 2,
         margin = 100,
         always_for_in = true)
end

function parse_commandline_arguments()
  s = ArgParseSettings()
  @add_arg_table s begin
    "path"
      help = ("Name of file or folder to format. If PATH is a folder, "
              * "its contents are examined recursively and all `.jl` files are formatted.")
      arg_type = String
      required = true
      nargs = '+'
  end

  return parse_args(s)
end

if abspath(PROGRAM_FILE) == @__FILE__
  main()
end


text:
using CaimCore.ImageStack

@testset "Box" begin
    let box = Box([0, 0], [0, 0])
        @test box.tl == [0, 0]
        @test box.br == [0, 0]
        @test width(box) == 1
        @test height(box) == 1
        @test area(box) == 1
        @test Set(box) == Set([[0, 0]])
    end

    let box = Box([0, 0], [0, 1])
        @test box.tl == [0, 0]
        @test box.br == [0, 1]
        @test width(box) == 1
        @test height(box) == 2
        @test area(box) == 2
        @test Set(box) == Set([[0, 0], [0, 1]])
    end

    let box = Box([0, 0], [1, 0])
        @test box.tl == [0, 0]
        @test box.br == [1, 0]
        @test width(box) == 2
        @test height(box) == 1
        @test area(box) == 2
        @test Set(box) == Set([[0, 0], [1, 0]])
    end

    let box = Box([0, 1], [-1, 3])
        @test box.tl == [-1, 1]
        @test box.br == [0, 3]
        @test width(box) == 2
        @test height(box) == 3
        @test area(box) == 6
        @test Set(box) == Set([[-1, 1], [0, 1],
                               [-1, 2], [0, 2],
                               [-1, 3], [0, 3]])
    end

    let box = Box([-1, -1], [3, 3])
        @test [-1,-1] in box
        @test [ 0,-1] in box
        @test [ 3,-1] in box
        @test [-1, 0] in box
        @test [-1, 3] in box
        @test [ 1, 1] in box

        @test !([-2, 0] in box)
        @test !([ 4, 0] in box)
        @test !([ 0,-2] in box)
        @test !([ 0, 4] in box)
    end
end

@testset "Circle" begin
    @test_throws ArgumentError Circle([0], 2.)
    @test_throws ArgumentError Circle([ 0, 0, 0], 2.)

    let circle = Circle([ 0, 0], [ 0, 0])
        @test circle.center == [ 0, 0]
        @test circle.radius ≈ 0.
        @test circle.radius² ≈ 0.
        @test box(circle) == Box([ 0, 0], [ 0, 0])
        @test Set(circle) == Set([[ 0, 0]])
    end

    let circle = Circle([ 0, 0], [ 1, 0])
        @test circle.center == [ 0, 0]
        @test circle.radius ≈ 1.
        @test circle.radius² ≈ 1.
        @test box(circle) == Box([-1,-1], [ 1, 1])
        @test Set(circle) == Set([[ 0,-1],
                                  [-1, 0], [ 0, 0], [ 1, 0],
                                  [ 0, 1]])
    end

    let circle = Circle([ 0, 0], [ 2, 0])
        @test circle.center == [ 0, 0]
        @test circle.radius ≈ 2.
        @test circle.radius² ≈ 4.
        @test box(circle) == Box([-2,-2], [ 2, 2])
        @test Set(circle) == Set([[ 0,-2],
                                  [-1,-1], [ 0,-1], [ 1,-1],
                                  [-2, 0], [-1, 0], [ 0, 0], [ 1, 0], [ 2, 0],
                                  [-1, 1], [ 0, 1], [ 1, 1],
                                  [ 0, 2]])
    end

    let circle = Circle([ 0, 0], 0)
        @test circle.center == [ 0, 0]
        @test circle.radius ≈ 0.
        @test circle.radius² ≈ 0.
        @test box(circle) == Box([ 0, 0], [ 0, 0])
        @test Set(circle) == Set([[ 0, 0]])
    end

    let circle = Circle([ 1, 1], 1.)
        @test circle.center == [ 1, 1]
        @test circle.radius ≈ 1.
        @test circle.radius² ≈ 1.
        @test box(circle) == Box([ 0, 0], [ 2, 2])
        @test Set(circle) == Set([[ 1, 0],
                                  [ 0, 1], [ 1, 1], [ 2, 1],
                                  [ 1, 2]])
    end

    let circle = Circle([ 0, 0], 1)
        @test [ 0,-1] in circle
        @test [-1, 0] in circle
        @test [ 0, 0] in circle
        @test [ 1, 0] in circle
        @test [ 0, 1] in circle

        @test !([-1,-1] in circle)
        @test !([ 1,-1] in circle)
        @test !([-1, 1] in circle)
        @test !([ 1, 1] in circle)
    end
end

@testset "FreeForm" begin
    @test_throws ArgumentError FreeForm(Point[])
    @test_throws ArgumentError FreeForm([[0, 1, 2]])
    @test_throws ArgumentError FreeForm([[0, 1], [1, 2, 3]])

    let ff = FreeForm([[ 0, 0], [ 1, 0], [ 1, 1], [ 0, 1]])
        @test ff.boundary == [[ 0, 0], [ 1, 0], [ 1, 1], [ 0, 1]]
        @test box(ff) == Box([ 0, 0], [ 1, 1])
        @test Set(ff) == Set(box(ff))
    end

    let ff = FreeForm([[ 0, 0], [ 2, 0], [ 2, 2], [ 0, 2]])
        @test ff.boundary == [[ 0, 0], [ 2, 0], [ 2, 2], [ 0, 2]]
        @test box(ff) == Box([ 0, 0], [ 2, 2])
        @test Set(ff) == Set(box(ff))
    end

    let ff = FreeForm([[ 0, 0], [ 4, 0], [ 0, 4], [ 4, 4]])
        @test ff.boundary == [[ 0, 0], [ 4, 0], [ 0, 4], [ 4, 4]]
        @test box(ff) == Box([ 0, 0], [ 4, 4])
        @test Set(ff) == Set([[ 0, 0], [ 1, 0], [ 2, 0], [ 3, 0], [ 4, 0],
                              [ 1, 1], [ 2, 1], [ 3, 1],
                              [ 2, 2],
                              [ 1, 3], [ 2, 3], [ 3, 3],
                              [ 0, 4], [ 1, 4], [ 2, 4], [ 3, 4], [ 4, 4]])
    end

    let ff = FreeForm([[ 0, 0], [ 0, 4], [ 4, 0], [ 4, 4]])
        @test ff.boundary == [[ 0, 0], [ 0, 4], [ 4, 0], [ 4, 4]]
        @test box(ff) == Box([ 0, 0], [ 4, 4])
        @test Set(ff) == Set([[ 0, 0], [ 0, 1], [ 0, 2], [ 0, 3], [ 0, 4],
                              [ 1, 1], [ 1, 2], [ 1, 3],
                              [ 2, 2],
                              [ 3, 1], [ 3, 2], [ 3, 3],
                              [ 4, 0], [ 4, 1], [ 4, 2], [ 4, 3], [4, 4]])
    end

    let ff = FreeForm([[ 0, 0], [ 2, 0], [ 2, 2], [ 0, 2]])
        @testset for p in ff.boundary
            @test p in ff
        end

        @testset for p in [[ 0, 1], [ 1, 0], [ 2, 1], [ 1, 2]]
            @test p in ff
        end

        @test !([-1, 0] in ff)
        @test !([ 0,-1] in ff)
        @test !([ 3, 3] in ff)
    end

    let ff = FreeForm([[ 0, 0], [ 4, 0], [ 0, 4], [ 4, 4]])
        @testset for p in ff.boundary
            @test p in ff
        end

        @test [ 1, 0] in ff
        @test [ 2, 0] in ff
        @test [ 3, 0] in ff
        @test [ 1, 1] in ff
        @test [ 2, 1] in ff
        @test [ 3, 1] in ff
        @test [ 2, 2] in ff
        @test [ 1, 3] in ff
        @test [ 2, 3] in ff
        @test [ 3, 3] in ff
        @test [ 1, 4] in ff
        @test [ 2, 4] in ff
        @test [ 3, 4] in ff
    end
end


text:
export TinyHanabiEnv

const TINY_HANABI_REWARD_TABLE = begin
    t = Array{Int,4}(undef, 3, 3, 2, 2)
    t[:, :, 1, 1] = [
        10 0 0
        4 8 4
        10 0 0
    ]
    t[:, :, 1, 2] = [
        0 0 10
        4 8 4
        0 0 10
    ]
    t[:, :, 2, 1] = [
        0 0 10
        4 8 4
        0 0 0
    ]
    t[:, :, 2, 2] = [
        10 0 0
        4 8 4
        10 0 0
    ]
    t
end

struct TinyHanabiEnv <: AbstractEnv
    reward_table::Array{Int,4}
    cards::Vector{Int}
    actions::Vector{Int}
end

"""
    TinyHanabiEnv()

See https://arxiv.org/abs/1902.00506.
"""
TinyHanabiEnv() = TinyHanabiEnv(TINY_HANABI_REWARD_TABLE, Int[], Int[])

function RLBase.reset!(env::TinyHanabiEnv)
    empty!(env.cards)
    empty!(env.actions)
end

RLBase.players(env::TinyHanabiEnv) = 1:2

RLBase.current_player(env::TinyHanabiEnv) =
    if length(env.cards) < 2
        CHANCE_PLAYER
    elseif length(env.actions) == 0
        1
    else
        2
    end

(env::TinyHanabiEnv)(action, ::ChancePlayer) = push!(env.cards, action)
(env::TinyHanabiEnv)(action, ::Int) = push!(env.actions, action)

RLBase.action_space(env::TinyHanabiEnv, ::Int) = Base.OneTo(3)
RLBase.action_space(env::TinyHanabiEnv, ::ChancePlayer) = Base.OneTo(2)

RLBase.legal_action_space(env::TinyHanabiEnv, ::ChancePlayer) = findall(!in(env.cards), 1:2)
RLBase.legal_action_space_mask(env::TinyHanabiEnv, ::ChancePlayer) =
    [x ∉ env.cards for x in 1:2]

function RLBase.prob(env::TinyHanabiEnv, ::ChancePlayer)
    if isempty(env.cards)
        [0.5, 0.5]
    elseif length(env.cards) == 1
        p = ones(2)
        p[env.cards[]] = 0.0
        p
    else
        @error "shouldn't reach here."
    end
end

RLBase.state_space(env::TinyHanabiEnv, ::InformationSet, ::ChancePlayer) =
    ((0,), (0, 1), (0, 2), (0, 1, 2), (0, 2, 1)) # (chance_player_id(0), chance_player's actions...)
RLBase.state(env::TinyHanabiEnv, ::InformationSet, ::ChancePlayer) = (0, env.cards...)

function RLBase.state_space(env::TinyHanabiEnv, ::InformationSet, p::Int)
    Tuple(
        (p, c..., a...) for p in 1:2 for c in ((), 1, 2) for
        a in ((), 1:3..., ((i, j) for i in 1:3 for j in 1:3)...)
    )
end

function RLBase.state(env::TinyHanabiEnv, ::InformationSet, p::Int)
    card = length(env.cards) >= p ? env.cards[p] : ()
    (p, card..., env.actions...)
end

RLBase.is_terminated(env::TinyHanabiEnv) = length(env.actions) == 2
RLBase.reward(env::TinyHanabiEnv, player) =
    is_terminated(env) ? env.reward_table[env.actions..., env.cards...] : 0

(env::TinyHanabiEnv)(action::Int, ::ChancePlayer) = push!(env.cards, action)
(env::TinyHanabiEnv)(action::Int, ::Int) = push!(env.actions, action)

RLBase.NumAgentStyle(::TinyHanabiEnv) = MultiAgent(2)
RLBase.DynamicStyle(::TinyHanabiEnv) = SEQUENTIAL
RLBase.ActionStyle(::TinyHanabiEnv) = MINIMAL_ACTION_SET
RLBase.InformationStyle(::TinyHanabiEnv) = IMPERFECT_INFORMATION
RLBase.StateStyle(::TinyHanabiEnv) = InformationSet{Tuple{Vararg{Int}}}()
RLBase.RewardStyle(::TinyHanabiEnv) = TERMINAL_REWARD
RLBase.UtilityStyle(::TinyHanabiEnv) = IDENTICAL_UTILITY
RLBase.ChanceStyle(::TinyHanabiEnv) = EXPLICIT_STOCHASTIC


text:
include("../../src/SFFM.jl")
using LinearAlgebra, Plots, JLD2
#
# ## define the model(s)
include("exampleModelDef.jl")

## simulate Ψ paths for comparison
innerNSim = 10^2
NSim = 10^1 * innerNSim

# # we can do this in parallell
using SharedArrays, Distributed
nprocs() < 5 && addprocs(5 - nprocs())
@everywhere include("../../src/SFFM.jl")
@everywhere include("exampleModelDef.jl")
simsOuter = SharedArray(zeros(NSim, 5))

# using Profile
# Profile.clear()
# @profiler @time for n = 1:(NSim÷innerNSim)
#     IC = (φ = 3 .* ones(Int, innerNSim), X = 5 .* ones(innerNSim), Y = zeros(innerNSim))
#     simsInner = SFFM.SimSFFM(
#         model = simModel,
#         StoppingTime = SFFM.FirstExitY(u = 0, v = Inf),
#         InitCondition = IC,
#     )
#     simsOuter[1+(n-1)*innerNSim:n*innerNSim, :] =
#         [simsInner.t simsInner.φ simsInner.X simsInner.Y simsInner.n]
# end

@time @sync @distributed for n = 1:(NSim÷innerNSim)
    IC = (φ = 3 .* ones(Int, innerNSim), X = 5 .* ones(innerNSim), Y = zeros(innerNSim))
    simsInner = SFFM.SimSFFM(
        simModel,
        SFFM.FirstExitY(0, Inf),
        IC,
    )
    simsOuter[1+(n-1)*innerNSim:n*innerNSim, :] =
        [simsInner.t simsInner.φ simsInner.X simsInner.Y simsInner.n]
end

sims = (
    t = simsOuter[:, 1],
    φ = simsOuter[:, 2],
    X = simsOuter[:, 3],
    Y = simsOuter[:, 4],
    n = simsOuter[:, 5],
)

@save pwd()*"/examples/paperNumerics/dump/sims.jld2" sims

max_stars_repo_name: angus-lewis/SFFM

max_issues_repo_name: angus-lewis/SFFM

max_forks_repo_name: angus-lewis/SFFM


text:
function get_Yeoh_loading()
    _C = range(0.0,  0.005, length=3)
    C = [SymmetricTensor{2,3}((x + 1.0, x/10, 0.0, 1.0, 0.0, 1.0)) for x in _C]

    return C
end

@testset "Yeoh" begin
    m = Yeoh(μ = 6.8, λ=62.1, c₂ = -6.8/10, c₃ = 6.8/30)

    loading = get_Yeoh_loading()
    check_jld2(m, loading, "Yeoh")#, debug_print=false, OVERWRITE_JLD2=true)
    check_tangents_AD(m,loading)
end


text:
using LinearAlgebra
using CUDA
using Distributions
using Random

struct OptimizerRandomCfg
    population_size::Int

    function OptimizerRandomCfg(configuration::OrderedDict)
        new(configuration["population_size"])
    end
end

mutable struct OptimizerRandom
    population_size::Int
    individual_size::Int

    function OptimizerRandom(individual_size::Int, optimizer_configuration::OrderedDict)

        config = OptimizerRandomCfg(optimizer_configuration)
        optimizer = new(config.population_size, individual_size)

        return optimizer

    end

end

function ask(optimizer::OptimizerRandom)

    return rand(optimizer.population_size, optimizer.individual_size)

end

function tell(optimizer::OptimizerRandom, rewards_training)

end


text:
function update!(sim::Simulation{T, M},
                 event::Event{T},
                 transmission::Transmission) where {T <: DiseaseStateSequence, M <: ILM}
  if _time(event) < sim.time
    error("Time of a new event must be >= that of the previous event")
  elseif _time(event) < Inf
    update!(sim.events, event)
    update!(sim.transmission_network, transmission)
    update!(sim.disease_states, event)
    update!(sim.transmission_rates,
            sim.event_rates,
            event,
            sim.disease_states,
            sim.population,
            sim.risk_functions,
            sim.risk_parameters)
    # Count the iteration as having occurred
    sim.iterations += 1
  end
  # Update simulation time
  sim.time = _time(event)
  return sim
end


text:
function create_jacobian(mask::BitArray, A::Array, B::Array)
    @assert size(A, 3) == size(B, 3)

    N = sum(mask)
    n_points, n_viewpoints = size(mask)
    n_pose_params = size(A, 2)
    n_point_params = size(B, 2)

    n_rows = 2 * N
    n_cols_a = n_pose_params * n_viewpoints
    n_cols_b = n_point_params * n_points
    JA = zeros(Float64, n_rows, n_cols_a)
    JB = zeros(Float64, n_rows, n_cols_b)

    # J' * J should be invertible
    # n_rows(J) >= n_cols(J)
    @assert n_rows >= n_cols_a + n_cols_b

    n_points, n_viewpoints = size(mask, 1), size(mask, 2)

    viewpoint_indices = Array{Int}(undef, N)
    point_indices = Array{Int}(undef, N)

    index = 1
    for i in 1:n_points
        for j in 1:n_viewpoints
            if !mask[i, j]
                continue
            end

            viewpoint_indices[index] = j
            point_indices[index] = i

            row = (index - 1) * 2 + 1

            col = (j-1) * n_pose_params + 1
            JA[row:row+1, col:col+n_pose_params-1] = A[:, :, index]

            col = (i-1) * n_point_params + 1
            JB[row:row+1, col:col+n_point_params-1] = B[:, :, index]

            index += 1
        end
    end

    indices = SBA.Indices(viewpoint_indices, point_indices)
    J = hcat(JA, JB)
    indices, J
end

# there shouldn't be an empty row / column
# (empty means that all row elements / column elements = 0)
# and it seems that at least two '1' elements must be
# found per one row / column
mask = BitArray([
    1 1 1 1 1 1 1 1 1;
    1 0 1 1 1 0 1 1 0;
    1 1 1 1 1 1 0 1 0;
    1 0 0 1 1 1 0 1 1;
    0 0 1 0 0 0 0 0 1
])

N = sum(mask)

x_true = rand(-9:9, 2, N)
x_pred = rand(-9:9, 2, N)
A = randn(Float64, 2, 4, N)
B = randn(Float64, 2, 3, N)
indices, J = create_jacobian(mask, A, B)
delta_a, delta_b = SBA.sba(indices, x_true, x_pred, A, B)

delta = (J' * J) \ (J' * vec(x_true - x_pred))

n_pose_params = size(A, 2)
n_viewpoints = size(mask, 2)
size_A = n_pose_params * n_viewpoints

@test isapprox(delta[1:size_A], vec(delta_a))
@test isapprox(delta[size_A+1:end], vec(delta_b))


text:
@testset "Bad cases" begin
    F.def_LOCAL_VARS!()
    # Lonely End block
    s = """A {{end}}"""
    @test_throws F.HTMLBlockError F.convert_html(s)

    # Inbalanced
    s = """A {{if a}} B {{if b}} C {{else}} {{end}}"""
    @test_throws F.HTMLBlockError F.convert_html(s)

    # Some of the conditions are not bools
    F.set_vars!(F.LOCAL_VARS, [
        "a" => "false",
        "b" => "false",
        "c" => "\"Hello\""])
    s = """A {{if a}} A {{elseif c}} B {{end}}"""
    @test_throws F.HTMLBlockError F.convert_html(s)
end

@testset "Script" begin
    F.def_LOCAL_VARS!()
    F.set_var!(F.LOCAL_VARS, "hasmath", true)
    s = """
        Hasmath: {{hasmath}}

        """
    @test isapproxstr(F.convert_html(s), """
        Hasmath: true

        """)
end

# issue #482
@testset "div-dbb" begin
    Franklin.eval(:(hfun_bar(p) = string(round(sqrt(Meta.parse(p[1])), digits=1)) ))
    s = "@@B @@A {{author}} @@\n@@ \n" |> fd2html
    @test isapproxstr(s, """

        """)
    s = "**{{author}}**" |> fd2html
    @test isapproxstr(s, "

")
    s = raw"\style{font-weight:bold;}{ {{author}} }" |> fd2html
    @test isapproxstr(s, """

        """)
    s = raw"@@bold {{bar 4}} @@" |> fd2html
    @test isapproxstr(s, """

2.0

        """)
end

@testset "iss#502" begin
    s = """
       @def upcoming_release_short = "1.5"
       @def upcoming_release_date = "May 28, 2020"

       Blah v{{upcoming_release_short}} and {{upcoming_release_date}}.
       """ |> fd2html
    @test isapproxstr(s, "
Blah v1.5 and May 28, 2020.
")
end


text:
module CrumbleBlock

using ..Ahorn, Maple

const placements = Ahorn.PlacementDict(
    "Crumble Blocks ($(uppercasefirst(texture)))" => Ahorn.EntityPlacement(
        Maple.CrumbleBlock,
        "rectangle",
        Dict{String, Any}(
            "texture" => texture
        )
    ) for texture in Maple.crumble_block_textures
)

Ahorn.editingOptions(entity::Maple.CrumbleBlock) = Dict{String, Any}(
    "texture" => Maple.crumble_block_textures
)

Ahorn.minimumSize(entity::Maple.CrumbleBlock) = 8, 0
Ahorn.resizable(entity::Maple.CrumbleBlock) = true, false

function Ahorn.selection(entity::Maple.CrumbleBlock)
    x, y = Ahorn.position(entity)
    width = Int(get(entity.data, "width", 8))

    return Ahorn.Rectangle(x, y, width, 8)
end

function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::Maple.CrumbleBlock, room::Maple.Room)
    texture = get(entity.data, "texture", "default")
    texture = "objects/crumbleBlock/$texture"

    # Values need to be system specific integer
    x = Int(get(entity.data, "x", 0))
    y = Int(get(entity.data, "y", 0))

    width = Int(get(entity.data, "width", 8))
    tilesWidth = div(width, 8)

    Ahorn.Cairo.save(ctx)

    Ahorn.rectangle(ctx, 0, 0, width, 8)
    Ahorn.clip(ctx)

    for i in 0:ceil(Int, tilesWidth / 4)
        Ahorn.drawImage(ctx, texture, 32 * i, 0, 0, 0, 32, 8)
    end

    Ahorn.restore(ctx)
end

end


text:
"""
Ignore model for light interception, see [here](https://archimed-platform.github.io/archimed-phys-user-doc/3-inputs/5-models/2-models_list/).
Make the mesh invisible, and not computed. Can save a lot of time for the computations when there are components types
that are not visible anyway (e.g. inside others).
"""
struct Ignore <: AbstractInterceptionModel end


text:
using Logging

export LogEntry, VectorLogger

struct LogEntry
    level
    message
    _module
    group
    id
    file
    line
    keys
end

@Base.kwdef mutable struct VectorLogger <: Logging.AbstractLogger
    log::Vector{LogEntry} = Vector{LogEntry}()
end

function Logging.handle_message(logger::VectorLogger, level::LogLevel,
                                message, _module, group, id,
                                file, line; keys...)
    push!(logger.log,
          LogEntry(level, message, _module, group, id,
                   file, line, keys))
    return nothing
end

function Logging.shouldlog(logger, level, _module, group, id)
    true
end

function Logging.min_enabled_level(logger::VectorLogger)
    Logging.LogLevel(0)
end

function Logging.catch_exceptions(logger::VectorLogger)
    false
end


text:
@everywhere push!(LOAD_PATH,"mdp")
@everywhere using LocalFunctionApproximation
@everywhere using GridInterpolations
@everywhere using POMDPs
@everywhere using SparseArrays
@everywhere using HCAS
@everywhere using HDF5
@everywhere using POMDPModelTools
@everywhere using Printf

### OPTIONS ###
saveFile = "./Qtables/HCAS_oneSpeed_v6.h5"
nTau_warm=99
nTau_max = 60
###############

mdps = Array{Union{MDP, POMDP},1}()
for tau = 0:nTau_max
    push!(mdps, HCAS_MDP())
    mdps[end].currentTau = tau
end

@everywhere function compute_helper(states,n_states,mdps,grid,a,nTau_max)
    rval = zeros(Int32,n_states*100)
    cval = zeros(Int32,n_states*100)
    zval = zeros(Float32,n_states*100)
    rews = []
    for tau = 0:nTau_max
        push!(rews,zeros(n_states))
    end
    index=1
    fracBase=0.2
    frac = fracBase

    ## Iterate through all states
    for (i,s) in enumerate(states)
        if i/n_states>=frac
            print(round(frac*100))
            println("% Complete")
            frac+=fracBase
        end

        ## Compute rewards
        for tau=0:nTau_max
            rews[tau+1][i] = reward(mdps[tau+1],s,a)
        end

        ## Compute transitions
        dist = transition(mdps[1], s, a)
        for (sp, p) in weighted_iterator(dist)
            if p >0.0
                sp_point = POMDPs.convert_s(Vector{Float64}, sp, mdps[1])
                sps, probs = interpolants(grid, sp_point)
                for (spi, probi) in zip(sps,probs)
                    rval[index] = i
                    cval[index] = spi
                    zval[index] = probi*p
                    index += 1
                end
            end
        end
    end
    trans = sparse(rval[1:index-1],cval[1:index-1],zval[1:index-1],n_states,n_states)
    return (trans, rews)
end

@everywhere function compute_Qa(r,gam,trans,U)
    return r + gam*trans*U
end

function compute_trans_reward(mdps::Array{Union{MDP, POMDP},1},interp::LocalFunctionApproximator,nTau_max)
    ## Dictionaries to populate
    t = Dict()
    rews = Dict()
    rc = Dict()

    ## Compute states
    n_states = n_interpolating_points(interp)
    interp_points = get_all_interpolating_points(interp)
    S = statetype(typeof(mdps[1]))
    interp_states = Vector{S}(undef, n_states)
    for (i,pt) in enumerate(interp_points)
        interp_states[i] = POMDPs.convert_s(S, pt, mdps[1])
    end

    ## Loop through all actions in parallel
    for (ai,a) in enumerate(actions(mdps[1]))
        rc[ai] = remotecall(compute_helper, mod(ai,nprocs()-1)+2,interp_states,n_states,mdps,interp.grid,a,nTau_max)
    end
    for (ai,a) in enumerate(actions(mdps[1]))
        (t[ai], rews[ai]) = fetch(rc[ai])
    end
    return (t, rews)
end

function computeQ(mdps::Array{Union{MDP, POMDP},1},interp,nTau_warm,nTau_max)
    (trans, rews) = compute_trans_reward(mdps,interp,nTau_max);

    # Initialize Q and U vectors
    ns = length(rews[1][1])
    na = length(actions(mdps[1]))
    nt = nTau_max+1
    taus = 0:nTau_max
    gam = discount(mdps[1])
    U = zeros(ns)
    Q = zeros(ns,length(actions(mdps[1])))
    Q_out = zeros(ns*nt,na)
    Q_rc = Dict()

    #mdp_tau0 = []
    #mdp_tau1 = []

    ## Warm start for U at tau=0
    for i=1:nTau_warm
        @printf("Warm up: %d/%d\n",i,nTau_warm)
        for ai = 1:na
            Q_rc[ai] = remotecall(compute_Qa,mod(ai,nprocs()-1)+2,rews[ai][1],gam,trans[ai],U)
        end
        for ai = 1:na
            Q[:,ai] = fetch(Q_rc[ai])
        end
        U = maximum(Q,dims=2)
    end

    for i=1:nt
        @printf("Tau %d\n",taus[i])
        for ai = 1:na
            Q_rc[ai] = remotecall(compute_Qa,mod(ai,nprocs()-1)+2,rews[ai][i],gam,trans[ai],U)
        end
        for ai = 1:na
            Q[:,ai] = fetch(Q_rc[ai])
        end
        U = maximum(Q,dims=2)
        Q_out[1+(i-1)*ns:(i*ns),:] = deepcopy(Q)
    end
    return Q_out
end
@time Q_out = computeQ(mdps,interp,nTau_warm,nTau_max)

println("Writing Qvalues")
h5open(saveFile, "w") do file
    write(file, "q", Q_out)
end

max_stars_repo_name: cherrywoods/HorizontalCAS

max_issues_repo_name: ahmed-irfan/HorizontalCAS

max_forks_repo_name: ahmed-irfan/HorizontalCAS


text:
using HDF5, JLD
using ArgParse
using Compat

function read_stats(fname)
  stats = jldopen(fname, "r") do file
    read(file, "statistics")
  end
  return stats
end

function number_stats(fnames, names)
  res = Any[]
  for (i, fname) in enumerate(fnames)
    for name in names[i]
      push!(res, (i, fname, name))
    end
  end
  return res
end

function list_stats(numbered_names)
  println("Listing available statistics")
  for (k, (_, fname, name)) in enumerate(numbered_names)
    println("  $k : $fname/$name")
  end
  println("Select statistics to plot using -i and specify the numbers 1-$(length(numbered_names)) seperated with ,")
end

function create_safe_files(fnames, to_tmp)
  # copy to temporary file if requested
  if to_tmp
    stats_files = [tempname() for fname in fnames]
    for (tmpfile,fname) in zip(stats_files, fnames)
      cp(fname, tmpfile)
    end
    return stats_files
  else
    return fnames
  end
end

s = ArgParseSettings()
@add_arg_table s begin
  "--idx", "-i"
    help = "a list of indices seperated by , denoting the statistics that should be plotted"
    arg_type = AbstractString
    default = ""
  "--list", "-l"
    help = "list all available statistics for plotting"
    action = :store_true
  "--tmp", "-t"
    help = "copy the statistics file to a temporary location before plotting (useful when plotting during training)"
    action = :store_true
  "statistics_filenames"
    nargs = '*'
    help = "the filenames of the statistics JLD files"
    required = true
end

# first parse arguments and read statistics files
parsed_args = parse_args(ARGS, s)
filenames = unique(parsed_args["statistics_filenames"])
stats_files = create_safe_files(filenames, parsed_args["tmp"])
all_stats = map(read_stats, stats_files)
# get all unique statistic names that were logged in each files
names = map(keys, all_stats)
# and assign a number to each
numbered_names = number_stats(filenames, names)

# process according to arguments
if parsed_args["list"] || parsed_args["idx"] == ""
  list_stats(numbered_names)
end

using PyPlot
if parsed_args["idx"] != ""
  selected_ind = map(int, split(parsed_args["idx"], ","))
  if any([x < 0 || x > length(numbered_names) for x in selected_ind])
    list_stats(numbered_names)
    error("Invalid index in your list : $selected_ind make sure the indices are between 1 and $(length(numbered_names))")
  end

  figure()
  for ind in selected_ind
    # get the right stats file
    (stats_num, fname, selected) = numbered_names[ind]
    stats = all_stats[stats_num][selected]

    # do the actual plotting
    # x will simply be the iteration number
    #   which we will sort
    x = sort(collect(keys(stats)))
    # and y is the statistics corresponding to
    # the selected statistics you want to plot
    y = [stats[i] for i in x]
    plot(x, y, label="$(fname)/$(selected)")
  end
  legend()

  print("Hit <enter> to continue")
  readline()
  close()
end

# delete temporary file if it was created
if parsed_args["tmp"]
  for f in stats_files
    rm(f)
  end
end


text:
# Use baremodule to shave off a few KB from the serialized `.ji` file
baremodule bitwarden_cli_jll
using Base
using Base: UUID
import JLLWrappers

JLLWrappers.@generate_main_file_header("bitwarden_cli")
JLLWrappers.@generate_main_file("bitwarden_cli", UUID("9720d140-3882-5b8d-84f3-6af565c4e518"))
end  # module bitwarden_cli_jll


text:
# Autogenerated wrapper script for ImageMagick_jll for x86_64-w64-mingw32-cxx03
export identify, imagemagick_convert, libwand, mogrify, montage

using Zlib_jll
using libpng_jll
using JpegTurbo_jll
using Libtiff_jll
JLLWrappers.@generate_wrapper_header("ImageMagick")
JLLWrappers.@declare_library_product(libwand, "libMagickWand-6.Q16-7.dll")
JLLWrappers.@declare_executable_product(identify)
JLLWrappers.@declare_executable_product(imagemagick_convert)
JLLWrappers.@declare_executable_product(mogrify)
JLLWrappers.@declare_executable_product(montage)
function __init__()
    JLLWrappers.@generate_init_header(Zlib_jll, libpng_jll, JpegTurbo_jll, Libtiff_jll)
    JLLWrappers.@init_library_product(
        libwand,
        "bin\\libMagickWand-6.Q16-7.dll",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_executable_product(
        identify,
        "bin\\identify.exe",
    )

    JLLWrappers.@init_executable_product(
        imagemagick_convert,
        "bin\\convert.exe",
    )

    JLLWrappers.@init_executable_product(
        mogrify,
        "bin\\mogrify.exe",
    )

    JLLWrappers.@init_executable_product(
        montage,
        "bin\\montage.exe",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
# LOGICCIRCUITS LIBRARY ROOT

module LogicCircuits

using Reexport

include("Utils/Utils.jl")
@reexport using .Utils

include("abstract_logic_nodes.jl")
include("bit_circuit.jl")
include("queries.jl")
include("satisfies_flow.jl")
include("transformations.jl")
include("plain_logic_nodes.jl")

include("structured/abstract_vtrees.jl")
include("structured/plain_vtrees.jl")
include("structured/structured_logic_nodes.jl")

include("sdd/sdds.jl")
include("sdd/sdd_functions.jl")
include("sdd/apply.jl")

include("LoadSave/LoadSave.jl")
@reexport using .LoadSave

end