text:
ENV["GKSwstype"] = "nul"
using model1d1d
# using Plots
# using Plots.PlotMeasures
using JLD2

function eval_all_times(;folder::AbstractString=pwd(), img_folder::AbstractString=joinpath(pwd(), "img"))

    folder = joinpath(pwd(),"results")
    casenames = String[]

    files = readdir(folder)
    jld2_files = files[endswith.(files, ".jld2")]

    @show jld2_files

    for jld2_file in jld2_files

        casename = jld2_file[1:end-5]

        if !isdir(joinpath(img_folder, casename))
            println("Generating images for ", casename)

            @load joinpath(folder, jld2_file) model desc results

            println("Casename: ", casename, "\n")

            model1d1d.print_case_info(model)

            model1d1d.post_process_results(@__DIR__, casename, model, results,
                    ylim_cust = (0.0,1400.0))
        else
            println("Result image folder allready exsits, skipping!")
        end
    end
end

eval_all_times()


text:
using ArgParse

s = ArgParseSettings()
@add_arg_table! s begin
    "-f"
    help = "Output base filename"
    arg_type = String
    "-L"
    help = "Cell length (volume is L³)"
    arg_type = AbstractFloat
    default = 5.
    "-m"
    help = "Grid resolution. Total number of voxels is (m*L)³"
    arg_type = Int16
    default = Int16(10)
    "-s"
    help = "Seed for RNG for reproduceable runs"
    arg_type = Integer
    "--vol_frac_goal"
    help = "Goal for volume fraction inclusions (excluding overlap)"
    arg_type = Real
    default = 1.
    "--compress"
    help = "H5 compression (90 percent space savings, but not supported in DREAM.3D currently)"
    action = :store_true
end

add_arg_group!(s, "Packing")
@add_arg_table! s begin
    "--nr_tries"
    help = "Number of random translations"
    arg_type = Integer
    default = 2500
    "--delta"
    help = "Maximum distance for randomized translations"
    arg_type = Real
    default = 0.0
    "--m_pack"
    help = "Grid resolution during packing"
    arg_type = Int16
    default = Int16(10)
end

add_arg_group!(s, "WC grain shape")
@add_arg_table! s begin
    "-k"
    help = "k distribution"
    arg_type = String
    default = "U:0.4,1.4"
    "-r"
    help = "r distribution"
    arg_type = String
    default = "U:0.1,0.4"
    "-d"
    help = "d distribution"
    arg_type = String
    default = "U:0.5,1.5"
end

add_arg_group!(s, "Potts simulation")
@add_arg_table! s begin
    "--mc_steps"
    help = "Monte-Carlo steps (scales with (m*L)⁴. Set to zero to turn off."
    arg_type = AbstractFloat
    default = 0.05
    "--tau"
    help = "Ficticious temperature in Potts model"
    arg_type = AbstractFloat
    default = 0.5
end

ext: jl


text:
module testSep_Compute
using Test
using JuMP
srcDir = pwd()*"\\src\\"
include(srcDir*"sep_Model.jl")
include(srcDir*"sep_Compute.jl")
using .sep_Model
using .sep_Compute

@testset "Compute sep.-rank test" begin
    sep_mod = read_model(pwd()*"\\test\\Defualt_sep_mod.dat-s")
    sep_mod_opt = Computeξₜˢᵉᵖ(sep_mod)

    @test result_count(sep_mod_opt) == 1
    @test string(primal_status(sep_mod_opt)) == "FEASIBLE_POINT"
    @test string(dual_status(sep_mod_opt)) == "FEASIBLE_POINT"
    @test (objective_value(sep_mod_opt) - 2.828427006418139) < 0.0000001
end

# Lx = sep_mod_opt[:Lx]
# rec_mom_mat

# using MathOptInterface
# const MOI = MathOptInterface
#
# # this only works if you have a linear objective function (the model has a ScalarAffineFunction as its objective)
# obj = MOI.get(Mod, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
#
# # take a look at the terms
# obj.terms
# # from this you could extract your vector c
# c = zeros(4)
# for term in obj.terms
#     c[term.variable_index.value] = term.coefficient
# end
# @show(c)

end  # module testSep_Compute

max_stars_repo_name: JAndriesJ/sep-rank-bounding

max_issues_repo_name: JAndriesJ/sep-rank-bounding

max_forks_repo_name: JAndriesJ/sep-rank-bounding


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

using Markdown
using InteractiveUtils

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

# ╔═╡ bf1385da-4ac2-11eb-3992-41abac921370
using Plots

# ╔═╡ e97e5984-4ab9-11eb-3efb-9f54c6c307dd
# edit the code below to set your name and UGent username

student = (name = "Jenke Janssen", email = "Jenke.Janssen@UGent.be");

# press the ▶ button in the bottom right of this cell to run your edits
# or use Shift+Enter

# you might need to wait until all other cells in this notebook have completed running.
# scroll down the page to see what's up

# ╔═╡ f089cbaa-4ab9-11eb-09d1-05f49911487f
begin
	import Pkg; Pkg.activate("..")
	using DSJulia;
	using PlutoUI;
	tracker = ProgressTracker(student.name, student.email);
	md"""

	Submission by: **_$(student.name)_**
	"""
end

# ╔═╡ fd21a9fa-4ab9-11eb-05e9-0d0963826b9f
md"""
# Notebook 1: Getting up and running

First of all, welcome to the course, **$(student[:name])**! We hope you enjoy the ride.
"""

# ╔═╡ 0f47f5b2-4aba-11eb-2e5a-b10407e3f928

# ╔═╡ 23d3c9cc-4abd-11eb-0cb0-21673effee6c
md"""## 1. The basics
*From zero to newbie.*
"""

# ╔═╡ 62c3b076-4ab7-11eb-0cf2-25cdf7d2540d
md"""
Let's get started with the basics. Some mathematical operations, """

# ╔═╡ 7bf5bdbe-4ab7-11eb-0d4b-c116e02cb9d9
1 + 2       # adding integers

# ╔═╡ 83306610-4ab7-11eb-3eb5-55a465e0abb9
1.0 + 2.0   # adding floats

# ╔═╡ 3fa0a15c-5008-11eb-13b5-a91b02c1eb2d
1 + 2.0     # adding a float to an integer...

# ╔═╡ 83311b8a-4ab7-11eb-0067-e57ceabdfe9d
2 / 4       # standard division

# ╔═╡ 833dbc66-4ab7-11eb-216d-f9900f95deb8
div(2, 4)   # Computes 2/4 truncated to an integer

# ╔═╡ 8342c042-4ab7-11eb-2136-497fc9e1b9c4
2 ÷ 4       # looks nicer but does exactly the same!

# ╔═╡ 834d4cbc-4ab7-11eb-1f1a-df05b0c00d66
7 % 3       # get the remainder of the integer division

# ╔═╡ 8360ffac-4ab7-11eb-1162-f7a536eb0765
35 \ 7      # inverse division

# ╔═╡ 8365cb3e-4ab7-11eb-05c0-85f51cc9b018
1 // 3      # fractions, gives the result as a rational

# ╔═╡ 8370eaf0-4ab7-11eb-1cd3-dfeec9341c4b
1//2 + 1//4

# ╔═╡ 50bb93e6-5a6c-11eb-0a6c-d5d749857771
2.0 + 3.0im  # complex numbers

# ╔═╡ 8383f104-4ab7-11eb-38a5-33e59b1591f6
'c'        # characters (unicode)

# ╔═╡ 8387934a-4ab7-11eb-11b2-471b08d87b31
:symbol    # symbols, mostly used for macros

# ╔═╡ 8bab2e50-5a6c-11eb-3c5f-a9f811483814
:ζ         # any LaTeX symbol

# ╔═╡ 9d2708ca-5a6c-11eb-1c0f-473f0e2b5363
:🎉        # or Unicode emoji

# ╔═╡ 8c14cb9a-4ab7-11eb-0666-b1d4aca00f97
md"variable assignment"

# ╔═╡ 93b5a126-4ab7-11eb-2f67-290ed869d44a
x = 2

# ╔═╡ 353efeea-6492-11eb-3d09-353d4dae491a
md"In the Pluto notebook environment you are currently working in, it is not possible to define the same variable in two cells. However, this is not standard Julia behaviour. You can see that redefining a variable is possible,"

# ╔═╡ 92f57780-6492-11eb-1264-bbef04a8ae99
begin
	variable1 = 2.0
	variable1 = 4.0
end;

# ╔═╡ a17ebeba-6492-11eb-07ba-f516a990affc
variable1

# ╔═╡ 3055711a-6493-11eb-252b-7f5d99115551
md"""
```julia
begin
 statement1
 statement2
end
```

Enable to wrap multiple statements, since only single-line statements are allowed in this notebook environment.

"""

# ╔═╡ 962ae6d2-4ab7-11eb-14a2-c76a2221f544
τ = 1 / 37  # unicode variable names are allowed

# ╔═╡ 98d48302-4ab7-11eb-2397-710d0ae425f7
md"""

unicode! In most Julia editing environments, unicode math symbols can be typed when starting with a '\' and hitting '[TAB]'.

"""

# ╔═╡ acb3b57a-661c-11eb-2c6a-99793a47ff29
fyi(md"""Unsure what the LaTeX name for a symbol is or how to type an emoiji? Just copy-paste it in the REPL with a `?` at the beginning, e.g., `?ζ` and it will tell you how to type it.""")

# ╔═╡ cee8a766-4ab7-11eb-2bc7-898df2c9b1ff
# type \alpha  and <TAB>

# ╔═╡ e2c5b558-4ab7-11eb-09be-b354fc56cc6e
md"Operators are overrated."

# ╔═╡ ec754104-4ab7-11eb-2a44-557e4304dd43
5x         # This works

# ╔═╡ f23a2d2a-4ab7-11eb-1e26-bb2d1d19829f
md"But strings are quite essential,"

# ╔═╡ fa836e88-4ab7-11eb-0ba6-5fc7372f32ab
mystery = "life, the universe and everything"

# ╔═╡ 0138ef46-4ab8-11eb-1813-55594927d661
md"and string interpolation is performed with `$`."

# ╔═╡ 0b73d66a-4ab8-11eb-06e9-bbe95285a69f
"The answer to $mystery is $(3*2*7)"

# ╔═╡ 6b6eb954-4ab8-11eb-17f9-ef3445d359a3
md"""
Printing can be done with `println()` but in this notebook environment this does not seem to do much.
"""

# ╔═╡ 94e3eb74-4ab8-11eb-1b27-573dd2f02b1d
println("The answer to $mystery is $(3*2*7)")

# ╔═╡ 7592f8a2-4ac0-11eb-375c-61c915380eeb
md"... but take a look at the terminal window."

# ╔═╡ 97b7f7d8-61aa-11eb-2bf6-739bd5f2e941
md"So the [Stdout](https://www.computerhope.com/jargon/s/stdout.htm) is printed to the terminal window and not the notebooks. We can use the @terminal macro of our own making to circumvent this. More on macros tomorrow! What you need to know now is that the macro @terminal does some additional processing to the statement is proceeds."

# ╔═╡ dcf78e38-61a7-11eb-1ac8-9927f5dca4f2
@terminal println("The answer to $mystery is $(3*2*7)")

# ╔═╡ abf00a78-4ab8-11eb-1063-1bf4905ca250
md"""
repetitions of strings can be done using the operators `*` and `^`.
This use of `*` and `^` makes sense by analogy with multiplication and exponentiation. Just as `4^3` is equivalent to `4*4*4`, we expect `"Spam"^3` to be the same as `"Spam"*"Spam"*"Spam"`, and it is.
"""

# ╔═╡ be220a48-4ab8-11eb-1cd4-db99cd9db066
breakfast = "eggs"

# ╔═╡ cadaf948-4ab8-11eb-3110-259768055e85
abetterbreakfast = "SPAM"

# ╔═╡ cadb506e-4ab8-11eb-23ed-2d5f88fd30b0
breakfast * abetterbreakfast

# ╔═╡ caf56346-4ab8-11eb-38f5-41336c5b45a7
breakfast * abetterbreakfast^3 * breakfast

# ╔═╡ 046133a8-4ab9-11eb-0591-9de27d85bbca
md"""
Lots of handy `String`-operations are available in the standard library of Julia:
"""

# ╔═╡ 0c8bc7f0-4ab9-11eb-1c73-b7ec002c4155
uppercase("Sometimes I feel like shouting.")

# ╔═╡ 0f8a311e-4ab9-11eb-1b64-cd62b65c49bf
findfirst("a", "banana")

# ╔═╡ 0f8a5e94-4ab9-11eb-170b-cfec74d6ebbc
findfirst("na", "banana")

# ╔═╡ 0f96fdd6-4ab9-11eb-0e33-2719394a66ba
findnext("na", "banana", 4)  # start looking from position 4 onwards

# ╔═╡ 1f255304-4ab9-11eb-34f1-270fd5a95256
md"Unlike `Strings`, a `Char` value represents a single character and is surrounded by single quotes."

# ╔═╡ 34a18900-4ab9-11eb-17a0-1168dd9d06f9
'x'

# ╔═╡ 39a0a328-4ab9-11eb-0f37-6717095b56aa
md"
The operator `∈` (\in [TAB]) is a boolean operator that takes a character and a string and returns `true` if the first appears in the second:"

# ╔═╡ 4749f268-4ab9-11eb-15a7-579437e0bd20
'a' ∈ "banana"

# ╔═╡ 5a9bbbe6-4aba-11eb-3652-43eb7891f437

# ╔═╡ 6bdc8a5e-4aba-11eb-263c-df3af7afa517
QuestionBlock(;
	title=md"**Question 1: logical statements**",
	description = md"""

	Check the behaviour of the relational operators on strings.

	```julia

	"apples" == "pears"
	"apples" < "pears"
	"apples" < "Pears"
	```
	""",
	questions = [Question(;description=md"", validators = Bool[], status=md"")]
)

# ╔═╡ 77c5280c-58d0-11eb-19ad-83ec82b6d7c7

# ╔═╡ a69ead46-4abc-11eb-3d1d-eb1c73f65150
md"All binary arithmetic and bitwise operators have an updating version that assigns the result of the operation back into the left operand. The updating version of the binary operator is formed by placing a, `=`, immediately after the operator."

# ╔═╡ b482b998-4abc-11eb-36da-379010485bfa
let         # the `let`-environment creates a local workspace, x will only exist here.
	x = 1   # inplace update of x
	x += 2  # inplace update of x
	x += 2
end

# ╔═╡ 07b103ae-4abd-11eb-311b-278d1e033642
md"Similarly to Matlab, when using the REPL, Julia will print the result of every statement by default. To suppress this behavious, just end the statement with a semicolon."

# ╔═╡ 15f8b7fe-4abd-11eb-2777-8fc8bf9d342e
var1 = 10;  # not printed...

# ╔═╡ efae58fa-5008-11eb-32fe-c3ae588d14f2
var1  # ...but still defined

# ╔═╡ 18f99e46-4abd-11eb-20a8-859cb1b12fe3
var2 = 20

# ╔═╡ 3a7954da-4abd-11eb-3c5b-858054b4d06b
md"""## 2. Logical statements

*From zero to one.*
"""

# ╔═╡ 8b17d538-4abd-11eb-0543-ab95c9548d6f
md"""**Boolean operators**

Julia uses `true` and `false` for Boolean variables.
"""

# ╔═╡ 29d34e64-5009-11eb-3301-f729150e17b2
I💖Julia = true

# ╔═╡ 91a9d1a0-4abd-11eb-3337-71983f32b6ae
!true

# ╔═╡ 942d4202-4abd-11eb-1f01-dfe3df40a5b7
!false

# ╔═╡ 942dae0e-4abd-11eb-20a2-37d9c9882ba8
1 == 1

# ╔═╡ 943d9850-4abd-11eb-1cbc-a1bef988c910
2 == 1

# ╔═╡ 943de2ce-4abd-11eb-2410-31382ae9c74f
1 != 1

# ╔═╡ 9460c03c-4abd-11eb-0d60-4d8aeb5b0c1d
2 != 1

# ╔═╡ 946161f4-4abd-11eb-0ec5-df225dc140d0
1 < 10

# ╔═╡ 947d143a-4abd-11eb-067d-dff955c90407
1 > 10

# ╔═╡ 947fea8e-4abd-11eb-1d6a-2bc540f7a50e
2 <= 2  # or 2 ≤ 2  (\le<TAB>)

# ╔═╡ 948eff10-4abd-11eb-36d0-5183e882a9e2
2 >= 2  # or 2 ≥ 2  (\ge<TAB>)

# ╔═╡ 948f5032-4abd-11eb-3d1c-7da4cb64521c
# Comparisons can be chained
1 < 2 < 3

# ╔═╡ 94b520e6-4abd-11eb-3161-addf3b0e4f24
2 < 3 < 2

# ╔═╡ 94b78322-4abd-11eb-3006-454548efd164
# Logical operators
true && true

# ╔═╡ 94d28c80-4abd-11eb-08c0-717207e4c682
true || false

# ╔═╡ 9fe6e1a2-4abd-11eb-0c39-458ce94265c0
md"Likewise, we have the Boolean logic operators `&&` (AND), `||` (OR) and `⊻` (XOR, exclusive or)."

# ╔═╡ ae26ab9e-4abd-11eb-3270-33558dbdf663
true && true

# ╔═╡ b08dc886-4abd-11eb-1807-096a7e6fd6f9
true && false

# ╔═╡ b08e3a28-4abd-11eb-258a-a5a93b4b882c
true || false

# ╔═╡ b0a8dfe0-4abd-11eb-167d-2fc3974c7c92
false || false

# ╔═╡ b0a97e00-4abd-11eb-371c-e138aea17bb6
true ⊻ false

# ╔═╡ b0ccc252-4abd-11eb-048b-4bec3750bbf1
true ⊻ true

# ╔═╡ 60b066d8-5009-11eb-3b4c-8b8fa2f4831d
md"""
Chaining logic operators is frequently done in Julia as a short alternative for an `if` statement. The idea is if you use an `&&` statement, the second part is only evaluated if the first part is true! The inverse is true for `||`, where the second part is only evaluated if the first part is false."""

# ╔═╡ b8b7c0f6-5009-11eb-1eb7-0be723a88863
let
	x = 12
	(x > 10) && (x -= 5)  # I am evaluated!
	(x > 4) || (x *= 2)   # I am not...
	x
end

# ╔═╡ 1c5975ec-4abe-11eb-0ff0-bfb2f03a520b
statements = [
	missing,  # first statement
	missing,  # second statement
	missing   # third statement
]

# ╔═╡ bd446c42-4abd-11eb-0465-d9a61c48ff48
begin

qb1 = QuestionBlock(;
	title=md"**Question 2: advanced logical statements**",
	description = md"""

	Predict the outcomes of the following statements.

	```julia
	z, y = true, false

	(z || y) && !(y || y) # first

	(z ⊻ y) && (!z ⊻ !y)  # second

	(z || y) ⊻ (z && y)   # third
	```
	""",
	questions = [Question(validators = statements, description = md"Replace `missing` with the correct boolean (true, false) below.")]
)
	validate(qb1, tracker)
end

# ╔═╡ 1c22b880-4abf-11eb-3f18-756c1198ccad
md"## 3. Control flow"

# ╔═╡ 37086212-4abf-11eb-3ec9-7f8dae57121e
md"The `if`, `else`, `elseif`-statement is instrumental to any programming language./ Note that control flow is ended with an `end` statement. In constrast to Python, tabs are only for clarity but do not impact functionality."

# ╔═╡ 489421d8-4abf-11eb-0d5e-fd779cc918a1
if 4 > 3
  'A'
elseif 3 > 4
  'B'
else
  'C'
end

# ╔═╡ 6736dafe-4abf-11eb-1fce-0716d2b7f4a8
md"""
Julia allows for some very condense control flow structures.
y = `condition` ? `valueiftrue` : `valueiffalse`. To make this work Julia requires spaces around the ? and the :
"""

# ╔═╡ 0c693c24-4ac0-11eb-2329-c743dcc5039d
clip(x) = missing

# ╔═╡ 8933033a-4abf-11eb-1156-a53a5ee9152c
begin
   q2 = Question(
			description=md"""
			Complete the clip function: $\max(0, \min(1, x))$ for a given $x$, **without** making use of the functions `min` and `max`.

			Open assignments always return `missing`.
			""",
			validators= @safe[
			clip(-1)==Solutions.clip(-1),
			clip(0.3)==Solutions.clip(0.3),
			clip(1.1)==Solutions.clip(1.1)],
		)

   qb2 = QuestionBlock(
	title=md"**Question 3: silly clipping exercise**",
	questions = [q2],
	);

	validate(qb2, tracker)
end

# ╔═╡ 035a53ba-4ac1-11eb-3c34-b50a803b7b7d
if !ismissing(clip(3))
	md"Oh yeah! 🎉 You defined your first function in Julia. More on this later."
end

# ╔═╡ 2a5fca7c-4ac0-11eb-33a3-23d972ca27b8
md"## 4. Looping"

# ╔═╡ 3896642a-4ac0-11eb-2c7c-4f376ab82217
characters = ["Harry", "Ron", "Hermione"]

# ╔═╡ 3ef3faf8-4ac0-11eb-1965-fd23413e29f3
@terminal begin
	for char in characters
	  println("Character $char")
	end
end

# ╔═╡ 3916f50e-661d-11eb-0829-cb3821836fdf
md"We can use `enumerate` to generate an iterator of tuples containing the index and the values of an iterator."

# ╔═╡ 4118016e-4ac0-11eb-18bf-5de326782c87
@terminal begin
	for (i, char) in enumerate(characters)
	  println("$i. $char")
	end
end

# ╔═╡ 4119fbca-4ac0-11eb-1ea9-0bdd324214c5
pets = ["Hedwig", "Pig", "Crookhanks"]

# ╔═╡ 5ebe25c0-661d-11eb-389b-3d81570f7cf0
md"`zip` binds two or more iterators and yield tuples of the pairs."

# ╔═╡ 4139bf3c-4ac0-11eb-2b63-77a513149351
@terminal begin
	for (char, pet) in zip(characters, pets)
	  println("$char has $pet as a pet")
	end
end

# ╔═╡ a1d4127c-4ac0-11eb-116f-79c6ee58f524
md"Strings can also be looped"

# ╔═╡ a93b28e6-4ac0-11eb-074f-a7b64f43a194
getme = "a shrubbery"

# ╔═╡ ac35b796-4ac0-11eb-3bc5-5ff4350d5452
@terminal begin
	for letter in getme  # or, equivalently, use `∈` instead of `in` to look like a mathematician!
	  println("$letter")
	end
end

# ╔═╡ b18e55ae-4ac0-11eb-1455-21b83b7c61d5
@terminal let
	n = 16757676187786;
	steps = 0
	while n > 1
	  println(n)
	  steps = steps +1
	  if iseven(n)
		n = div(n, 2)
	  else
		n = 3n + 1
	  end
	end
	n, steps
end

# ╔═╡ ec4190a8-4ac0-11eb-0421-398f063775bb
md"(Mathematical note: [they got closer to cracking this one](https://www.quantamagazine.org/mathematician-terence-tao-and-the-collatz-conjecture-20191211/?mc_cid=a3adbffb9f&mc_eid=41ed2fca13).)"

# ╔═╡ 7dce10be-500c-11eb-12cf-43f6989aae91
md"A useful trick when working with loops is using `break` and `continue` statements. If a `break` is encountered, the loop will immediately be exited. A `continue` statement will only skip the rest of the current iteration but will not halt the loop."

# ╔═╡ bafbe0ea-500c-11eb-10be-75d1738d42a2
let
	i = 1
	while true  # infinite loop
		if i^2 ≥ 231
			break
		end
		i += 1
	end
	i  # first integer with a square larger than 231
end

# ╔═╡ 2033383e-500d-11eb-3aea-cdc1e90dad2c
let
	s = 0
	for i in 1:100
		!isodd(i) && continue
		s += i
	end
	s # sum of all odd numbers between 1 and 100
end

# ╔═╡ fdb67aba-4ac0-11eb-1d4a-c354de54baa9
md"""## 5. Functions
Julia puts the fun in functions. User-defined functions can be declared as follows,
"""

# ╔═╡ 28f47a24-4ac1-11eb-271f-6b4de7311db3
function square(x)
  result = x * x
  return result
end

# ╔═╡ 47338c78-4ac1-11eb-04d6-35c2361eaea6
md"A more condensed version of of `square(x)`."

# ╔═╡ 463689b0-4ac1-11eb-1b0f-b7a239011c5c
s(x) = x * x

# ╔═╡ 52bfff04-4ac1-11eb-1216-25eedd9184c3
md"Passing an array to a function that takes a single element as argument takes a special syntax. By putting a `.` before the brackets, the function is executed on all the elements of the Array. More on this in **part2: collections**."

# ╔═╡ 61846dae-4ac1-11eb-389a-4fbe3f6145b1
s([1, 2, 3, 4, 5])   # Multiplication is not defined for Arrays

# ╔═╡ 6321ae1a-4ac1-11eb-04cb-33e939694874
s.([1, 2, 3, 4, 5])  # This is an elements-wise execution of s()

# ╔═╡ 3eda5d12-5a6e-11eb-3264-9925907c85b5
md"Maybe you are of the opinion that squaring a vector should automatically involve squaring the elements, or you might want to concatenate strings using `+`. Tomorrow we will learn to 'overload' functions to induce specific behaviour for specific datatypes!"

# ╔═╡ 7b874424-4ac1-11eb-2d4e-0b4607559b8f
md"""Keyword arguments are defined using a semicolon in the back signature and a default value can be assigned. "Keywords" assigned before a semicolon (`;`) are default values but their keywords are not ignored."""

# ╔═╡ 86defe2a-4ac1-11eb-3c01-c5e671877212
safelog(x, offset=0.1; base=10) = log(x + offset) / log(base)

# ╔═╡ 886512de-4ac1-11eb-00e1-73292ec23277
safelog(0)

# ╔═╡ 88678820-4ac1-11eb-272e-0df61e418900
safelog(0, 0.01)

# ╔═╡ 888dee1e-4ac1-11eb-264d-cd4a4f30f498
safelog(0, 0.01, base=2)

# ╔═╡ 8acb086c-4ac1-11eb-1715-756fde34b38f
md"""When functions have a variable number of arguments, one can use the *slurping* `...` operator to denote a variable number of arguments. The argument will be treated as a collection. For example"""

# ╔═╡ 944e1aaa-4ac1-11eb-0e23-41b1c5d0e889
function mymean(X...)
  m = 0.0
  for x in X
	m += x
  end
  return m / length(X)
end

# ╔═╡ 9d4e11be-4ac1-11eb-1fa0-13f1fe60c3bc
mymean(1, 3, 5)

# ╔═╡ 9d514ef6-4ac1-11eb-25fc-ffaa2dcc9b02
mymean(1, 3, 5, 7)

# ╔═╡ a0781222-4ac1-11eb-3425-d9b9603487f3
md"Similarly, the *splatting* operator can be used to split a collection into its individual elements."

# ╔═╡ a6b95d62-4ac1-11eb-0c93-7fa0f6a120d5
var3 = [1.0, 3.0, 5.0];

# ╔═╡ ab006064-4ac1-11eb-32be-6557b8d45f32
mymean(var3...)

# ╔═╡ b0603566-4ac1-11eb-17bc-3b63cd2aa1e9
md"""When unsure of what a function does, in the REPL the documentation can be viewed by adding a "?" in front of the function. Here, in the Pluto environment, put the cursor in the function of interest and open the documentation tab (look on your right)."""

# ╔═╡ b6db7e2a-5a6e-11eb-1a55-e74b15cbb9e2
begin
   q40 = Question(;
			description=md"""
			What is the difference between `max` and `maximum`?
			""")

   qb40 = QuestionBlock(;
	title=md"**Question 4: max(imum)**",
	questions = [q40],
	)

end

# ╔═╡ beafce06-4ac1-11eb-2431-1ffeba45716b
sort

# ╔═╡ ec487488-4ac1-11eb-1be3-a93e41f78bf3
md"""
For a lot of standard Julia functions an in-place version is defined. In-place means that the function changes one of the input arguments of the function. As an example, `sort()` sorts and returns the array passed as argument, it does not change the original array. In contrast, `sort!()` is the inplace version of sort and directly sorts the array passed as argument.
"""

# ╔═╡ f88fee6c-4ac1-11eb-1671-43493122f061
my_unsorted_list = [4, 5, 9, 7, 1, 9]

# ╔═╡ fa759f92-4ac1-11eb-0d72-1f9d6d38a831
sort(my_unsorted_list)

# ╔═╡ fa7ba458-4ac1-11eb-2ca1-59ff3c032b26
my_unsorted_list

# ╔═╡ fa9b3266-4ac1-11eb-153a-87c6a1124890
sort!(my_unsorted_list)

# ╔═╡ fa9d43b2-4ac1-11eb-33fc-a37503cedabf
my_unsorted_list

# ╔═╡ 0dbe0c34-500e-11eb-2633-67d8dc6b24c8
md"""
Functions are first-class and work just like any other variable! For example, you can give a function as an input in another function.

In some cases, you might want to define an **anonymous function**, without given them a name:"""

# ╔═╡ 900da226-500e-11eb-3f1c-5582ed0cf749
afun = x -> 2x^2 - 2x + 4

# ╔═╡ b24bee9a-500e-11eb-2130-45b78bbebdec
afun(4)  # can be used as a regular function

# ╔═╡ c8cf6520-500e-11eb-2dd3-4507a3994a83
md"""
A particular powerful tool is creating a **pipeline**, i.e., using the output of a previous function as input of the next one. This can be done using the `|>` syntax."""

# ╔═╡ 19402512-500f-11eb-3ea7-c3d1c52025d6
π/4 |> sin |> inv

# ╔═╡ 6dc0eaae-500f-11eb-3051-37d870366201
md"The `.` syntax also works here and it is often useful to combine with anonymous functions."

# ╔═╡ 8b57c506-500f-11eb-3114-55785eb593a7
1:1000 .|> abs2 .|> inv |> sum |> (x->6x) |> sqrt  # poor man's pi

# ╔═╡ fd171e0e-4ac1-11eb-09ea-337d17500149
md"Specific functions can be generated if you have more information on the input type.
This is called multiple dispatch.

The `::` operator attaches type annotations to expressions and variables.

How does the documentation for the function square look like after you executed the cell below?"

# ╔═╡ 10e71260-4ac2-11eb-1069-55613ee7df0a
function square(x::Float64)
  println("using function with floats")
  result = x * x
  return result
end

# ╔═╡ 3e433ab4-4ac1-11eb-2178-53b7220fa9ab
square(2)

# ╔═╡ 46112c44-4ac1-11eb-2ad8-030406c7cf67
square(2.0)

# ╔═╡ 461489fa-4ac1-11eb-0596-1d3bedb61778
square("ni")   # the multiplication of strings is defined as a concatenation

# ╔═╡ 1c0230f8-4ac2-11eb-32aa-e7a4b2ae9cff
@terminal square(4) #... nothing is printed, obviously

# ╔═╡ 226417c2-4ac2-11eb-2914-196461e2b40e
@terminal square(4.)

# ╔═╡ 3daf4fa6-4ac2-11eb-0541-b98c2e97dfe4
md"More about types in the next section!"

# ╔═╡ a777f624-4f2f-11eb-0595-432ea5115a2d
md"## Intermezzo: Types"

# ╔═╡ abb7bf4e-4f2f-11eb-1dde-abf1cb0fb8b4
 md"""All Julia objects, both those already defined as well as those you might make yourself, have a type. The type system is the secret sauce, allowing Julia to be fast because code can be specialized for a particular combination of types. It is also supremely useful in conjunction with *multiple dispatch*, in which functions work differently depending on which types you feed into them. In one of the exercises at the end of this notebook you will get a taster of how multiple dispatch can be used. A function behaviour changes depending on the type of the arguments. Day 2 will mainly focus on the type system, so in what follows, a concise introduction is given to types."""

# ╔═╡ b63533e8-4f2f-11eb-3811-5f43fcaa2a9e
begin
	number = 42
	str = "mice"
	var4 = 0.9
	A = [1 2; 3 4]
end;

# ╔═╡ b730b7b8-4f2f-11eb-3216-613c6100fb9c
typeof(number)

# ╔═╡ b99eb16c-4f2f-11eb-0f1e-5d2b1244689f
typeof(str)

# ╔═╡ bc67fc50-4f2f-11eb-2209-bf35df0e8c27
typeof(var4)

# ╔═╡ bf40274a-4f2f-11eb-0086-d3502f67161b
typeof(A)

# ╔═╡ c48e035c-4f2f-11eb-12c7-576aadca7511
md"**Multiple dispatch**"

# ╔═╡ c8435740-4f2f-11eb-3046-3d45d5e0e805
md"Multiple dispatch is a concept where the behaviour of a function can be specified differently according to the type of the input arguments. Let's us look at an example,"

# ╔═╡ cacb7254-4f2f-11eb-1daa-1bc04678835c
begin
	translate(xy::Array) = xy .+ [1 3]
	translate(s::String) = Markdown.parse("[Ctrl + click here](https://translate.google.com/?sl=en&tl=nl&text=$(s)&op=translate)")
	translate(anything::Any) = "I'm lost in translation..."
end

# ╔═╡ cc48bc9a-4f2f-11eb-134c-71bd8a944943
translate(1.0)

# ╔═╡ d9f28c04-4f2f-11eb-0255-1965fb8f07b5
translate([0.0 0.0])

# ╔═╡ dc1dbe90-4f2f-11eb-05ce-c1fe46ae14dd
translate("Hi how are you?")

# ╔═╡ de48a3f6-4f2f-11eb-314b-493546c37a21
 md"A great deal of time will be spend on the julia's time system in day 2. So do not worry if this still feels vague. Just **remember** that all objects have a type and the behaviour of a function can vary depending on the type of the input arguments."

# ╔═╡ 6da71180-4ac2-11eb-1cac-410bd1cce70c
md"""## 6. Macros
Macros provide a method to include generated code in the final body of a program. It is a way of generating a new output expression, given an unevaluated input expression. When your Julia program runs, it first parses and evaluates the macro, and the processed code produced by the macro is eventually evaluated like an ordinary expression.

Some nifty basic macros are `@time` and `@show`. `@time` prints the cpu time and memory allocations of an expression.
"""

# ╔═╡ 85b96ff0-4ac2-11eb-077f-cf4aad8a3c24
@terminal @time square(10)

# ╔═╡ a11c2898-4ac2-11eb-24d3-6f8060b5fd65
md"""The `@show` macro is often useful for debugging purposes. It displays both the expression to be evaluated and its result, finally returning the value of the result."""

# ╔═╡ a686e67e-4ac2-11eb-228e-23524a3ddc59
@terminal @show 1 + 1

# ╔═╡ d50cced2-500d-11eb-2dcc-21fc50825f43
md"We will provide more examples tomorrow!"

# ╔═╡ ad156892-4ac2-11eb-3634-a3783231e5a1
md"""## 7. Plotting

Quite essential for scientific programming is the visualisation of the results. `Plots` is the Julia package that handles a lot of the visualisation. `rand(10)`, returns an array of 10 random floats between 0 and 1.
"""

# ╔═╡ d779956a-4ac2-11eb-39de-4b3cecace452
md"""When loading in a package for the first time Julia will have to precompile this package, hence this step can take some time."""

# ╔═╡ c7d2a048-4ac2-11eb-3902-b7c8505096ae
begin
	plot(1:10, rand(10), label="first")
	plot!(1:10, rand(10), label="second") # adding to current figure using plot!

	scatter!([1:10], randn(10), label="scatter")

	xlabel!("x")
	ylabel!("f(x)")
	title!("My pretty Julia plot")
end

# ╔═╡ cf35b2b2-4ac2-11eb-1ae6-5d3c108210df
plot(0:0.1:10, x -> sin(x) / x, xlabel="x", ylabel="sin(x)/x", color=:red, marker=:square, legend=:none)
# notice the use of a symbol as an argument !

# ╔═╡ d1010f88-4ac2-11eb-0fa9-0902fef0cf9f
contour(-5:0.1:5, -10:0.1:10, (x, y) -> 3x^2-4y^2 + x*y/6)

# ╔═╡ 0e63d722-4ac3-11eb-3740-d31b47a77912
md"""### 8. Exercises"""

# ╔═╡ a3969292-57ff-11eb-059b-e9e931a30dc1
stirling(n) = missing

# ╔═╡ a48be23a-57ff-11eb-1bc3-3d3e046ea67c
begin
	q4 = Question(
			validators = @safe[
				stirling(5) ≈ Solutions.stirling(5),
				stirling(10) ≈ Solutions.stirling(10)
				],
			description = md""
		)

	qo4 = QuestionOptional{Easy}(
			description=md"""
				How accurate is this approximation?
				Plot the true factorial function (scatter) on top of the approximation 					for n ∈ [0, 10].
				""",
				validators = Bool[]
			)

	qb4 = QuestionBlock(;
		title=md"**Question 5: Stirling's approximation for factorials**",
		description = md"""
	The factorial function,

	$${\displaystyle n!=1\cdot 2\cdot 3\cdots (n-2)\cdot (n-1)\cdot n,}$$

	is often used in combinatorics but also other mathematical areas. Especially for large numbers it can get quite inefficient to compute. Stirling's approximation is an approximation for factorials,

	$${\displaystyle n!\sim {\sqrt {2\pi n}}\left({\frac {n}{e}}\right)^{n},}$$

	Complete the function `stirling()` by implementing Stirling's approximation.

		""",
		questions = [q4, qo4]
	)

	validate(qb4, tracker)
end

# ╔═╡ adb47b64-5af8-11eb-1b32-57cbe5d47200

# ╔═╡ 0cd2d0e4-59e1-11eb-112e-83ebe626f597
present = time()

# ╔═╡ 0c306fd8-4ad5-11eb-1a9f-2d3d1e838a77
function since_epoch(t)
	days, hours, minutes, seconds = missing, missing, missing, missing
	return days, hours, minutes, seconds
end

# ╔═╡ c34ede1c-4ad4-11eb-050f-bb07c5d19c1c
begin
 now = time()
qb5 = QuestionBlock(;
	title=md"**Question 6: time is relative**",
	description = md"""
The function `time` returns the current Greenwich Mean Time in seconds since "the epoch", which is an arbitrary time used as a reference point. On UNIX systems, the epoch is 1 January 1970.
Write a script that reads the current time in seconds (use `time()`) and converts it to a time of day in hours, minutes, and seconds, plus the number of days since the beginning of the year.
	""",
	questions = [Question(validators = @safe[since_epoch(now)==Solutions.since_epoch(now)], description = md"")]
)
	validate(qb5, tracker)
end

# ╔═╡ 40557d76-661e-11eb-3df0-659c6095285d
since_epoch(present)

# ╔═╡ b1af96ea-5af8-11eb-0d08-f59a4c2b686c

# ╔═╡ e99d6b96-4ad5-11eb-2144-f97a97e71ae4
begin

q6 = Question(;
	description=md"""
1. Write a function named `checkfermat` that takes four parameters ($a$, $b$, $c$ and $n$) and checks to see if Fermat’s theorem holds. If $n$ is greater than 2 and $a^n + b^n == c^n$ the program should print, *"Holy smokes, Fermat was wrong!"* Otherwise the program should print and return, *"No, that doesn’t work."* """,
	validators = [], status=md""
)

q7 = Question(;
	description=md"""
2. Write a function that takes 4 random positive integers for $a$, $b$, $c$ and $n$, and use `checkfermat` to check whether they violate Fermat’s theorem.""",
	validators = Bool[], status=md""
)

oq8 = QuestionOptional{Easy}(;
	description=md"""
3. Can you write the code so that the functions in 4.1 and 4.2 have the same name?""",
	validators = [], status=md""
)

qb6 = QuestionBlock(;
	title=md"**Question 7: Fermat's Last Theorem**",
	description = md"""
Fermat’s Last Theorem says that there are no positive integers $a$, $b$, and $c$ such that

$a^n + b^n = c^n$

for any value of $n$ greater than 2.""",
	questions = [q6, q7, oq8],
	hints=[
			hint(md"check the functions `rand` and `abs`."),
			hint(md"You can write multiple functions with the same name but with a different number or type of input arguments. So you can write a second version of `checkfermat` with no input arguments for a random version. Depending on the number and the type of the arguments, Julia will choose the appropriate version of `checkfermat` Remember that this construct is called multiple dispatch."),
			hint(md"In Pluto, you have to put methods of the same name in a `begin ... end` block.")
			],

	)
end

# ╔═╡ bf53d86c-59e1-11eb-1456-5518e1f63390
function checkfermat(a::Int, b::Int, c::Int, n::Int)
	answer = missing
	println(answer)
	return answer
end

# ╔═╡ da5537b5-88e7-4377-90c6-abbc7eaf6495
md"""
Insert some numbers below to test your function `checkfermat()`\
a = $(@bind a NumberField(1:100000000))\
b = $(@bind b NumberField(1:100000000))\
c = $(@bind c NumberField(1:100000000))\
n = $(@bind n NumberField(1:100000000))
"""

# ╔═╡ b4118bfa-5af8-11eb-0aca-cddef8e191ee
@terminal checkfermat(a,b,c,n)

# ╔═╡ f077c390-57fe-11eb-1ad9-31110b3dac39
md"""### 9. Extra exercises"""

# ╔═╡ 87871f34-4ad1-11eb-3903-93e3f63ea14a
function rightjustify(text)
	missing
end

# ╔═╡ 42f24f58-4ac3-11eb-06b5-ebc015c17520
begin
qb7 = QuestionBlock(;
	title=md"**Question 8: can you justify this?**",
	description = md"""

	Write a function named `rightjustify` that takes a string named `s` as a parameter and prints the string with enough leading spaces so that the last letter of the string is in column 70 of the display.

	Use string concatenation and repetition. Also, Julia provides a built-in function called `length`. Check what it does!

	""",
	questions = [Question(
					validators = @safe[
						rightjustify("To the left!") ==
						Solutions.rightjustify("To the left!")
					],
					description = md"")]
)
end

# ╔═╡ e2583a06-6479-11eb-091b-8be70024930d

# ╔═╡ 1f588828-6477-11eb-0c70-557f130c6785
function degrade(molecule)
	return missing # replace this with the correct code
end

# ╔═╡ 4d0e1d60-6476-11eb-154e-d9cc3bf284c2
caproic_acid = "CH3-CH2-CH2-CH2-CH2-COOH"

# ╔═╡ 5167ded6-6463-11eb-3279-0dd226fae82e
verylongium = "COOH-CO-CO-CH2-CH2-CO-CO-CH2-CO-CO-CH2-CH2-CO-CH2-CH2-CH2-CO-CH2-CO-CH2-CH2-CO-CH2-CH2-CO-CH2-CO-CH2-CH2-CO-CO-CH2-CH2-CH2-CH2-CH2-CO-CO-CH2-CH2-CO-CH2-CO-CH2-CO-CH2-CH2-CO-CO-CO-CH2-CO-CH2-CH2-CO-CO-CH2-CH2-CH2-CH2-CO-CH2-CH2-CO-CO-CO-CH2-CO-CH2-CH2-CH2-CO-CO-CO-CO-CO-CO-CH2-CH2-CH2-CO-CO-CO-CO-CH2-CH2-CH2-CO-CH2-CO-CO-CO-CO-CO-CH2-CO-CO-CH2-CH2-CH3"

# ╔═╡ c4d03faa-647d-11eb-3a84-d7b2d53e4720

# ╔═╡ c42f69f6-647d-11eb-143b-2f0ab33616e7

# ╔═╡ c55ed390-647d-11eb-22d5-33fbae27a2bb
function generator(N; tips=["CH3", "COOH"], backbone=["CH2", "CO"])
	return rand(tips) * "-" * reduce(*,rand(backbone,N-2).* "-") * rand(tips)
end

# ╔═╡ 23db2764-647e-11eb-37dd-a34db2ff0fcd
generator(6) #try rerunning this cell multiple times

# ╔═╡ 989d0dd2-647a-11eb-3123-39786452714a
C12 = [generator(12) for i in 1:10]

# ╔═╡ 061e4894-647b-11eb-06db-310d02463b80
function molecular_weight(molecule)
	return missing
end

# ╔═╡ e6217d50-63de-11eb-28d8-452aeffc956c
begin

molecule1_mm = "CH3-CH2-CH2-CH2-CH2-COOH"
molecule2_mm = "COOH-CO-CO-CH2-CH2-CO-CO-CH2-CO-CO-CH2-CH2-CO-CH2-CH2-CH2-CO-CH2-CO-CH2-CH2-CO-CH2-CH2-CO-CH2-CO-CH2-CH2-CO-CO-CH2-CH2-CH2-CH2-CH2-CO-CO-CH2-CH2-CO-CH2-CO-CH2-CO-CH2-CH2-CO-CO-CO-CH2-CO-CH2-CH2-CO-CO-CH2-CH2-CH2-CH2-CO-CH2-CH2-CO-CO-CO-CH2-CO-CH2-CH2-CH2-CO-CO-CO-CO-CO-CO-CH2-CH2-CH2-CO-CO-CO-CO-CH2-CH2-CH2-CO-CH2-CO-CO-CO-CO-CO-CH2-CO-CO-CH2-CH2-CH3"

test1_mm = 	@safe[molecule1_mm |> Solutions.degrade == molecule1_mm |> degrade]

molecule1_mm = "CH3-CH2-CH2-CH2-CH2-COOH"
molecule2_mm = "COOH-CO-CO-CH2-CH2-CO-CO-CH2-CO-CO-CH2-CH2-CO-CH2-CH2-CH2-CO-CH2-CO-CH2-CH2-CO-CH2-CH2-CO-CH2-CO-CH2-CH2-CO-CO-CH2-CH2-CH2-CH2-CH2-CO-CO-CH2-CH2-CO-CH2-CO-CH2-CO-CH2-CH2-CO-CO-CO-CH2-CO-CH2-CH2-CO-CO-CH2-CH2-CH2-CH2-CO-CH2-CH2-CO-CO-CO-CH2-CO-CH2-CH2-CH2-CO-CO-CO-CO-CO-CO-CH2-CH2-CH2-CO-CO-CO-CO-CH2-CH2-CH2-CO-CH2-CO-CO-CO-CO-CO-CH2-CO-CO-CH2-CH2-CH3"

test2_mm = 	@safe[
		molecule1_mm |> Solutions.molecular_weight ==
			molecule1_mm |> molecular_weight,
		molecule2_mm |> Solutions.molecular_weight ==
			molecule2_mm |> molecular_weight,
		]

q_mm1 = Question(validators = test1_mm,
					description = md"""Happy with their discovery (less happy with the odours coming from their machine), the scientist take out a small vial... We wonder what would be the stable molecule obtained from this very expensive bottle filled with the molecule `verylongium`.""")

q_mm2 = Question(validators = test2_mm,
		description = md""" ...
		All of the sudden you hear, *\"Eureka!\"* from the other end of the corridor. It appears the chemical engineers finally figured out how to get their molecular generator working. This machine creates random molecules with a set number of carbon atoms `N`. They even show you how it works (see `generator` below), it is quite simple actually (try to understand which string manupilation are being used here).

The engineers would like you to analyse the molecules they generate, using the mass spectroscopy machine. They do not only want to know which molecules are obtained after degradation but also want to see the spread of the molecular weight of the degradation product.

Degrade *1000* molecules containing *12* carbon atoms, compute the molecular weight of each component and plot a histogram that depicts the relative frequency to the molecular weight of these components.

| Group | Molecular weight (g/mol) |
|-------|--------------------------|
| CH4   | 16.0                     |
| CH3   | 15.0                     |
| CH2   | 14.0                     |
| CO    | 28.0                     |
| OH    | 17.0                     |

"""

	)

qb_mm = QuestionBlock(;
	title=md"**Question 9: Molecular mass spectroscopy**",
	description = md"""
	Analytical chemists at Ghent University have discovered a new analytical technique where organic molecules are partially burned into smaller molecules. These oxidised molecules can then be detected via acetatometry.

	An organic molecule entering the analyser will always follow the same degradation pattern and subsequently,

	1. *CH₃*-endpoints will turn into *COOH*;
	2. *CO-COOH*-endpoints are unstable and will evaporate;
	3. Any *CH₂*-endpoints are unstable and will turn into *CH₃*;
	4. The first *-CH₂-CH₂-* wil react to *CO*;
	5. *CO* at the endpoints will be converted to *COOH*;
	6. A *CO-CO-CO* is also very unstable and will turn into *CO-CH₂-CO*.
	7. If the *CH2* or *CH3* is left at the very end, *CH4* is formed.

	This degradation pattern is repeated until a stable molecule is obtained.

	Let's take caproic acid for example,
		![](https://i.imgur.com/xsb9tGR.png)

	with a structural formula of CH3-CH2-CH2-CH2-CH2-COOH, caproic acid will degrade following this pattern,

	- Cycle 1: Step 1: CH₃ will turn into COOH: COOH-CH2-CH2-CH2-CH2-COOH.
	- Cycle 1: Step2: No CO-COOH to remove: COOH-CH2-CH2-CH2-CH2-COOH.
	- Cycle 1: Step3: No unstable CH₂ ends:  COOH-CH2-CH2-CH2-CH2-COOH.
	- Cycle 1: Step4: Reaction of the first -CH₂-CH₂- to CO: COOH-CO-CH2-CH2-COOH.
	- Cycle 1: Steps 5 & 6 do not change the molecule and a new cycle is repeated
	- Cycle 2: Step 1 does not change the molecule.
	- Cycle 2: Step 2 removes a single CO-COOH: CH2-CH2-COOH.
	- Cycle 2: Step 3: changes the molecule to: CH3-CH2-COOH.
	- ...
	- Cycle 3: step 1: changes the molecule CH3-CH2-COOH to **COOH-CH2-COOH**.

	  Additional cycles will not further degrade the structure of the molecule and can be considered stable. Caproic acid degraded into [malonic acid](https://en.wikipedia.org/wiki/Malonic_acid) (COOH-CH2-COOH)

	""",
	questions = [q_mm1, q_mm2],
	hints= [hint(md"Make sure you to check both endpoints CH₃-, -CH₃ and -CO-COOH or COOH-CO-, etc."),
			hint(md"""`replace(string1, \"-CH2-CH2-\" => \"-CO-\"; count=1)` replaces the first occurence of  "-CH2-CH2-" in `string1` to \"-CO-\". """),
			hint(md"""For part 2: use the function `count` and the function `histogram` from the Plots library"""),
			hint(md"""Type \"histogram\", put your cursor on histogram and open the "Live docs", to find informaiton on how to use the histogram function.""")
		]
)
	validate(qb_mm)
end

# ╔═╡ 89a9ad00-6489-11eb-2a2d-bfa18af103ab
#Plotting

# ╔═╡ 8ab56dcc-6489-11eb-16a4-b7d12003ed99

# ╔═╡ 0507186a-647a-11eb-07cf-ebf32bdff5b0

# ╔═╡ 01eb4816-4ad2-11eb-3991-af76de0110c5
begin
q9 = Question(validators = [],
		description = md"")

oq9 = QuestionOptional{Easy}(validators = [],
		description = md"Write a function that draws a similar grid with four rows and four columns.")

qb8 = QuestionBlock(;
	title=md"**Question 10: grid print**",
	description = md"""
	Complete the function `printgrid` that draws a grid like the following:
	```
	+ - - - - + - - - - +
	|         |         |
	|         |         |
	|         |         |
	|         |         |
	+ - - - - + - - - - +
	|         |         |
	|         |         |
	|         |         |
	|         |         |
	+ - - - - + - - - - +
	```

	""", questions = [q9, oq9],
		hints=[
				hint(md"""The function `print` does not advance to the next line."""),
				hint(md"""To print more than one value on a line, you can print a comma-separated sequence of values:

`println("+", "-")`
""")
			]
	)
	validate(qb8)
end

# ╔═╡ 448ef88e-4ad2-11eb-20d6-17a51d665ef9
function print_grid()
	missing
end

# ╔═╡ 14d50ee8-4ad3-11eb-3b81-9138aec66207
function print_big_grid()
	missing
end

# ╔═╡ 2bff11a1-7e39-43e5-8058-5d1496f43387
md"""## Answers:
If you would like to take a look at the answers, you can do so by checking the boxo the question you would like to see. The function will be shown just below the question you want to look at.

| Question | Show solution |
|-----|:---------:|
| Question 1 | $(@bind answ_q1 CheckBox()) |
| Question 2 | $(@bind answ_q2 CheckBox()) |
| Question 3 | $(@bind answ_q3 CheckBox()) |
| Question 4 | $(@bind answ_q4 CheckBox()) |
| Question 5 | $(@bind answ_q5 CheckBox()) |
| Question 6 | $(@bind answ_q6 CheckBox()) |
| Question 7 | $(@bind answ_q7 CheckBox()) |
| Question 8 | $(@bind answ_q8 CheckBox()) |
| Question 9 | $(@bind answ_q9 CheckBox()) |
| Question 10 | $(@bind answ_q10 CheckBox()) |
"""

# ╔═╡ c297d307-a640-4d86-aa0b-a149fe741bac
if answ_q1
	md"""
	```julia
	false
	true
	false
	```
	"""
end

# ╔═╡ 10a31233-5de3-401b-abdd-a27908e2de9d
if answ_q2
	md"""
	```julia
	statements = [
		true,
		true,
		true
	]
	```
	"""
end

# ╔═╡ 32c8ff60-9a35-4d40-8b54-b4805ca81885
if answ_q3
	md"""
	```julia
	function clip(x)
	  if x ≤ 0 && return 0.
	  elseif x ≥ 1 && return 1.
	  else
	  return x
	  end
	end
	```
	"""
end

# ╔═╡ a647ec42-1b3c-4280-958a-e2a96cd3d103
if answ_q4
	md"""
	`max` gives you the maximum of the given arguments while `maximum` gives you the max of the given collection.
	"""
end

# ╔═╡ 9e211fbf-4c43-439a-95ba-86b48767c053
if answ_q5
	md"""
	```julia
	stirling(n) = sqrt(2*π*n)*(n/exp(1))^n
	```
	"""
end

# ╔═╡ fd368fc2-2c64-463e-976e-9dd52ce80f13
if answ_q6
	md"""
	```julia
	function since_epoch(t)
	  now = t
	  seconds = now % 60 # 60 seconds in a minute
	  now -= seconds
	  # to minutes
	  now = div(now, 60)
	  minutes = now % 60 # 60 minutes in an hour
	  now -= minutes
	  # to hours
	  now = div(now, 60)
	  hours = now % 24 # 24 hours in a day
	  now -= hours
	  # to days
	  now = div(now, 24)
	  days = now % 365 # ± 365 days in a year
	  return days, hours, minutes, seconds
	end
	```
	"""
end

# ╔═╡ db78af32-b822-49dd-b085-6f51ba6b623f
if answ_q7
	md"""
	```julia
	function checkfermat(a::Int, b::Int, c::Int, n::Int)
	  if a^n + b^n == c^n
		  println("Holy smokes, Fermat was wrong!")
	  else
		  println("No, that doesn’t work.")
	  end
	end
	```
	"""
end

# ╔═╡ c8151b58-735c-4f16-b410-0c5c88818e6d
if answ_q8
	md"""
	```julia
	rightjustify(s) = println(" "^(70-length(s)) * s)
	```
	"""
end

# ╔═╡ c01bb803-2255-4164-b216-23ec7310f89f
if answ_q9
	md"""
	```julia
	f1(s) = replace(s, "-CH3"=> "-COOH") |> s -> replace(s, "CH3-"=> "COOH-")
	f2(s) = replace(s, "-CO-COOH"=> "") |> s -> replace(s, "COOH-CO-"=> "")

	function f3(molecule)
	  if length(molecule) < 4
		molecule = "CH4"
		return molecule
	  end

	  if molecule[1:4] == "CH2-"
		molecule = "CH3-" * molecule[5:end]
	  end
	  if molecule[end-3:end] == "-CH2"
		molecule = molecule[1:end-4] * "-CH3"
	  end
	  return molecule
	end

	f4(s) = replace(s, "-CH2-CH2-"=> "-CO-")

	function f5(molecule)
	  if molecule[1:3] == "CO-"
		molecule = "COOH-" * molecule[4:end]
	  end

	  if molecule[end-2:end] == "-CO"
		molecule = molecule[1:end-3] * "-COOH"
	  end
	  return molecule
	end

	f6(s) = replace(s, "CO-CO-CO" => "CO-CH2-CO")

	function cycle(molecule)
	  molecule |> f1 |> f2 |> f3 |> f4 |> f5 |> f6
	end

	function degrade(molecule)
	  mol_prev = ""
	  while mol_prev !== molecule
		mol_prev = molecule
		molecule = cycle(molecule)
	  end
	  return molecule
	end
	```
	"""
end

# ╔═╡ 743924b0-0b3b-48e2-9b93-961311d08022
if answ_q9
	md"""
	```julia
	function molecular_weight(molecule)
		return count("CH3", molecule)* 15.0 +
				count("CH4", molecule) * 16.0 +
				count("CH2", molecule) * 14.0 +
				count("CO", molecule) * 28.0 +
				count("OH", molecule) * 17
	end
	```
	"""
end

# ╔═╡ 15257785-52b5-4129-9e65-7fa15e83a6e3
if answ_q10
	md"""
	```julia
	function print_grid()
	  dashed = " -"^4*" "
	  plusdash = "+"*dashed*"+"*dashed*"+"
	  verticals = ("|"*" "^9)^2*"|"
	  s = (plusdash * "\n" * (verticals * "\n")^4)^2*plusdash * "\n"
	  println(s)
	end
	```
	"""
end

# ╔═╡ 0f095a13-8586-4da3-84ae-24c43a303776
if answ_q10
	md"""
	```julia
	function print_big_grid()
	  dashed = " -"^4*" "
	  plusdash = "+"*dashed*"+"*dashed*"+"
	  verticals = ("|"*" "^9)^2*"|"
	  s = (plusdash * "\n" * (verticals * "\n")^4)^2*plusdash * "\n"
	  println(s)
	end
	```
	"""
end

# ╔═╡ Cell order:
# ╟─f089cbaa-4ab9-11eb-09d1-05f49911487f
# ╠═e97e5984-4ab9-11eb-3efb-9f54c6c307dd
# ╟─fd21a9fa-4ab9-11eb-05e9-0d0963826b9f
# ╟─0f47f5b2-4aba-11eb-2e5a-b10407e3f928
# ╟─23d3c9cc-4abd-11eb-0cb0-21673effee6c
# ╟─62c3b076-4ab7-11eb-0cf2-25cdf7d2540d
# ╠═7bf5bdbe-4ab7-11eb-0d4b-c116e02cb9d9
# ╠═83306610-4ab7-11eb-3eb5-55a465e0abb9
# ╠═3fa0a15c-5008-11eb-13b5-a91b02c1eb2d
# ╠═83311b8a-4ab7-11eb-0067-e57ceabdfe9d
# ╠═833dbc66-4ab7-11eb-216d-f9900f95deb8
# ╠═8342c042-4ab7-11eb-2136-497fc9e1b9c4
# ╠═834d4cbc-4ab7-11eb-1f1a-df05b0c00d66
# ╠═8360ffac-4ab7-11eb-1162-f7a536eb0765
# ╠═8365cb3e-4ab7-11eb-05c0-85f51cc9b018
# ╠═8370eaf0-4ab7-11eb-1cd3-dfeec9341c4b
# ╠═50bb93e6-5a6c-11eb-0a6c-d5d749857771
# ╠═8383f104-4ab7-11eb-38a5-33e59b1591f6
# ╠═8387934a-4ab7-11eb-11b2-471b08d87b31
# ╠═8bab2e50-5a6c-11eb-3c5f-a9f811483814
# ╠═9d2708ca-5a6c-11eb-1c0f-473f0e2b5363
# ╟─8c14cb9a-4ab7-11eb-0666-b1d4aca00f97
# ╠═93b5a126-4ab7-11eb-2f67-290ed869d44a
# ╟─353efeea-6492-11eb-3d09-353d4dae491a
# ╠═92f57780-6492-11eb-1264-bbef04a8ae99
# ╠═a17ebeba-6492-11eb-07ba-f516a990affc
# ╟─3055711a-6493-11eb-252b-7f5d99115551
# ╠═962ae6d2-4ab7-11eb-14a2-c76a2221f544
# ╟─98d48302-4ab7-11eb-2397-710d0ae425f7
# ╟─acb3b57a-661c-11eb-2c6a-99793a47ff29
# ╠═cee8a766-4ab7-11eb-2bc7-898df2c9b1ff
# ╟─e2c5b558-4ab7-11eb-09be-b354fc56cc6e
# ╠═ec754104-4ab7-11eb-2a44-557e4304dd43
# ╟─f23a2d2a-4ab7-11eb-1e26-bb2d1d19829f
# ╠═fa836e88-4ab7-11eb-0ba6-5fc7372f32ab
# ╟─0138ef46-4ab8-11eb-1813-55594927d661
# ╠═0b73d66a-4ab8-11eb-06e9-bbe95285a69f
# ╟─6b6eb954-4ab8-11eb-17f9-ef3445d359a3
# ╠═94e3eb74-4ab8-11eb-1b27-573dd2f02b1d
# ╟─7592f8a2-4ac0-11eb-375c-61c915380eeb
# ╟─97b7f7d8-61aa-11eb-2bf6-739bd5f2e941
# ╠═dcf78e38-61a7-11eb-1ac8-9927f5dca4f2
# ╟─abf00a78-4ab8-11eb-1063-1bf4905ca250
# ╠═be220a48-4ab8-11eb-1cd4-db99cd9db066
# ╠═cadaf948-4ab8-11eb-3110-259768055e85
# ╠═cadb506e-4ab8-11eb-23ed-2d5f88fd30b0
# ╠═caf56346-4ab8-11eb-38f5-41336c5b45a7
# ╟─046133a8-4ab9-11eb-0591-9de27d85bbca
# ╠═0c8bc7f0-4ab9-11eb-1c73-b7ec002c4155
# ╠═0f8a311e-4ab9-11eb-1b64-cd62b65c49bf
# ╠═0f8a5e94-4ab9-11eb-170b-cfec74d6ebbc
# ╠═0f96fdd6-4ab9-11eb-0e33-2719394a66ba
# ╟─1f255304-4ab9-11eb-34f1-270fd5a95256
# ╠═34a18900-4ab9-11eb-17a0-1168dd9d06f9
# ╟─39a0a328-4ab9-11eb-0f37-6717095b56aa
# ╠═4749f268-4ab9-11eb-15a7-579437e0bd20
# ╟─5a9bbbe6-4aba-11eb-3652-43eb7891f437
# ╟─6bdc8a5e-4aba-11eb-263c-df3af7afa517
# ╠═77c5280c-58d0-11eb-19ad-83ec82b6d7c7
# ╟─c297d307-a640-4d86-aa0b-a149fe741bac
# ╟─a69ead46-4abc-11eb-3d1d-eb1c73f65150
# ╠═b482b998-4abc-11eb-36da-379010485bfa
# ╟─07b103ae-4abd-11eb-311b-278d1e033642
# ╠═15f8b7fe-4abd-11eb-2777-8fc8bf9d342e
# ╠═efae58fa-5008-11eb-32fe-c3ae588d14f2
# ╠═18f99e46-4abd-11eb-20a8-859cb1b12fe3
# ╟─3a7954da-4abd-11eb-3c5b-858054b4d06b
# ╟─8b17d538-4abd-11eb-0543-ab95c9548d6f
# ╠═29d34e64-5009-11eb-3301-f729150e17b2
# ╠═91a9d1a0-4abd-11eb-3337-71983f32b6ae
# ╠═942d4202-4abd-11eb-1f01-dfe3df40a5b7
# ╠═942dae0e-4abd-11eb-20a2-37d9c9882ba8
# ╠═943d9850-4abd-11eb-1cbc-a1bef988c910
# ╠═943de2ce-4abd-11eb-2410-31382ae9c74f
# ╠═9460c03c-4abd-11eb-0d60-4d8aeb5b0c1d
# ╠═946161f4-4abd-11eb-0ec5-df225dc140d0
# ╠═947d143a-4abd-11eb-067d-dff955c90407
# ╠═947fea8e-4abd-11eb-1d6a-2bc540f7a50e
# ╠═948eff10-4abd-11eb-36d0-5183e882a9e2
# ╠═948f5032-4abd-11eb-3d1c-7da4cb64521c
# ╠═94b520e6-4abd-11eb-3161-addf3b0e4f24
# ╠═94b78322-4abd-11eb-3006-454548efd164
# ╠═94d28c80-4abd-11eb-08c0-717207e4c682
# ╟─9fe6e1a2-4abd-11eb-0c39-458ce94265c0
# ╠═ae26ab9e-4abd-11eb-3270-33558dbdf663
# ╠═b08dc886-4abd-11eb-1807-096a7e6fd6f9
# ╠═b08e3a28-4abd-11eb-258a-a5a93b4b882c
# ╠═b0a8dfe0-4abd-11eb-167d-2fc3974c7c92
# ╠═b0a97e00-4abd-11eb-371c-e138aea17bb6
# ╠═b0ccc252-4abd-11eb-048b-4bec3750bbf1
# ╟─60b066d8-5009-11eb-3b4c-8b8fa2f4831d
# ╠═b8b7c0f6-5009-11eb-1eb7-0be723a88863
# ╟─bd446c42-4abd-11eb-0465-d9a61c48ff48
# ╠═1c5975ec-4abe-11eb-0ff0-bfb2f03a520b
# ╟─10a31233-5de3-401b-abdd-a27908e2de9d
# ╟─1c22b880-4abf-11eb-3f18-756c1198ccad
# ╟─37086212-4abf-11eb-3ec9-7f8dae57121e
# ╠═489421d8-4abf-11eb-0d5e-fd779cc918a1
# ╟─6736dafe-4abf-11eb-1fce-0716d2b7f4a8
# ╟─8933033a-4abf-11eb-1156-a53a5ee9152c
# ╠═0c693c24-4ac0-11eb-2329-c743dcc5039d
# ╟─32c8ff60-9a35-4d40-8b54-b4805ca81885
# ╟─035a53ba-4ac1-11eb-3c34-b50a803b7b7d
# ╟─2a5fca7c-4ac0-11eb-33a3-23d972ca27b8
# ╠═3896642a-4ac0-11eb-2c7c-4f376ab82217
# ╠═3ef3faf8-4ac0-11eb-1965-fd23413e29f3
# ╟─3916f50e-661d-11eb-0829-cb3821836fdf
# ╠═4118016e-4ac0-11eb-18bf-5de326782c87
# ╠═4119fbca-4ac0-11eb-1ea9-0bdd324214c5
# ╟─5ebe25c0-661d-11eb-389b-3d81570f7cf0
# ╠═4139bf3c-4ac0-11eb-2b63-77a513149351
# ╟─a1d4127c-4ac0-11eb-116f-79c6ee58f524
# ╠═a93b28e6-4ac0-11eb-074f-a7b64f43a194
# ╠═ac35b796-4ac0-11eb-3bc5-5ff4350d5452
# ╠═b18e55ae-4ac0-11eb-1455-21b83b7c61d5
# ╟─ec4190a8-4ac0-11eb-0421-398f063775bb
# ╟─7dce10be-500c-11eb-12cf-43f6989aae91
# ╠═bafbe0ea-500c-11eb-10be-75d1738d42a2
# ╠═2033383e-500d-11eb-3aea-cdc1e90dad2c
# ╟─fdb67aba-4ac0-11eb-1d4a-c354de54baa9
# ╠═28f47a24-4ac1-11eb-271f-6b4de7311db3
# ╠═3e433ab4-4ac1-11eb-2178-53b7220fa9ab
# ╠═46112c44-4ac1-11eb-2ad8-030406c7cf67
# ╠═461489fa-4ac1-11eb-0596-1d3bedb61778
# ╟─47338c78-4ac1-11eb-04d6-35c2361eaea6
# ╠═463689b0-4ac1-11eb-1b0f-b7a239011c5c
# ╟─52bfff04-4ac1-11eb-1216-25eedd9184c3
# ╠═61846dae-4ac1-11eb-389a-4fbe3f6145b1
# ╠═6321ae1a-4ac1-11eb-04cb-33e939694874
# ╟─3eda5d12-5a6e-11eb-3264-9925907c85b5
# ╟─7b874424-4ac1-11eb-2d4e-0b4607559b8f
# ╠═86defe2a-4ac1-11eb-3c01-c5e671877212
# ╠═886512de-4ac1-11eb-00e1-73292ec23277
# ╠═88678820-4ac1-11eb-272e-0df61e418900
# ╠═888dee1e-4ac1-11eb-264d-cd4a4f30f498
# ╟─8acb086c-4ac1-11eb-1715-756fde34b38f
# ╠═944e1aaa-4ac1-11eb-0e23-41b1c5d0e889
# ╠═9d4e11be-4ac1-11eb-1fa0-13f1fe60c3bc
# ╠═9d514ef6-4ac1-11eb-25fc-ffaa2dcc9b02
# ╟─a0781222-4ac1-11eb-3425-d9b9603487f3
# ╠═a6b95d62-4ac1-11eb-0c93-7fa0f6a120d5
# ╠═ab006064-4ac1-11eb-32be-6557b8d45f32
# ╟─b0603566-4ac1-11eb-17bc-3b63cd2aa1e9
# ╟─b6db7e2a-5a6e-11eb-1a55-e74b15cbb9e2
# ╟─a647ec42-1b3c-4280-958a-e2a96cd3d103
# ╠═beafce06-4ac1-11eb-2431-1ffeba45716b
# ╟─ec487488-4ac1-11eb-1be3-a93e41f78bf3
# ╠═f88fee6c-4ac1-11eb-1671-43493122f061
# ╠═fa759f92-4ac1-11eb-0d72-1f9d6d38a831
# ╠═fa7ba458-4ac1-11eb-2ca1-59ff3c032b26
# ╠═fa9b3266-4ac1-11eb-153a-87c6a1124890
# ╠═fa9d43b2-4ac1-11eb-33fc-a37503cedabf
# ╟─0dbe0c34-500e-11eb-2633-67d8dc6b24c8
# ╠═900da226-500e-11eb-3f1c-5582ed0cf749
# ╠═b24bee9a-500e-11eb-2130-45b78bbebdec
# ╟─c8cf6520-500e-11eb-2dd3-4507a3994a83
# ╠═19402512-500f-11eb-3ea7-c3d1c52025d6
# ╟─6dc0eaae-500f-11eb-3051-37d870366201
# ╠═8b57c506-500f-11eb-3114-55785eb593a7
# ╟─fd171e0e-4ac1-11eb-09ea-337d17500149
# ╠═10e71260-4ac2-11eb-1069-55613ee7df0a
# ╠═1c0230f8-4ac2-11eb-32aa-e7a4b2ae9cff
# ╠═226417c2-4ac2-11eb-2914-196461e2b40e
# ╟─3daf4fa6-4ac2-11eb-0541-b98c2e97dfe4
# ╟─a777f624-4f2f-11eb-0595-432ea5115a2d
# ╟─abb7bf4e-4f2f-11eb-1dde-abf1cb0fb8b4
# ╠═b63533e8-4f2f-11eb-3811-5f43fcaa2a9e
# ╠═b730b7b8-4f2f-11eb-3216-613c6100fb9c
# ╠═b99eb16c-4f2f-11eb-0f1e-5d2b1244689f
# ╠═bc67fc50-4f2f-11eb-2209-bf35df0e8c27
# ╠═bf40274a-4f2f-11eb-0086-d3502f67161b
# ╟─c48e035c-4f2f-11eb-12c7-576aadca7511
# ╟─c8435740-4f2f-11eb-3046-3d45d5e0e805
# ╠═cacb7254-4f2f-11eb-1daa-1bc04678835c
# ╠═cc48bc9a-4f2f-11eb-134c-71bd8a944943
# ╠═d9f28c04-4f2f-11eb-0255-1965fb8f07b5
# ╠═dc1dbe90-4f2f-11eb-05ce-c1fe46ae14dd
# ╟─de48a3f6-4f2f-11eb-314b-493546c37a21
# ╟─6da71180-4ac2-11eb-1cac-410bd1cce70c
# ╠═85b96ff0-4ac2-11eb-077f-cf4aad8a3c24
# ╟─a11c2898-4ac2-11eb-24d3-6f8060b5fd65
# ╠═a686e67e-4ac2-11eb-228e-23524a3ddc59
# ╟─d50cced2-500d-11eb-2dcc-21fc50825f43
# ╟─ad156892-4ac2-11eb-3634-a3783231e5a1
# ╠═bf1385da-4ac2-11eb-3992-41abac921370
# ╟─d779956a-4ac2-11eb-39de-4b3cecace452
# ╠═c7d2a048-4ac2-11eb-3902-b7c8505096ae
# ╠═cf35b2b2-4ac2-11eb-1ae6-5d3c108210df
# ╠═d1010f88-4ac2-11eb-0fa9-0902fef0cf9f
# ╟─0e63d722-4ac3-11eb-3740-d31b47a77912
# ╟─a48be23a-57ff-11eb-1bc3-3d3e046ea67c
# ╠═a3969292-57ff-11eb-059b-e9e931a30dc1
# ╟─9e211fbf-4c43-439a-95ba-86b48767c053
# ╟─adb47b64-5af8-11eb-1b32-57cbe5d47200
# ╟─c34ede1c-4ad4-11eb-050f-bb07c5d19c1c
# ╠═0cd2d0e4-59e1-11eb-112e-83ebe626f597
# ╠═0c306fd8-4ad5-11eb-1a9f-2d3d1e838a77
# ╠═40557d76-661e-11eb-3df0-659c6095285d
# ╟─fd368fc2-2c64-463e-976e-9dd52ce80f13
# ╟─b1af96ea-5af8-11eb-0d08-f59a4c2b686c
# ╟─e99d6b96-4ad5-11eb-2144-f97a97e71ae4
# ╠═bf53d86c-59e1-11eb-1456-5518e1f63390
# ╟─da5537b5-88e7-4377-90c6-abbc7eaf6495
# ╟─b4118bfa-5af8-11eb-0aca-cddef8e191ee
# ╟─db78af32-b822-49dd-b085-6f51ba6b623f
# ╟─f077c390-57fe-11eb-1ad9-31110b3dac39
# ╟─42f24f58-4ac3-11eb-06b5-ebc015c17520
# ╠═87871f34-4ad1-11eb-3903-93e3f63ea14a
# ╟─c8151b58-735c-4f16-b410-0c5c88818e6d
# ╟─e2583a06-6479-11eb-091b-8be70024930d
# ╟─e6217d50-63de-11eb-28d8-452aeffc956c
# ╠═1f588828-6477-11eb-0c70-557f130c6785
# ╠═4d0e1d60-6476-11eb-154e-d9cc3bf284c2
# ╠═5167ded6-6463-11eb-3279-0dd226fae82e
# ╠═c4d03faa-647d-11eb-3a84-d7b2d53e4720
# ╟─c01bb803-2255-4164-b216-23ec7310f89f
# ╟─c42f69f6-647d-11eb-143b-2f0ab33616e7
# ╠═c55ed390-647d-11eb-22d5-33fbae27a2bb
# ╠═23db2764-647e-11eb-37dd-a34db2ff0fcd
# ╠═989d0dd2-647a-11eb-3123-39786452714a
# ╠═061e4894-647b-11eb-06db-310d02463b80
# ╠═89a9ad00-6489-11eb-2a2d-bfa18af103ab
# ╠═8ab56dcc-6489-11eb-16a4-b7d12003ed99
# ╟─743924b0-0b3b-48e2-9b93-961311d08022
# ╟─0507186a-647a-11eb-07cf-ebf32bdff5b0
# ╟─01eb4816-4ad2-11eb-3991-af76de0110c5
# ╠═448ef88e-4ad2-11eb-20d6-17a51d665ef9
# ╟─15257785-52b5-4129-9e65-7fa15e83a6e3
# ╠═14d50ee8-4ad3-11eb-3b81-9138aec66207
# ╟─0f095a13-8586-4da3-84ae-24c43a303776
# ╟─2bff11a1-7e39-43e5-8058-5d1496f43387


text:
# Don't taint the value enclosed by Fixed
struct Fixed
    value
end

# Get the type of the metadata attached to a value
function metatype(x, ctx)
    if  istagged(x, ctx) && hasmetadata(x, ctx)
        typeof(metadata(x, ctx))
    else
        Cassette.NoMetaData
    end
end

# generic implementations

_name(x::Symbol) = x
_name(x::Expr) = (@assert x.head == :(::); x.args[1])
macro reroute(f, g)
    fname = f.args[1]
    fargs = f.args[2:end]
    gname = g.args[1]
    gargs = g.args[2:end]
    quote
        @inline function Cassette.overdub(ctx::JacobianSparsityContext,
                                          f::typeof($(esc(fname))),
                                          $(fargs...))
            Cassette.recurse(
                ctx,
                invoke,
                $(esc(gname)),
                $(esc(:(Tuple{$(gargs...)}))),
                $(map(_name, fargs)...))
        end

        @inline function Cassette.overdub(ctx::HessianSparsityContext,
                                          f::typeof($(esc(fname))),
                                          $(fargs...))
            Cassette.recurse(
                ctx,
                invoke,
                $(esc(gname)),
                $(esc(:(Tuple{$(gargs...)}))),
                $(map(_name, fargs)...))
        end
    end
end


text:
#############################################################################
#############################################################################
#
# This file implements factorization
#
#############################################################################
#############################################################################

"""
Factors a polynomial over the field Z_p.

Returns a vector of tuples of (irreducible polynomials (mod p), multiplicity) such that their product of the list (mod p) is f. Irreducibles are fixed points on the function factor.
"""
function factor(f::Polynomial, prime::Int)::Vector{Tuple{Polynomial,Int}}
    #Cantor Zassenhaus factorization

    f_modp = mod(f, prime)
    degree(f_modp) ≤ 1 && return [(f_modp,1)]

    # make f primitive
    ff = prim_part(f_modp)(prime)
    # @show "after prim:", ff

     # make f square-free
    squares_poly = gcd(f, derivative(ff), prime)
    ff = (ff ÷ squares_poly)(prime)
    # @show "after square free:", ff

    # make f monic
    old_coeff = leading(ff).coeff
    ff = (ff ÷ old_coeff)(prime)
    # @show "after monic:", ff

    dds = dd_factor(ff, prime)

    ret_val = Tuple{Polynomial,Int}[]

    for (k,dd) in enumerate(dds)
        sp = dd_split(dd, k, prime)
        sp = map((p)->(p ÷ leading(p).coeff)(prime),sp) #makes the polynomials inside the list sp, monic
        for mp in sp
            push!(ret_val, (mp, multiplicity(f_modp,mp,prime)) )
        end
    end

    #Append the leading coefficient as well
    push!(ret_val, (leading(f_modp).coeff* one(Polynomial), 1) )

    return ret_val
end

"""
Expand a factorization.
"""
function expand_factorization(factorization::Vector{Tuple{Polynomial,Int}})::Polynomial
    length(factorization) == 1 && return first(factorization[1])^last(factorization[1])
    return *([first(tt)^last(tt) for tt in factorization]...)
end

"""
Compute the number of times g divides f
"""
function multiplicity(f::Polynomial, g::Polynomial, prime::Int)::Int
    degree(gcd(f, g, prime)) == 0 && return 0
    return 1 + multiplicity((f ÷ g)(prime), g, prime)
end

"""
Distinct degree factorization.

Given a square free polynomial `f` returns a list, `g` such that `g[k]` is a product of irreducible polynomials of degree `k` for `k` in 1,...,degree(f) ÷ 2, such that the product of the list (mod `prime`) is equal to `f` (mod `prime`).
"""
function dd_factor(f::Polynomial, prime::Int)::Array{Polynomial}
    x = x_poly()
    w = deepcopy(x)
    g = Array{Polynomial}(undef,degree(f)) #Array of polynomials indexed by degree

    #Looping over degrees
    for k in 1:degree(f)
        w = rem(pow_mod(w,prime,prime), f)(prime)
        g[k] = gcd(w - x, f, prime)
        f = (f ÷ g[k])(prime)
    end

    #edge case for final factor
    f != one(Polynomial) && push!(g,f)

    return g
end

"""
Distinct degree split.

Returns a list of irreducible polynomials of degree `d` so that the product of that list (mod prime) is the polynomial `f`.
"""
function dd_split(f::Polynomial, d::Int, prime::Int)::Vector{Polynomial}
    f = mod(f,prime)
    degree(f) == d && return [f]
    degree(f) == 0 && return []
    w = rand(Polynomial, degree = d, monic = true)
    w = mod(w,prime)
    n_power = (prime^d-1) ÷ 2
    g = gcd(pow_mod(w,n_power,prime) - one(Polynomial), f, prime)
    ḡ = (f ÷ g)(prime) # g\bar + [TAB]
    return vcat(dd_split(g, d, prime), dd_split(ḡ, d, prime) )
end


text:
# # [Basics of wiring diagrams](@id wiring_diagram_basics)
#
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/wiring_diagrams/wiring_diagram_basics.ipynb)
#
# Using Catlab, you can create, manipulate, serialize, and visualize *wiring
# diagrams*, also known as [string
# diagrams](https://ncatlab.org/nlab/show/string+diagram). The flexible data
# structure for wiring diagrams allows arbitrary data to be attached to boxes,
# ports, and wires, and supports recursively nested diagrams.
#
# You can interact with wiring diagrams using two different progamming
# interfaces:
#
# 1. **Categorical**: A high-level, functional interface expressed in terms of
#    categorical concepts, such as composition (`compose`), monoidal products
#    (`otimes`), duplication (`mcopy`), and deletion (`delete`).
#
# 2. **Imperative**: A lower-level, mutating interface to directly manipulate
#    boxes, ports, and wires, via operations like adding boxes (`add_box`) and
#    wires (`add_wire`).
#
# In this notebook, we introduce both interfaces. We do not explicitly cover the
# visualization API, although for illustrative purposes we will draw wiring
# diagrams using Graphviz. Thus, you should install
# [Graphviz](https://www.graphviz.org/) if you wish to run this notebook.

using Catlab.WiringDiagrams

using Catlab.Graphics
import Catlab.Graphics: Graphviz

show_diagram(d::WiringDiagram) = to_graphviz(d,
  orientation=LeftToRight,
  labels=true, label_attr=:xlabel,
  node_attrs=Graphviz.Attributes(
    :fontname => "Courier",
  ),
  edge_attrs=Graphviz.Attributes(
    :fontname => "Courier",
  )
)

# ## Data structures
#
# The basic building blocks of a wiring diagram are boxes, ports, and wires. The
# top-level data structure is `WiringDiagram`, defined in the module
# `Catlab.WiringDiagrams`. A wiring diagram consists of boxes (usually of type
# `Box`) connected by wires (of type `Wire`). Each box has a sequence of input
# ports and a sequence of output ports, as does the wiring diagram itself. The
# wires have sources and targets, both of which consist of a box and a port on
# that box.
#
# The boxes in a wiring diagram are indexed by integer IDs. Boxes can be
# retrieved by ID, and wires refer to boxes using their IDs. Two special IDs,
# obtained by `input_id` and `output_id` methods, refer to the inputs and
# outputs of the diagram itself. In this way, wires can connect the (inner)
# boxes of a diagram to the diagram's "outer box".
#
# The `WiringDiagram` data structure is an elaborate wrapper around a directed
# graph from [LightGraphs.jl](https://github.com/JuliaGraphs/LightGraphs.jl).
# The underlying `DiGraph` object can be accessed using the `graph` method. The
# vertices of this graph are exactly the box IDs. The graph should never be
# mutated directly, on pain of creating inconsistent state, but it does allow
# convenient access to the large array of [graph
# algorithms](https://juliagraphs.github.io/LightGraphs.jl/latest/) supported by
# LightGraphs.
#
# All this is somewhat abstract but should become clearer as we see concrete
# examples.

# ## Categorical interface
#
# In this example, the wiring diagrams will carry symbolic expressions (of type
# `Catlab.ObExpr` and `Catlab.HomExpr`).

using Catlab.Theories

A, B, C, D = Ob(FreeBiproductCategory, :A, :B, :C, :D)
f = Hom(:f, A, B)
g = Hom(:g, B, C)
h = Hom(:h, C, D)

f

# ### Generators
#
# Convert each of the morphism generators into a diagram with a single box.

f, g, h = to_wiring_diagram(f), to_wiring_diagram(g), to_wiring_diagram(h)
f
#-
show_diagram(f)

# ### Composition

compose(f,g)
#-
show_diagram(compose(f,g))

# ### Monoidal products

otimes(f,h)
#-
show_diagram(otimes(f,h))

# ### Copy and merge, delete and create

mcopy(codom(f),2)
#-
show_diagram(mcopy(codom(f),2))
#-
show_diagram(compose(f, mcopy(codom(f),2)))
#-
show_diagram(compose(mcopy(dom(f),2), otimes(f,f)))

# ## Imperative interface
#
# We now show how to manipulate wiring diagrams using the low-level, imperative
# interface. The diagrams will carry Julia symbols.

f = Box(:f, [:A], [:B])
g = Box(:g, [:B], [:C])
h = Box(:h, [:C], [:D])

f

# ### Composition
#
# For example, here is how to manually construct a composition of two boxes.
#
# The `add_box!` method adds a box to a wiring diagrams and returns the ID
# assigned to the box. How the boxes are indexed is an implementation detail
# that you should not rely on; use the IDs that the system gives you.

d = WiringDiagram([:A], [:C])

fv = add_box!(d, f)
gv = add_box!(d, g)

add_wires!(d, [
  (input_id(d),1) => (fv,1),
  (fv,1) => (gv,1),
  (gv,1) => (output_id(d),1),
])

nboxes(d)
#-
nwires(d)
#-
d
#-
show_diagram(d)

# ### Products
#
# Here is how to manually construct a product of two boxes.

d = WiringDiagram([:A,:C], [:B,:D])

fv = add_box!(d, f)
hv = add_box!(d, h)

add_wires!(d, [
  (input_id(d),1) => (fv,1),
  (input_id(d),2) => (hv,1),
  (fv,1) => (output_id(d),1),
  (hv,1) => (output_id(d),2),
])

d
#-
show_diagram(d)


text:
struct Advection <: Equation end
@declare_dofs Advection [:rho1, :rho2, :rho3]

struct PlanarWaves <: Scenario
end

function is_periodic_boundary(equation::Advection, scenario::PlanarWaves)
    true
end

function get_initial_values(eq::Advection, scenario::PlanarWaves, global_position; t=0.0)
    pxg, pyg = global_position

    # Solution is ρ(x,t) = ρ(x - at, 0)
    # So we just adjust the global_position

    # Takes periodic boundary conditions into account
    # Not elegant, but works ;)
    function adj_coord(x)
        while (x < 0)
            x += 1.0
        end
        while (x > 1.0)
            x -= 1.0
        end
        x
    end

    pxg = adj_coord(pxg - t)
    pyg = adj_coord(pyg - t)

    # And then use the initial condition.
    k = (2 * pi) # wave number
    val1 = sin(k * (pxg + pyg))
    val2 = sin(k * (pyg))
    val3 = 1
    [val1, val2, val3]
end

function is_analytical_solution(equation::Advection, scenario::PlanarWaves)
    true
end

function evaluate_flux(eq::Advection, celldofs, cellflux)
    s = AdvectionShortcuts()
    cellflux .= 0.0
    for pointidx=1:size(celldofs, 1)
        for dimension=1:2
            if dimension == 1
                cellflux[1,1] = 1 * celldofs[s.rho1]
                cellflux[1,2] = 1 * celldofs[s.rho2]
                cellflux[1,3] = 1 * celldofs[s.rho3]
            else
                @assert(dimension == 2)
                cellflux[2,1] = 1 * celldofs[s.rho1]
                cellflux[2,2] = 1 * celldofs[s.rho2]
                cellflux[2,3] = 1 * celldofs[s.rho3]
            end
        end
    end
end

function max_eigenval(eq::Advection, celldata, normalidx)
    1.0
end


text:
@userplot Scope

"""
    scope(sol, varnames)
    scope!(sol, varnames)

Plot the variables in `varnames` from an `ODESolution` `sol`.
If `varnames` is a vector, the variables will be plotted against
time on the same axis. If `varnames` is a tuple, the variables
will be plotted against each other.
"""
scope, scope!

@recipe function f(s::Scope)
    sol, vars = s.args
    t = range(extrema(sol.t)..., length=2000)
    signals = get_log(sol, t)
    seriestype := :path
    xguide --> "t"
    if vars isa Symbol
        vars = [vars]
    end

    if vars isa AbstractArray
        for var in vars
            @series begin
                x = getproperty(signals, var)
                if x isa AbstractMatrix
                    sizes = string.(permutedims(1:size(x, 2)))
                    label --> string(var) * "[:," .* sizes .* "]"
                else
                    label --> "$(string(var))"
                end
                t, x
            end
        end

    else vars isa Tuple
        @series begin
            label --> "("*join(string.(vars), ", ")*")"
            map(var->getproperty(signals, var), vars)
        end
    end
end

max_stars_repo_name: jonniedie/SimulationLogs

max_issues_repo_name: jonniedie/SimulationLogs

max_forks_repo_name: jonniedie/SimulationLogs


text:
@testset "patch" begin
    @testset "unnamed function" begin
        @test_throws LoadError macroexpand(@__MODULE__, :(@patch () -> nothing))
    end

    @testset "non-function definition" begin
        @test_throws LoadError macroexpand(@__MODULE__, :(@patch f()))
    end

    @testset "empty-function definition" begin
        @test_throws LoadError macroexpand(@__MODULE__, :(@patch function f end))
    end

    @testset "non-function definition" begin
        f() = 1
        p = @patch f() = 2
        @test p.target() == 1
        @test p.alternate() == 2
    end
end


text:
using Test
using WordTokenizers

@testset "default behaviour" begin
	str = "Is 9.5 or 525,600 my favorite\u00A0 number?"
	tokenized = ["Is", "9.5", "or", "525,600", "my", "favorite", "number", "?"]

	@test tokenized == toktok_tokenize(str)

	str = "This \xa1124, is a sentence with weird \u00a2symbols \u2026 appearing everywhere \xbf"
	tokenized = ["This", "\xa1", "124", ",", "is", "a", "sentence", "with", "weird", "\u00a2", "symbols",
                      "\u2026", "appearing", "everywhere", "\xbf"]
	@test tokenized == toktok_tokenize(str)
end

@testset "URL types" begin
    str = "The https://github.com/jonsafari/tok-tok/blob/master/tok-tok.pl is a website with/and/or slashes and sort of weird : things"
    tokenized = ["The", "https://github.com/jonsafari/tok-tok/blob/master/tok-tok.pl", "is", "a", "website", "with/and/or", "slashes", "and", "sort", "of", "weird", ":", "things"]

    @test tokenized == toktok_tokenize(str)

    @testset "url_handler 1" begin
        str = "https://example.com:8085"
        tokenized = ["https://example.com", ":", "8085"]
        @test tokenized == toktok_tokenize(str)
    end

    @testset "url_handler 2" begin
        str = "https://www.google.com/search?q=example? "
        tokenized = ["https://www.google.com/search?q=example", "?"]
        @test tokenized == toktok_tokenize(str)
    end

    @testset "url_handler 3" begin
        str = "https://+/mxl.com, https://google.com"
        tokenized = ["https", "/", "mxl.com", ",", "https://google.com"]
        @test tokenized == toktok_tokenize(str)
    end

    @testset "url_handler 4" begin
        str = "https://example.com /example"
        tokenized = ["https://example.com", "/", "example"]
        @test tokenized == toktok_tokenize(str)

		str = "'re too Buff \\/ Fred thinks he 's tough \\/"
        tokenized = ["'","re", "too", "Buff", "\\", "/", "Fred", "thinks",
                     "he", "'", "s", "tough", "\\", "/"]
        @test tokenized == toktok_tokenize(str)
    end
end

@testset "multi-lingual" begin
    french = "Maître Corbeau, sur un arbre perché,
    Tenait en son bec un fromage.
    Maître Renard, par l’odeur alléché,
    Lui tint à peu près ce langage :
    « Hé ! bonjour, Monsieur du Corbeau.
    Que vous êtes joli ! Que vous me semblez beau !
    Sans mentir, si votre ramage
    Se rapporte à votre plumage,
    Vous êtes le Phénix des hôtes de ces bois. »
    A ces mots le Corbeau ne se sent pas de joie ;
    Et pour montrer sa belle voix,
    Il ouvre un large bec, laisse tomber sa proie.
    Le Renard s’en saisit, et dit : « Mon bon Monsieur,
    Apprenez que tout flatteur
    Vit aux dépens de celui qui l’écoute :
    Cette leçon vaut bien un fromage, sans doute. »
    Le Corbeau, honteux et confus,
    Jura, mais un peu tard, qu’on ne l’y prendrait plus. "

    old_english = "An. M.LXVI. On þyssum geare man halgode þet
    mynster æt Westmynstre on Cyldamæsse dæg 7 se cyng Eadward forðferde
    on Twelfts mæsse æfen 7 hine mann bebyrgede on Twelftan mæssedæg innan
    þære niwa halgodre circean on Westmyntre 7 Harold eorl feng to Englalandes
    cynerice swa swa se cyng hit him geuðe 7 eac men hine þærto gecuron 7 wæs
    gebletsod to cynge on Twelftan mæssedæg 7 þa ylcan geare þe he cyng wæs he
    for ut mid sciphere togeanes Willelme ... 7 þa hwile com Willelm eorl upp æt
    Hestingan on Sce Michaeles mæssedæg 7 Harold com norðan 7 him wið gefeaht ear
    þan þe his here com eall 7 þær he feoll 7 his twægen gebroðra Gyrð 7 Leofwine
    and Willelm þis land geeode 7 com to Westmynstre 7 Ealdred arceb hine to cynge
    gehalgode 7 menn guldon him gyld 7 gislas sealdon 7 syððan heora land bohtan. "

    farsi = "مادهٔ بیست و ششم
    1) هر کس حق دارد که از آموزش و پرورش بهره‌مند شود.
    آموزش و پرورش لااقل تا حدودی که مربوط بتعلیمات ابتدائی و اساسی است باید
    مجانی باشد. آموزش ابتدائی اجباری است. آموزش حرفه‌ای باید با شرایط تساوی
    کامل بروی همه باز باشد تا همه بنا باستعداد خود بتوانند از آن بهره‌مند گردند.
    2) آموزش و پرورش باید طوری هدایت شود که شخصیت انسانی هر کس را
    بحد اکمل رشد آن برساند و احترام حقوق و آزادی‌های بشر را تقویت کند. آموزش و
    پرورش باید حسن تفاهم، گذشت و احترام عقاید مخالف و دوستی بین تمام ملل و جمعیتهای
    نژادی یا مذهبی و همچنین توسعه فعالیتهای ملل متحد را در راه حفظ صلح تسهیل نماید.
    3) پدر و مادر در انتخاب نوع آموزش و پرورش فرزندان خود نسبت بدیگران اولویت دارند.
    مادهٔ بیست و هفتم
    1) هر کس حق دارد آزادانه در زندگی فرهنگی اجتما
    عی شرکت کند، از فنون و هنرها متمتع گردد و در پیشرفت علمی و فوائد آن سهیم باشد.
    2) هر کس حق دارد از حمایت منافع معنوی و مادی آثار علمی، فرهنگی یا هنری خود برخوردار شود."

    russian = "Лорем ипсум долор сит амет, яуи ин реяуе пертинациа, еа ест яуод ехпетенда
    витуператорибус. Еум ут дицант граеци саперет, еу тамяуам епицуреи елецтрам сед. Аеяуе анциллае
    пер ид, вим ностер албуциус вивендум ин, цонгуе долоре сит но. Ид еум алияуип интерпретарис,
    легере пертинах малуиссет ин усу. Еам еу еиус поссе.
    Сеа еи малорум ассентиор. Алии мутат персиус усу но, цу вих ирацундиа цонсететур, цоррумпит
    форенсибус диссентиунт но иус. Ессе цибо нонумес ин сеа. Доминг еурипидис модератиус сеа ут,
    алии иллуд граецис ет сед. Цу путент десеруиссе еам."

    spanish = "Mentiría si dijera que era del todo nuevo el sentimiento de que ya no iba a poder
    ser más que lo que era, que era un hombre que había envejecido más de lo que suponía, que había
    sospechado tener toda la vida por delante y había ido dejando pasar los años a la espera de que
    llegara su momento, y ahora la tenía a su espalda. La vida pospuesta para cuando las condiciones
    fueran favorables. Vivir en una suerte de provisionalidad que le había empujado a aplazarlo todo.
    Ahora que no tenía futuro alguno, o que éste era una plácida, rutinaria, repetición del presente.
    Ciertas madrugadas le habían enseñado la serenidad de lo irremediable. Despertarse, ventearse como
    un perro la vida, ocuparse de sus asuntillos, sacar provecho de ellos, comer, beber, dormir. Ahora,
    sólo ahora, cuando estaba de verdad solo, sabía que la vida se escapa por las buenas, corre mucho
    "

    chez = "Článek 26
    Každý má právo na vzdělání. Vzdělání nechť je bezplatné, alespoň v počátečních a základních stupních.
    Základní vzdělání je povinné. Technické a odborné vzdělání budiž všeobecně přístupné a rovněž vyšší
    vzdělání má být stejně přístupné všem podle schopností.
    Vzdělání má směřovat k plnému rozvoji lidské osobnosti a k posílení úcty k lidským právům a základním svobodám.
    Má napomáhat k vzájemnému porozumění, snášenlivosti a přátelství mezi všemi národy a všemi skupinami rasovými
    i náboženskými, jakož i k rozvoji činnosti Spojených národů pro zachování míru.
    Rodiče mají přednostní právo volit druh vzdělání pro své děti.
    Článek 27
    Každý má právo svobodně se účastnit kulturního života společnosti, úžívat plodů umění a podílet se na vědeckém
    pokroku a jeho výtěžcích.
    Každý má právo na ochranu morálních a materiálních zájmů, které vyplývají z jeho vědecké, literární nebo umělecké
    tvorby. "

    vietnamese = "Điều 26:
    1) Mọi người đều có quyền được học hành. Phải áp dụng chế độ giáo dục miễn phí, ít nhất là ở bậc tiểu học và
    giáo dục cơ sở. Giáo dục tiểu học là bắt buộc. Giáo dục kỹ thuật và ngành nghề phải mang tính phổ thông, và
    giáo dục cao học phải theo nguyên tắc công bằng cho bất cứ ai có đủ khả năng.
    2) Giáo dục phải hướng tới mục tiêu giúp con người phát triển đầy đủ nhân cách và thúc đẩy sự tôn trọng đối với
    các quyền và tự do cơ bản của con người. Giáo dục phải tăng cường sự hiểu biết, lòng vị tha và tình bằng hữu giữa
    tất cả các dân tộc, các nhóm tôn giáo và chủng tộc, cũng như phải đẩy mạnh các hoạt động của Liên Hợp Quốc vì mục
    đích gìn giữ hoà bình.
    3) Cha, mẹ có quyền ưu tiên lựa chọn loại hình giáo dục cho con cái.
    Điều 27:
    1) Mọi người đều có quyền tự do tham gia vào đời sống văn hoá của cộng đồng, được thưởng thức nghệ thuật và chia
    xẻ những thành tựu và lợi ích của tiến bộ khoa học.
    2) Mọi người đều có quyền được bảo hộ đối với những quyền lợi về vật chất và tinh thần xuất phát từ công trình
    khoa học, văn học và nhgệ thuật mà người đó là tác giả. "

french_tokenized = ["Maître", "Corbeau", ",", "sur", "un", "arbre", "perché", ",", "Tenait", "en", "son", "bec",
    "un", "fromage.", "Maître", "Renard", ",", "par", "l", "’", "odeur", "alléché", ",", "Lui", "tint", "à", "peu",
    "près", "ce", "langage", ":", "«", "Hé", "!", "bonjour", ",", "Monsieur", "du", "Corbeau.", "Que", "vous",
    "êtes", "joli", "!", "Que", "vous", "me", "semblez", "beau", "!", "Sans", "mentir", ",", "si", "votre",
    "ramage", "Se", "rapporte", "à", "votre", "plumage", ",", "Vous", "êtes", "le", "Phénix", "des", "hôtes",
    "de", "ces", "bois.", "»", "A", "ces", "mots", "le", "Corbeau", "ne", "se", "sent", "pas", "de", "joie",
    ";", "Et", "pour", "montrer", "sa", "belle", "voix", ",", "Il", "ouvre", "un", "large", "bec", ",", "laisse",
    "tomber", "sa", "proie.", "Le", "Renard", "s", "’", "en", "saisit", ",", "et", "dit", ":", "«", "Mon", "bon",
    "Monsieur", ",", "Apprenez", "que", "tout", "flatteur", "Vit", "aux", "dépens", "de", "celui", "qui", "l", "’",
    "écoute", ":", "Cette", "leçon", "vaut", "bien", "un", "fromage", ",", "sans", "doute.", "»", "Le", "Corbeau",
    ",", "honteux", "et", "confus", ",", "Jura", ",", "mais", "un", "peu", "tard", ",", "qu", "’", "on", "ne", "l",
    "’", "y", "prendrait", "plus", "."]

old_english_tokenized = ["An.", "M.LXVI.", "On", "þyssum", "geare", "man", "halgode", "þet", "mynster", "æt",
    "Westmynstre", "on", "Cyldamæsse", "dæg", "7", "se", "cyng", "Eadward", "forðferde", "on", "Twelfts", "mæsse",
    "æfen", "7", "hine", "mann", "bebyrgede", "on", "Twelftan", "mæssedæg", "innan", "þære", "niwa", "halgodre",
    "circean", "on", "Westmyntre", "7", "Harold", "eorl", "feng", "to", "Englalandes", "cynerice", "swa", "swa",
    "se", "cyng", "hit", "him", "geuðe", "7", "eac", "men", "hine", "þærto", "gecuron", "7", "wæs", "gebletsod",
    "to", "cynge", "on", "Twelftan", "mæssedæg", "7", "þa", "ylcan", "geare", "þe", "he", "cyng", "wæs", "he",
    "for", "ut", "mid", "sciphere", "togeanes", "Willelme", "7", "þa", "hwile", "com", "Willelm", "eorl", "upp",
    "æt", "Hestingan", "on", "Sce", "Michaeles", "mæssedæg", "7", "Harold", "com", "norðan", "7", "him", "wið",
    "gefeaht", "ear", "þan", "þe", "his", "here", "com", "eall", "7", "þær", "he", "feoll", "7", "his", "twægen",
    "gebroðra", "Gyrð", "7", "Leofwine", "and", "Willelm", "þis", "land", "geeode", "7", "com", "to", "Westmynstre",
    "7", "Ealdred", "arceb", "hine", "to", "cynge", "gehalgode", "7", "menn", "guldon", "him", "gyld", "7", "gislas",
    "sealdon", "7", "syððan", "heora", "land", "bohtan", "."]

russian_tokenized = ["Лорем", "ипсум", "долор", "сит", "амет", ",", "яуи", "ин", "реяуе", "пертинациа", ",",
    "еа", "ест", "яуод", "ехпетенда", "витуператорибус.", "Еум", "ут", "дицант", "граеци", "саперет", ",", "еу",
    "тамяуам", "епицуреи", "елецтрам", "сед.", "Аеяуе", "анциллае", "пер", "ид", ",", "вим", "ностер", "албуциус",
    "вивендум", "ин", ",", "цонгуе", "долоре", "сит", "но.", "Ид", "еум", "алияуип", "интерпретарис", ",", "легере",
    "пертинах", "малуиссет", "ин", "усу.", "Еам", "еу", "еиус", "поссе.", "Сеа", "еи", "малорум", "ассентиор.", "Алии",
    "мутат", "персиус", "усу", "но", ",", "цу", "вих", "ирацундиа", "цонсететур", ",", "цоррумпит", "форенсибус", "диссентиунт",
    "но", "иус.", "Ессе", "цибо", "нонумес", "ин", "сеа.", "Доминг", "еурипидис", "модератиус", "сеа", "ут", ",",
    "алии", "иллуд", "граецис", "ет", "сед.", "Цу", "путент", "десеруиссе", "еам", "."]

spanish_tokenized = ["Mentiría", "si", "dijera", "que", "era", "del", "todo", "nuevo", "el", "sentimiento", "de",
    "que", "ya", "no", "iba", "a", "poder", "ser", "más", "que", "lo", "que", "era", ",", "que", "era", "un",
    "hombre", "que", "había", "envejecido", "más", "de", "lo", "que", "suponía", ",", "que", "había", "sospechado",
    "tener", "toda", "la", "vida", "por", "delante", "y", "había", "ido", "dejando", "pasar", "los", "años", "a",
    "la", "espera", "de", "que", "llegara", "su", "momento", ",", "y", "ahora", "la", "tenía", "a", "su", "espalda.",
    "La", "vida", "pospuesta", "para", "cuando", "las", "condiciones", "fueran", "favorables.", "Vivir", "en", "una",
    "suerte", "de", "provisionalidad", "que", "le", "había", "empujado", "a", "aplazarlo", "todo.", "Ahora", "que",
    "no", "tenía", "futuro", "alguno", ",", "o", "que", "éste", "era", "una", "plácida", ",", "rutinaria", ",",
    "repetición", "del", "presente.", "Ciertas", "madrugadas", "le", "habían", "enseñado", "la", "serenidad", "de",
    "lo", "irremediable.", "Despertarse", ",", "ventearse", "como", "un", "perro", "la", "vida", ",", "ocuparse",
    "de", "sus", "asuntillos", ",", "sacar", "provecho", "de", "ellos", ",", "comer", ",", "beber", ",", "dormir.",
    "Ahora", ",", "sólo", "ahora", ",", "cuando", "estaba", "de", "verdad", "solo", ",", "sabía", "que", "la", "vida",
    "se", "escapa", "por", "las", "buenas", ",", "corre", "mucho"]

farsi_tokenized = ["مادهٔ", "بیست", "و", "ششم", "1", ")", "هر", "کس", "حق", "دارد", "که",
    "از", "آموزش", "و", "پرورش", "بهره‌مند", "شود.", "آموزش", "و", "پرورش", "لااقل", "تا",
    "حدودی", "که", "مربوط", "بتعلیمات", "ابتدائی", "و", "اساسی", "است", "باید",
    "مجانی", "باشد.", "آموزش", "ابتدائی", "اجباری", "است.", "آموزش", "حرفه‌ای",
    "باید", "با", "شرایط", "تساوی", "کامل", "بروی", "همه", "باز", "باشد", "تا",
    "همه", "بنا", "باستعداد", "خود", "بتوانند", "از", "آن", "بهره‌مند", "گردند.", "2",
    ")", "آموزش", "و", "پرورش", "باید", "طوری", "هدایت", "شود", "که", "شخصیت", "انسانی",
    "هر", "کس", "را", "بحد", "اکمل", "رشد", "آن", "برساند", "و", "احترام", "حقوق", "و",
    "آزادی‌های", "بشر", "را", "تقویت", "کند.", "آموزش", "و", "پرورش", "باید", "حسن", "تفاهم", "،",
    "گذشت", "و", "احترام", "عقاید", "مخالف", "و", "دوستی", "بین", "تمام", "ملل", "و",
    "جمعیتهای", "نژادی", "یا", "مذهبی", "و", "همچنین", "توسعه", "فعالیتهای", "ملل",
    "متحد", "را", "در", "راه", "حفظ", "صلح", "تسهیل", "نماید.", "3", ")", "پدر", "و",
    "مادر", "در", "انتخاب", "نوع", "آموزش", "و", "پرورش", "فرزندان", "خود", "نسبت",
    "بدیگران", "اولویت", "دارند.", "مادهٔ", "بیست", "و", "هفتم", "1", ")", "هر", "کس", "حق",
    "دارد", "آزادانه", "در", "زندگی", "فرهنگی", "اجتما", "عی", "شرکت", "کند", "،", "از",
    "فنون", "و", "هنرها", "متمتع", "گردد", "و", "در", "پیشرفت", "علمی", "و", "فوائد", "آن",
    "سهیم", "باشد.", "2", ")", "هر", "کس", "حق", "دارد", "از", "حمایت", "منافع", "معنوی",
    "و", "مادی", "آثار", "علمی", "،", "فرهنگی", "یا", "هنری", "خود", "برخوردار", "شود", "."]

chez_tokenized = ["Článek", "26", "Každý", "má", "právo", "na", "vzdělání.", "Vzdělání", "nechť", "je", "bezplatné",
    ",", "alespoň", "v", "počátečních", "a", "základních", "stupních.", "Základní", "vzdělání", "je", "povinné.",
    "Technické", "a", "odborné", "vzdělání", "budiž", "všeobecně", "přístupné", "a", "rovněž", "vyšší", "vzdělání",
    "má", "být", "stejně", "přístupné", "všem", "podle", "schopností.", "Vzdělání", "má", "směřovat", "k",
    "plnému", "rozvoji", "lidské", "osobnosti", "a", "k", "posílení", "úcty", "k", "lidským", "právům", "a",
    "základním", "svobodám.", "Má", "napomáhat", "k", "vzájemnému", "porozumění", ",", "snášenlivosti", "a",
    "přátelství", "mezi", "všemi", "národy", "a", "všemi", "skupinami", "rasovými", "i", "náboženskými", ",",
    "jakož", "i", "k", "rozvoji", "činnosti", "Spojených", "národů", "pro", "zachování", "míru.", "Rodiče",
    "mají", "přednostní", "právo", "volit", "druh", "vzdělání", "pro", "své", "děti.", "Článek", "27", "Každý",
    "má", "právo", "svobodně", "se", "účastnit", "kulturního", "života", "společnosti", ",", "úžívat", "plodů",
    "umění", "a", "podílet", "se", "na", "vědeckém", "pokroku", "a", "jeho", "výtěžcích.", "Každý", "má", "právo",
    "na", "ochranu", "morálních", "a", "materiálních", "zájmů", ",", "které", "vyplývají", "z", "jeho", "vědecké",
    ",", "literární", "nebo", "umělecké", "tvorby", "."]

vietnamese_tokenized = ["Điều", "26", ":", "1", ")", "Mọi", "người", "đều", "có", "quyền", "được", "học", "hành.",
    "Phải", "áp", "dụng", "chế", "độ", "giáo", "dục", "miễn", "phí", ",", "ít", "nhất", "là", "ở", "bậc", "tiểu",
    "học", "và", "giáo", "dục", "cơ", "sở.", "Giáo", "dục", "tiểu", "học", "là", "bắt", "buộc.", "Giáo", "dục",
    "kỹ", "thuật", "và", "ngành", "nghề", "phải", "mang", "tính", "phổ", "thông", ",", "và", "giáo", "dục", "cao",
    "học", "phải", "theo", "nguyên", "tắc", "công", "bằng", "cho", "bất", "cứ", "ai", "có", "đủ", "khả", "năng.",
    "2", ")", "Giáo", "dục", "phải", "hướng", "tới", "mục", "tiêu", "giúp", "con", "người", "phát", "triển", "đầy",
    "đủ", "nhân", "cách", "và", "thúc", "đẩy", "sự", "tôn", "trọng", "đối", "với", "các", "quyền", "và", "tự",
    "do", "cơ", "bản", "của", "con", "người.", "Giáo", "dục", "phải", "tăng", "cường", "sự", "hiểu", "biết", ",",
    "lòng", "vị", "tha", "và", "tình", "bằng", "hữu", "giữa", "tất", "cả", "các", "dân", "tộc", ",", "các", "nhóm",
    "tôn", "giáo", "và", "chủng", "tộc", ",", "cũng", "như", "phải", "đẩy", "mạnh", "các", "hoạt", "động",
    "của", "Liên", "Hợp", "Quốc", "vì", "mục", "đích", "gìn", "giữ", "hoà", "bình.", "3", ")", "Cha", ",",
    "mẹ", "có", "quyền", "ưu", "tiên", "lựa", "chọn", "loại", "hình", "giáo", "dục", "cho", "con", "cái.",
    "Điều", "27", ":", "1", ")", "Mọi", "người", "đều", "có", "quyền", "tự", "do", "tham", "gia", "vào", "đời",
    "sống", "văn", "hoá", "của", "cộng", "đồng", ",", "được", "thưởng", "thức", "nghệ", "thuật", "và", "chia",
    "xẻ", "những", "thành", "tựu", "và", "lợi", "ích", "của", "tiến", "bộ", "khoa", "học.", "2", ")", "Mọi",
    "người", "đều", "có", "quyền", "được", "bảo", "hộ", "đối", "với", "những", "quyền", "lợi", "về", "vật",
    "chất", "và", "tinh", "thần", "xuất", "phát", "từ", "công", "trình", "khoa", "học", ",", "văn", "học",
    "và", "nhgệ", "thuật", "mà", "người", "đó", "là", "tác", "giả", "."]

     languages = [french, old_english, russian, spanish, farsi, chez, vietnamese]
     languages_tokenized = [french_tokenized, old_english_tokenized, russian_tokenized, spanish_tokenized, farsi_tokenized, chez_tokenized, vietnamese_tokenized]

    for ind  in 1 : length(languages)
        @test toktok_tokenize(languages[ind]) == languages_tokenized[ind]
    end
end

@testset "final periods" begin
    @testset "final period 1" begin
        str = "example... sentence1.."
        tokenized = ["example", "sentence", "1"]
        @test tokenized == toktok_tokenize(str)

        str = "example... sentence1."
        tokenized = ["example", "sentence", "1", "."]
        @test tokenized == toktok_tokenize(str)
    end

    @testset "final period 2" begin
        str = "1)example sentence. 2)example sentence.   ‘ "
        tokenized = ["1", ")", "example", "sentence.", "2", ")", "example", "sentence", ".", "‘"]
        @test tokenized == toktok_tokenize(str)

        str = "1)example sentence. 2)example sentence...   ‘ "
        tokenized = ["1", ")", "example", "sentence.", "2", ")", "example", "sentence", "‘"]
        @test tokenized == toktok_tokenize(str)
    end

	@testset "spaces in final period" begin
		str = "This is a sentence. "
		tokenized = [ "This", "is", "a", "sentence", "."]
		@test tokenized == toktok_tokenize(str)

		str = "They say, \"This is a sentence . \" "
		tokenized = [ "They", "say", ",", "\"", "This", "is", "a", "sentence", ".", "\""]
		@test tokenized == toktok_tokenize(str)
	end
end

@testset "repeated_sequences" begin
    str = "tokenize this ,,, --- ..."
    tokenized = ["tokenize", "this"]
    @test tokenized == toktok_tokenize(str)
end

@testset "atoms" begin
	str = "=–="
	tokenized = ["=", "–", "="]
	@test tokenized == toktok_tokenize(str)

	str = "I have ¥50"
	tokenized = ["I", "have", "¥", "50"]
	@test tokenized == toktok_tokenize(str)
end


text:
# Common transformers

"""
    OneLineTransformerLogger(logger::AbstractLogger)

Return a logger that rolls all kwargs into the message field, resulting
in a single-line log.
"""
function OneLineTransformerLogger(logger::AbstractLogger)
    return TransformerLogger(logger) do log
        migrate(log, KwargsProperty(), MessageProperty())
    end
end

"""
    TimestampTransformerLogger([logger::AbstractLogger], location::AbstractInjectLocation; kwargs...)

Return a logger that includes a timestamp in the log.  If `logger` is not specified,
then it returns a closure that takes a single logger argument. The timestamp can be injected
in any of the following `location`:
- [`KwargsLocation`](@ref)
- [`BeginningMessageLocation`](@ref)
- [`EndingMessageLocation`](@ref)

# Keyword arguments
- `format`: a DateTime format string, see
[Dates.format](https://docs.julialang.org/en/v1/stdlib/Dates/#Dates.format-Tuple{TimeType,AbstractString}) and
[TimeZones documentation](https://juliatime.github.io/TimeZones.jl/stable/conversions/#Formatting-strings-1)
- `label`: a symbol that is used as the key of the timestamp for `KwargsLocation` injection location
"""
function TimestampTransformerLogger end

function TimestampTransformerLogger(location::T; kwargs...) where {T <: AbstractInjectLocation}
    return logger -> TimestampTransformerLogger(logger, location; kwargs...)
end

function TimestampTransformerLogger(logger::AbstractLogger, location::KwargsLocation;
                                    format = "yyyy-mm-ddTHH:MM:SSz",
                                    label = :timestamp) where {T <: AbstractInjectLocation}
    return TransformerLogger(logger) do log
        inject(log, location, () -> (label => current_time_string(format),))
    end
end

function TimestampTransformerLogger(logger::AbstractLogger, location::MessageLocation;
                                    format = "yyyy-mm-ddTHH:MM:SSz")
    return TransformerLogger(logger) do log
        inject(log, location, () -> current_time_string(format))
    end
end

"""
    current_time_string(format)

Return the current time as a formatted string.
"""
function current_time_string end

current_time_string(format::AbstractString) = current_time_string(DateFormat(format))

function current_time_string(format::DateFormat)
    ts = now(localzone())
    return Dates.format(ts, format)
end

"""
    JSONTransformerLogger(logger::AbstractLogger)

Return a logger that formats all information (level, message, and kwargs)
as a JSON string.  The result is stored in the message field of the log record.
The existing kwargs data is emptied automatically.
"""
function JSONTransformerLogger(logger::AbstractLogger;
                               level_label::Symbol = :level,
                               message_label::Symbol = :message,
                               indent::Integer = 0)
    return TransformerLogger(logger) do log
            @pipe log |>
                 migrate(_, LevelProperty(), KwargsProperty(); label = level_label, transform = string) |>
                 migrate(_, MessageProperty(), KwargsProperty(); label = message_label) |>
                 migrate(_, KwargsProperty(), MessageProperty();
                         transform = x -> chomp(json(namedtuple(x), indent)),
                         prepend = "")
    end
end

"""
    ColorMessageTransformerLogger([logger], colors::Dict{Logging.LogLevel,ColorSpec})

Apply `colors` to message string based upon log level. If `logger` is not specified,
then it returns a closure that takes a single logger argument.
"""
function ColorMessageTransformerLogger(colors::Dict{Logging.LogLevel,ColorSpec})
    return logger -> ColorMessageTransformerLogger(logger, colors)
end

function ColorMessageTransformerLogger(logger::AbstractLogger,
                              colors::Dict{Logging.LogLevel,ColorSpec})
    return TransformerLogger(logger) do log
                mutate(log, MessageProperty();
                       transform = (log) -> styled_string(log.message, colors[log.level]))
    end
end

function FixedMessageWidthTransformerLogger(logger::AbstractLogger, width::Integer)
    return TransformerLogger(logger) do log
                mutate(log, MessageProperty();
                    transform = (log) -> first(rpad(log.message, width), width))
    end
end

"""
    styled_string(xs...; spec::ColorSpec)

Return a styled string.
"""
function styled_string(x, spec::ColorSpec)
    io = IOBuffer()
    printstyled(IOContext(io, :color => true), x; bold = spec.bold, color = spec.color)
    return String(take!(io))
end

"""
    namedtuple(pairs)

Convert an iterable of pairs into a named tuple. If there's any duplicate in the key,
then the first one wins (silently).
"""
function namedtuple(pairs)
    d = Dict()
    for p in pairs
        get!(d, Symbol(first(p)), last(p))
    end
    names = (keys(d)...,)
    nt = NamedTuple{names}(values(d))
    return nt
end

"""
    compose(logger::AbstractLogger, transformers::Function...)

Compose a transformer logger by chaining the specified transformer loggers
in the same order as they are specified.

Note: this function should probably be upstreamed to LoggingExtras.jl
"""
function compose(logger::AbstractLogger, transformers::Function...)
    ∘(transformers...)(logger)
end


text:
export  ft_getcomportnumber

function ft_getcomportnumber(ft_handle::Culong)
  comportnumber = Ref{Cuint}()
  ft_status = ccall((:FT_GetComPortNumber, d2xx),
                     Cuint,
                     (Culong, Ref{Cuint}),
                     ft_handle, comportnumber)
  checkstatus(ft_status)
  return comportnumber[]
end


text:
using Distances
using LinearAlgebra
using Ripserer
using StaticArrays
using Test

using Ripserer:
    _landmarks_and_radius,
    _to_integer_coefficients,
    _zero_coboundary_matrix,
    _to_vector,
    _harmonic_smoothing,
    is_cocycle,
    ChainElement

include("../testdatasets.jl")

@testset "Helpers" begin
    @testset "Partition functions" begin
        for fun in (Partition.linear, Partition.quadratic, Partition.exponential)
            for r in range(1, 2; length=5), d in range(0, 2; length=5)
                if r > d
                    @test fun(r, d) > 0
                else
                    @test fun(r, d) == 0
                end
            end
        end
    end

    @testset "Landmark selection" begin
        points = [(sin(t), cos(t)) for t in range(0, 2π; length=41)[1:40]]
        point_matrix = reshape(reinterpret(Float64, points), (2, 40))

        for landmark_selector in (10, 1:4:40, points[1:4:40])
            landmarks, radius = _landmarks_and_radius(
                points, landmark_selector, Euclidean()
            )
            @test length(landmarks) == 10
            @test radius ≈ 0.3128689 atol = 1e-5
            @test eltype(landmarks) <: SVector{2,Float64}
        end
    end

    @testset "Cocycles" begin
        rips = Rips(
            [
                0.0 1.1 2.0 2.0 1.2
                1.1 0.0 1.0 2.0 2.0
                2.0 1.0 0.0 1.0 2.0
                2.0 2.0 1.0 0.0 1.0
                1.2 2.0 2.0 1.0 0.0
            ],
        )

        cocycle = ripserer(rips; modulus=17, reps=true)[2][1].representative
        integral = _to_integer_coefficients(cocycle)
        @test simplex.(integral) == simplex.(cocycle)
        @test coefficient.(integral) == [-1, -1, -1]
        @test _to_vector(rips, integral, Inf) == [0, -1, 0, -1, 0, 0, -1, 0, 0, 0]
        @test _to_vector(rips, integral, 2) == [0, 0, 0, 0, 0, 0, -1, 0, 0, 0]

        @test _zero_coboundary_matrix(rips, Inf) == [
            1 -1 0 0 0
            1 0 -1 0 0
            0 1 -1 0 0
            1 0 0 -1 0
            0 1 0 -1 0
            0 0 1 -1 0
            1 0 0 0 -1
            0 1 0 0 -1
            0 0 1 0 -1
            0 0 0 1 -1
        ]
        @test _zero_coboundary_matrix(rips, 2) == [
            1 -1 0 0 0
            0 0 0 0 0
            0 1 -1 0 0
            0 0 0 0 0
            0 0 0 0 0
            0 0 1 -1 0
            1 0 0 0 -1
            0 0 0 0 0
            0 0 0 0 0
            0 0 0 1 -1
        ]
        coords, harmonic = _harmonic_smoothing(rips, cocycle; time=2)
        @test all(diff(coords) .≈ 0.2)

        # The harmonic interval should have a minimal norm.
        @test norm(harmonic) < norm(_to_vector(rips, integral, 2))
    end
end

@testset "CircularCoordinates" begin
    @testset "landmark selection" begin
        points = torus_points(1024)

        cc1 = CircularCoordinates(points, 200)
        @test length(cc1.landmarks) == 200
        @test allunique(cc1.landmarks)

        cc2 = CircularCoordinates(points, 1:100)
        @test cc2.landmarks == SVector.(points[1:100])

        cc3 = CircularCoordinates(points, points[1:100])
        @test cc3.landmarks == SVector.(points[1:100])

        @test cc2.coordinate_data[1].radius == cc3.coordinate_data[1].radius
    end
    @testset "selecting out_dim" begin
        points = torus_points(1024)

        cc = CircularCoordinates(points, 1:10:1024)
        @test cc.out_dim == 1

        cc = CircularCoordinates(points, 1:10:1024; out_dim=3, warn=false)
        @test cc.out_dim == 1
        @test size(cc(points[1:10])) == (10, 1)
        @test size(cc(points[11:20], 1)) == (10,)

        cc = CircularCoordinates(points, 1:5:1024; out_dim=3, warn=false)
        @test cc.out_dim == 2
        out_both = cc(points[1:10])
        out_first = cc(points[1:10], 1)
        out_second = cc(points[1:10], 2)
        @test out_both[:, 1] == out_first
        @test out_both[:, 2] == out_second

        @test_throws ErrorException CircularCoordinates(points, 1:10)
    end
    @testset "transformation" begin
        ts1 = range(0, 1; length=100)[1:(end - 1)]
        circle = [(sinpi(2t), cospi(2t)) for t in ts1]

        cc = CircularCoordinates(circle, 1:10:100)
        transformed1 = cc(circle, 1)
        transformed1 .-= transformed1[1]
        transformed1 .= Ripserer._mod_z.(transformed1)

        @test transformed1 ≈ ts1 atol = 0.05

        @test cc([(100, 100)])[1] === missing

        ts2 = [0; rand(20)]
        new_points = [(sinpi(2t), cospi(2t)) for t in ts2]
        transformed2 = cc(new_points, 1)
        transformed2 .-= transformed2[1]
        transformed2 .= Ripserer._mod_z.(transformed2)

        @test transformed2 ≈ ts2 atol = 0.05
    end
end