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

using Markdown
using InteractiveUtils

# ╔═╡ 321b951c-9bea-11ec-0d10-31f76d44cc9e
md"""
# HW 7

Eric Nguyen
"""

# ╔═╡ 710b2cc8-45e7-4eee-9066-3fd4080aebc9
md"""
### Problem 1

First we start with the Cartesian coordinates ``x``, ``y``, and ``z`` in terms of spherical coordinates,

$\begin{align*}
x &= r \sin{\theta} \cos{\phi} \\
y &= r \sin{\theta} \sin{\phi} \\
z &= r \cos{\theta}
\end{align*}$

Imagine that we change the position ``\vec{r}`` by a tiny ``d\vec{r} = dx \, \hat{i} + dy \, \hat{j} + dz \, \hat{k}`` such that ``x``, ``y``, and ``z`` change by infinitesimal amounts,

$\begin{align*}
dx &= \sin{\theta} \cos{\phi} \,dr + r \cos{\theta} \cos{\phi} \,d\theta - r \sin{\theta} \sin{\phi} \,d\phi \\
dy &= \sin{\theta} \sin{\phi} \,dr + r \cos{\theta} \sin{\phi} \,d\theta + r \sin{\theta} \cos{\phi} \,d\phi \\
dz &= \cos{\theta} \,dr - r \sin{\theta} \,d\theta
\end{align*}$

Consider the change ``d\vec{r}`` in the ``r,\theta,\phi`` directions found by setting the infinitesimals equal to zero accordingly (e.g., for ``\hat{r}`` set ``d\theta = d\phi = 0``, etc.),

$\begin{align*}
d\vec{r}_r &= \hat{i} \sin{\theta} \cos{\phi} \,dr + \hat{j} \sin{\theta} \sin{\phi} \,dr + \hat{k} \cos{\theta} \,dr \\
&= (\hat{i} \sin{\theta} \cos{\phi} + \hat{j} \sin{\theta} \sin{\phi} + \hat{k} \cos{\theta}) \,dr \\
d\vec{r}_{\theta} &= \hat{i} r \cos{\theta} \cos{\phi} \,d\theta + \hat{j} r \cos{\theta} \sin{\phi} \,d\theta - \hat{k} r \cos{\theta} \,d\theta \\
&= (\hat{i} \cos{\theta} \cos{\phi} + \hat{j} \cos{\theta} \sin{\phi} - \hat{k} \sin{\theta}) r \,d\theta \\
d\vec{r}_{\phi} &= \hat{i} r \sin{\theta} \sin{\phi} \,d\phi + \hat{j} r \sin{\theta} \cos{\phi} \,d\phi \\
&= (\hat{i} \sin{\phi} + \hat{j} \cos{\phi}) r \sin{\theta} \,d\phi
\end{align*}$

It follows that the unit vectors in the spherical polar coordinates are

$\begin{align*}
\hat{r} &= \hat{i} \sin{\theta} \cos{\phi} + \hat{j} \sin{\theta} \sin{\phi} + \hat{k} \cos{\theta} \\
\hat{\theta} &= \hat{i} \cos{\theta} \cos{\phi} + \hat{j} \cos{\theta} \sin{\phi} - \hat{k} \sin{\theta} \\
\hat{\phi} &= \hat{i} \sin{\phi} + \hat{j} \cos{\phi}
\end{align*}$
"""

# ╔═╡ 370b6a47-79b6-41e3-ad41-01544affa97d
md"""
### Problem 2

Using ``\vec{v} = \frac{d\vec{r}}{dt}``, rewrite the expression for kinetic energy as

$K = \frac{1}{2} m \left(\vec{v} ⋅ \vec{v}\right) = \frac{1}{2} m \left(\sqrt{{v_x}^2 + {v_y}^2 + {v_z}^2}\right)^2 = \frac{1}{2} m ({v_x}^2 + {v_y}^2 + {v_z}^2)$

We start with the transformation equations for the Cartesian coordinates,

$\begin{align*}
x &= r \sin{\theta} \cos{\phi} \\
y &= r \sin{\theta} \sin{\phi} \\
z &= r \cos{\theta}
\end{align*}$

Take the time derivative for each coordinate (i.e., find expressions for the components of ``\vec{v}``):

$\begin{align*}
v_x &= ẋ = \sin{\theta} \cos{\phi} \, ṙ + r \cos{\theta} \cos{\phi} \,\dot{\theta} - r \sin{\theta} \sin{\phi} \,\dot{\phi} \\
v_y &= ẏ = \sin{\theta} \sin{\phi} \, ṙ + r \cos{\theta} \sin{\phi} \,\dot{\theta} + r \sin{\theta} \cos{\phi} \, \dot{\phi} \\
v_z &= ż = \cos{\theta} \,ṙ - r \sin{\theta} \,\dot{\theta}
\end{align*}$

Going back to the expression for kinetic energy, we can write the velocity expression in terms of ``r``, ``\theta``, and ``\phi`` and simplify.
First, we square each velocity component:

$\begin{align*}
{v_x}^2 &= (\sin{\theta} \cos{\phi} \, ṙ + r \cos{\theta} \cos{\phi} \,\dot{\theta} - r \sin{\theta} \sin{\phi} \,\dot{\phi})^2 \\
&= \sin{\theta}^2 \cos^2{\phi} \, ṙ^2 + r^2 \cos^2{\theta} \cos^2{\phi} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \sin^2{\phi} \,\dot{\phi}^2 \\
&\quad + 2r \sin{\theta} \cos{\theta} \cos^2{\phi} \,ṙ \,\dot{\theta} - 2r \sin^2{\theta} \sin{\phi} \cos{\phi} \,ṙ \,\dot{\phi} \\
&\quad - 2r \sin{\theta} \cos{\theta} \sin{\phi} \cos{\phi} \,\dot{\theta} \,\dot{\phi} \\
\\
{v_y}^2 &= (\sin{\theta} \sin{\phi} \, ṙ + r \cos{\theta} \sin{\phi} \,\dot{\theta} + r \sin{\theta} \cos{\phi} \, \dot{\phi})^2 \\
&= \sin^2{\theta} \sin^2{\phi} \,ṙ^2 + r^2 \cos^2{\theta} \sin^2{\phi} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \cos^2{\phi} \,\dot{\phi}^2 \\
&\quad + 2r \sin{\theta} \cos{\theta} \sin^2{\phi} \,ṙ \,\dot{\theta} + 2r \sin^2{\theta} \sin{\phi} \cos{\phi} \,ṙ \,\dot{\phi} \\
&\quad + 2r \sin{\theta} \cos{\theta} \sin{\phi} \cos{\phi} \,\dot{\theta} \,\dot{\phi} \\
\\
{v_z}^2 &= (\cos{\theta} \,ṙ - r \sin{\theta} \,\dot{\theta})^2 \\
&= \cos^2{\theta} \,ṙ^2 + r^2 \sin^2{\theta} \,\dot{\theta}^2 - 2r \sin{\theta} \cos{\theta} \,ṙ \,\dot{\theta}
\end{align*}$

Adding the terms together, we get:

$\begin{align*}
{v_x}^2 + {v_y}^2 + {v_z}^2 &= \sin{\theta}^2 \cos^2{\phi} \, ṙ^2 + r^2 \cos^2{\theta} \cos^2{\phi} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \sin^2{\phi} \,\dot{\phi}^2 \\
&\quad + 2r \sin{\theta} \cos{\theta} \cos^2{\phi} \,ṙ \,\dot{\theta} - 2r \sin^2{\theta} \sin{\phi} \cos{\phi} \,ṙ \,\dot{\phi} \\
&\quad - 2r \sin{\theta} \cos{\theta} \sin{\phi} \cos{\phi} \,\dot{\theta} \,\dot{\phi} \\
&\quad + \sin^2{\theta} \sin^2{\phi} \,ṙ^2 + r^2 \cos^2{\theta} \sin^2{\phi} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \cos^2{\phi} \,\dot{\phi}^2 \\
&\quad + 2r \sin{\theta} \cos{\theta} \sin^2{\phi} \,ṙ \,\dot{\theta} + 2r \sin^2{\theta} \sin{\phi} \cos{\phi} \,ṙ \,\dot{\phi} \\
&\quad + 2r \sin{\theta} \cos{\theta} \sin{\phi} \cos{\phi} \,\dot{\theta} \,\dot{\phi} \\
&\quad + \cos^2{\theta} \,ṙ^2 + r^2 \sin^2{\theta} \,\dot{\theta}^2 - 2r \sin{\theta} \cos{\theta} \,ṙ \,\dot{\theta} \\

&= \sin{\theta}^2 \cos^2{\phi} \, ṙ^2 + r^2 \cos^2{\theta} \cos^2{\phi} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \sin^2{\phi} \,\dot{\phi}^2 \\
&\quad + \sin^2{\theta} \sin^2{\phi} \,ṙ^2 + r^2 \cos^2{\theta} \sin^2{\phi} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \cos^2{\phi} \,\dot{\phi}^2 \\
&\quad + \cos^2{\theta} \,ṙ^2 + r^2 \sin^2{\theta} \,\dot{\theta}^2 \\

&= \sin^2{\theta} \,ṙ^2 + r^2 \cos^2{\theta} \,\dot{\theta}^2 + r^2 \sin^2{\theta} \,\dot{\phi}^2 \\
&\quad + \cos^2{\theta} \,ṙ^2 + r^2 \sin^2{\theta} \,\dot{\theta}^2 \\

&= ṙ^2 + r^2 \, \dot{\theta}^2 + r^2 \sin^2{\theta} \,\dot{\phi}^2
\end{align*}$

Substitute the result into the expression for kinetic energy:

$K = \frac{1}{2} m (ṙ^2 + r^2 \, \dot{\theta}^2 + r^2 \sin^2{\theta} \,\dot{\phi}^2)$
"""

# ╔═╡ a1799513-6a61-4443-8ec7-428c0696bc5f
md"""
### Problem 3

**(a).**
The square of the magnitude of the cross product ``|\vec{A} × \vec{B}|^2`` is

$\begin{align*}
|\vec{A} × \vec{B}|^2 &= (A_y B_z - A_z B_y)^2 |\hat{i}| + (A_z B_x - A_x B_z)^2 |\hat{j}| + (A_x B_y - A_y B_x)^2 |\hat{k}| \\
&= {A_y}^2 {B_z}^2 + {A_z}^2 {B_y}^2 - 2 A_y A_z B_y B_z \\
&\quad + {A_z}^2 {B_x}^2 + {A_x}^2 {B_z}^2 - 2 A_x A_z B_x B_z \\
&\quad + {A_x}^2 {B_y}^2 + {A_y}^2 {B_x}^2 - 2 A_x A_y B_x B_y \\
\end{align*}$

The square of the magnitude of ``|\vec{A}|^2`` and ``|\vec{B}|^2`` are

$\begin{align*}
|\vec{A}|^2 &= {A_x}^2 + {A_y}^2 + {A_z}^2 \\
|\vec{B}|^2 &= {B_x}^2 + {B_y}^2 + {B_z}^2
\end{align*}$

The product ``|\vec{A}|^2 |\vec{B}|^2`` is then

$\begin{align*}
|\vec{A}|^2 |\vec{B}|^2 &= ({A_x}^2 + {A_y}^2 + {A_z}^2)({B_x}^2 + {B_y}^2 + {B_z}^2) \\
&= {A_x}^2 {B_x}^2 + {A_x}^2 {B_y}^2 + {A_x}^2 {B_z}^2 \\
&\quad + {A_y}^2 {B_x}^2 + {A_y}^2 {B_y}^2 + {A_y}^2 {B_z}^2 \\
&\quad + {A_z}^2 {B_x}^2 + {A_z}^2 {B_y}^2 + {A_z}^2 {B_z}^2 \\
\end{align*}$

The square of the magnitude of ``(\vec{A} ⋅ \vec{B})^2`` is

$\begin{align*}
(\vec{A} ⋅ \vec{B})^2 &= (A_x B_x + A_y B_y + A_z B_z)^2 \\
&= {A_x}^2 {B_x}^2 + {A_y}^2 {B_y}^2 + {A_z}^2 {B_z}^2 \\
&\quad + 2 A_x A_y B_x B_y + 2 A_x A_z B_x B_z + 2 A_y A_z B_y B_z
\end{align*}$

Notice that

$|\vec{A}|^2 |\vec{B}|^2 - (\vec{A} ⋅ \vec{B})^2 = |\vec{A} × \vec{B}|^2$

Thus,

$|\vec{A} × \vec{B}|^2 = |\vec{A}|^2 |\vec{B}|^2 - (\vec{A} ⋅ \vec{B})^2$
"""

# ╔═╡ 08ef5ce6-9d82-4758-ade2-4ea62b514451
md"""
**(b).**
Using components, summation ceonvention, and the relationship between the totally antisymmetric symbol and the Kronecker delta it's possible to arrive at the same expression.

$\begin{align*}
|\vec{A} × \vec{B}|^2 &= (\epsilon_{ijk} \hat{e}_i A_j B_k) ⋅ (\epsilon_{ℓmn} \hat{e}_ℓ A_m B_n) \\
&= A_j B_k A_m B_n \epsilon_{ijk} \epsilon_{ℓmn} \delta_{iℓ} \\
&= A_j B_k A_m B_n \epsilon_{ijk} \epsilon_{imn} \\
&= A_j B_k A_m B_n (\delta_{jm} \delta_{kn} - \delta_{jn} \delta_{km}) \\
&= \sum_{jkmn} A_j B_k A_m B_n \delta_{jm} \delta_{kn} - \sum_{jkmn} A_j B_k A_m B_n \delta_{jn} \delta_{km} \\
&= \sum_{jm} A_j A_m \delta_{jm} \sum_{kn} B_k B_n \delta_{kn} - \sum_{jn} A_j B_m \delta_{jn} \sum_{km} A_m B_k \delta_{km} \\
&= (\vec{A} ⋅ \vec{A}) (\vec{B} ⋅ \vec{B}) - (\vec{A} ⋅ \vec{B}) (\vec{A} ⋅ \vec{B}) \\
&= |\vec{A}|^2 |\vec{B}|^2 - (\vec{A} ⋅ \vec{B})^2
\end{align*}$
"""

# ╔═╡ c0971a8a-7d86-4cb6-aae9-5423beeb1da4
md"""
### Problem 4

From Problem (1) I showed that

$\begin{align*}
d\vec{r}_{r} &= (\hat{i} \sin{\theta} \cos{\phi} + \hat{j} \sin{\theta} \sin{\phi} + \hat{k} \cos{\theta}) \,dr = \,dr \,\hat{r} \\
d\vec{r}_{\theta} &= (\hat{i} \cos{\theta} \cos{\phi} + \hat{j} \cos{\theta} \sin{\phi} - \hat{k} \sin{\theta}) r \,d\theta = r \,d\theta \,\hat{\theta}  \\
d\vec{r}_{\phi} &= (\hat{i} \sin{\phi} + \hat{j} \cos{\phi}) r \sin{\theta} \,d\phi = r \sin{\theta} \,d\phi \,\hat{\phi}
\end{align*}$

This shows that when moving in the ``r``-direction only, the derivative is ``\frac{∂}{∂r}``, in the ``\theta``-direction, the derivative is ``\frac{1}{r} \frac{∂}{∂\theta}``, and in the ``\phi``-direction, the derivative is ``\frac{1}{r \sin{\theta}} \frac{∂}{∂\phi}``.
The gradient in spherical coordinates is then

$\vec{∇} = \hat{r} \frac{∂}{∂r} + \hat{\theta} \frac{1}{r} \frac{∂}{∂\theta} + \hat{\phi} \frac{1}{r \sin{\theta}} \frac{∂}{∂\phi}$
"""

# ╔═╡ 74bea6fd-90e1-4aad-bb58-50b141eb0255
md"""
### Problem 5

From the notes, it is given

$\begin{align*}
\hat{r} &= \hat{i} \cos{\phi} + \hat{j} \sin{\phi} \\
\hat{\phi} &= -\hat{i} \sin{\phi} + \hat{j} \cos{\phi}
\end{align*}$

The partial derivatives of each unit vector with respect to ``\phi`` are

$\begin{align*}
\frac{∂\hat{r}}{∂\phi} &= -\hat{i} \sin{\phi} + \hat{j} \cos{\phi} = \hat{\phi} \\
\frac{∂\hat{\phi}}{∂\phi} &= -\hat{i} \cos{\phi} - \hat{j} \sin{\phi} = \hat{r}
\end{align*}$

Proceeding with the derivation, the result is

$\begin{align*}
\vec{∇}^2 f(r,\phi) &= \vec{∇} ⋅ \vec{∇} f(r,\phi) \\
&= \left(\hat{r} \frac{∂}{∂r} + \hat{\phi} \frac{1}{r} \frac{∂}{∂\phi}\right) ⋅ \left(\hat{r} \frac{∂f}{∂r} + \hat{\phi} \frac{1}{r} \frac{∂f}{∂\phi}\right) \\
&= \hat{r} \frac{∂}{∂r} \left(\hat{r} \frac{∂f}{∂r} + \hat{\phi} \frac{1}{r} \frac{∂f}{∂\phi}\right) + \hat{\phi} \frac{1}{r} \frac{∂}{∂\phi} \left(\hat{r} \frac{∂f}{∂r} + \hat{\phi} \frac{1}{r} \frac{∂f}{∂\phi}\right) \\
&= \hat{r} \left(\hat{r} \frac{∂^2f}{∂r^2} - \hat{\phi} \frac{1}{r^2} \frac{∂f}{∂\phi} + \hat{\phi} \frac{1}{r} \frac{∂f}{∂\phi ∂r}\right) \\
&\quad + \hat{\phi} \frac{1}{r} \left(\hat{\phi} \frac{∂f}{∂r} + \hat{r} \frac{∂f}{∂r ∂\phi} - \hat{r} \frac{1}{r} \frac{∂f}{∂\phi} + \hat{\phi} \frac{1}{r} \frac{∂^2 f}{∂\phi^2}\right) \\
&= \frac{∂^2f}{∂r^2} + \frac{1}{r} \frac{∂f}{∂r} + \frac{1}{r^2} \frac{∂^2 f}{∂\phi^2} \\
&= \frac{1}{r} \frac{∂}{∂r} \left(r \frac{∂f}{∂r}\right) + \frac{1}{r^2} \frac{∂^2 f}{∂\phi^2}
\end{align*}$
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
"""

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

julia_version = "1.7.1"
manifest_format = "2.0"

[deps]
"""

# ╔═╡ Cell order:
# ╟─321b951c-9bea-11ec-0d10-31f76d44cc9e
# ╟─710b2cc8-45e7-4eee-9066-3fd4080aebc9
# ╟─370b6a47-79b6-41e3-ad41-01544affa97d
# ╟─a1799513-6a61-4443-8ec7-428c0696bc5f
# ╟─08ef5ce6-9d82-4758-ade2-4ea62b514451
# ╟─c0971a8a-7d86-4cb6-aae9-5423beeb1da4
# ╟─74bea6fd-90e1-4aad-bb58-50b141eb0255
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
using Utils

using Chain

const file = find_input(@__FILE__)

function load(fname)
    dotlist = Tuple{Int,Int}[]
    folds = Tuple{Symbol,Int}[]
    maxx = maxy = 0
    open(fname) do io
        for line in eachline(io)
            line == "" && continue
            m = match(r"fold along ([xy])=(\d+)", line)
            if m === nothing
                x,y = map(curry(parse, Int), split(line, ","))
                maxx = max(maxx, x)
                maxy = max(maxy, y)
                push!(dotlist, (y,x))
            else
                push!(folds, (Symbol(m[1]), parse(Int, m[2])))
            end
        end
    end
    dots = falses(maxy + 1, maxx + 1) |> zerobased
    for d in dotlist
        dots[d...] = true
    end
    (dots=dots, folds=folds)
end
const input = load(file)

function part2(input, onlyone=false)
    dots = copy(input.dots)
    for fold in input.folds
        if fold[1] == :x
            dots = transpose(dots)
        end
        num = fold[2]
        foldin = size(dots, 1) - num - 1
        dots[num - foldin:num - 1,:] .|= @view dots[end:-1:num + 1,:]
        dots = dots[begin:num - 1,:] |> zerobased
        if fold[1] == :x
            dots = transpose(dots)
        end
        onlyone && return "$(count(dots))"
    end
    @chain dots begin
        map(c -> c ? '8' : ' ', _)
        eachrow
        map(String, _)
        join('\n')
    end
end

part1 = curry2nd(part2, true)
assertequal(part1(input), "678")
println('\n', part2(input))

ext: jl

max_stars_repo_name: CmdQ/AoC


text:
module PhotoOrganizer

abstract type AbstractFileType end
struct AnyFileType <: AbstractFileType end
struct MP4 <: AbstractFileType end

abstract type AbstractMake end
struct AnyMake <: AbstractMake end
struct Apple <: AbstractMake end

export organize_photos

using Dates

struct Photo
   src::String
   dt::Union{Missing,DateTime}
   dst::Union{Missing,String}
   backuped::Bool
   err
end

function Base.show(io::IO, ph::Photo)
   println(io, "Photo:")
   for (i, field) in enumerate(fieldnames(ph))
      f = getfield(ph, field)
      print(io, " - $field: $f")
      if i != length(fieldnames(ph))
         println(io)
      end
   end
end

const dt0 = now()
const ms_in_year = Dates.Millisecond(daysinyear(Dates.year(dt0)) * 24 * 60 * 60 * 1000)

function get_photo_dst_dir(dt::DateTime, dst_root::String)
   y, m, d = yearmonthday(dt)
   spring = Date(y, 3, 20)
   summer = Date(y, 6, 21)
   fall   = Date(y, 9, 22)
   winter = Date(y,12, 21)
   if dt < spring
      return joinpath(dst_root, string(y), "Winter")
   elseif dt < summer
      return joinpath(dst_root, string(y), "Spring")
   elseif dt < fall
      return joinpath(dst_root, string(y), "Summer")
   elseif dt < winter
      return joinpath(dst_root, string(y), "Fall")
   elseif dt >= winter
      # Consider part of next year's winter
      return joinpath(dst_root, string(y+1), "Winter")
   end
   error("Could not find season for $dt")
end

function parse_date(line, date)
      fields = split(date, [':', ' ', '-', '+', 'Z'])
      yr, m, d, H, M, S = (0, 0, 0, 0, 0, 0)
      #try
         yr, m, d, H, M, S = map(str -> parse(Int, str), fields[1:6])
      #catch err
      #   throw(ArgumentError("Could not parse date: $fields, $err"))
      #end
      if !(1980 < yr < 2100)
         throw(DomainError("year out of range ($yr) for date ($date), skipping: $line"))
      end
      if !(0 <= H <= 23)
         throw(DomainError("hour out of range ($H) for date ($date), skipping: $line"))
      end
      if !(0 <= M <= 59)
         throw(DomainError("minute out of range ($M) for date ($date), skipping: $line"))
      end
      if !(0 <= S <= 59)
         throw(DomainError("second out of range ($S) for date ($date), skipping: $line"))
      end
      return (yr, m, d, H, M, S)
end

function resolve_photo_date(Make::AbstractMake, FileType::AbstractFileType, line, pic)
      date = pic.date
		if date == "-" || startswith(date, "0000")
         date = pic.modifydate
      end
      if date == "-" || startswith(date, "0000")
         date = pic.filemodifydate
      end
      parse_date(line, date)
end

function resolve_photo_date(Make::Apple, FileType::AbstractFileType, line, pic)
   date = pic.date
	if pic.date2 != "-" && !startswith(pic.date2, "0000")
      date = pic.date2
   end
	if date == "-" || startswith(date, "0000")
      date = pic.filemodifydate
   end
   parse_date(line, date)
end

function resolve_make(pic)
      if pic.make == "Apple" || pic.make2 == "Apple"
         return Apple()
      else
         return AnyMake()
      end
end

function resolve_filetype(pic)
      if pic.filetype == "MP4"
         return MP4()
      else
         return AnyFileType()
      end
end

function _organize_photos(mount::String, dst_root::String, rm_src::Bool, dry_run::Bool, suffix::String, rm_dst::Union{Regex,Nothing})
   album = Vector{Photo}()
   if !isdir(mount)
       @warn "Mount doesn't exist, skipping:" mount
       return album
   end
   if dry_run
      @info("Reading from dir (DRY RUN): $mount")
   else
      @info("Reading from dir: $mount")
   end
   for line in eachline(Cmd(`exiftool -T -Make -HandlerVendorID -directory -filename -FileType -CreateDate -CreationDate -SubSecTime -SubSecCreateDate -modifydate -filemodifydate -model -r $mount`, ignorestatus=true))
      pic = NamedTuple{(:make, :make2, :dir, :fname, :filetype, :date, :date2, :subsec, :subsec2, :modifydate, :filemodifydate, :camera)}(split(line, ['\t']))
      #@show pic
      if pic.dir == "-"
         @warn("Bad SourceFile from exiftool: $line")
         continue
      end
      src = joinpath(pic.dir, pic.fname)
      backuped = false
      errmsg = missing
      dtmissing = missing
      dstmissing = missing
      print(src)
      local yr, m, d, H, M, S, Make, FileType
      Make = resolve_make(pic)
      FileType = resolve_filetype(pic)
      #try
        yr, m, d, H, M, S = resolve_photo_date(Make, FileType, line, pic)
      #catch err
      #   @warn(err)
      #   push!(album, Photo(src, dtmissing, dstmissing, backuped, errmsg))
      #   continue
      #end
      MS = 0
      if all(isnumeric, pic.subsec)
         #try
            MS = round(Int, parse(Float64, "0.$(pic.subsec)")*1000)
         #catch err
         #   @warn("Could not parse ms: $(pic.subsec), $err")
         #   push!(album, Photo(src, dtmissing, dstmissing, backuped, errmsg))
         #   continue
         #end
      elseif occursin(".", pic.subsec2)
         #try
            MS = split(split(pic.subsec2, '.')[end], "-")[1]
            MS = round(Int, parse(Float64, "0.$MS")*1000)
         #catch err
         #   @warn("Could not parse ms: $pic.subsec2, $err")
         #   continue
         #end
      end
      dt = DateTime(yr, m, d, H, M, S, MS)
      # Normalize name of new photo:
      ext = splitext(src)[2]
      new_dir = get_photo_dst_dir(dt, dst_root)
		camera = pic.camera
      if camera == "-"
         camera = ""
      else
         camera = string("_", replace(camera, " " => "_"))
      end
      froot = string(lpad(yr, 4, "0"), lpad(m, 2, "0"), lpad(d, 2, "0"), "_",
                     lpad(H,  2, "0"), lpad(M, 2, "0"), lpad(S, 2, "0"), ".",
                     lpad(MS, 3, "0"), camera, suffix)
      is_actual_dup = false
      is_rename_needed = false
      local dst, uniq_suffix
      for outer uniq_suffix in ["", map(v->string("_v$v"), 2:200)...]
         dst = joinpath(new_dir, string(froot, uniq_suffix, ext))
         if isfile(dst)
            if stat(src).size == stat(dst).size
               is_actual_dup = true
               break
            else
               is_rename_needed = true
               if length(uniq_suffix) == 0
                   printstyled(" renaming", color=:yellow)
               else
                   printstyled(" $uniq_suffix", color=:yellow)
               end
            end
         else
            break
         end
      end
      if is_rename_needed
         printstyled(" $uniq_suffix", color=:yellow)
      end
      if isfile(new_dir) && !isdir(new_dir)
          error("Expected directory but found a file:\n - $new_dir")
      end
      if !isdir(new_dir)
          printstyled(" mkdir -p $new_dir", color=:orange)
          if !dry_run
              run(`mkdir -p $new_dir`)
          end
      end
      backuped=false
      if !isnothing(rm_dst) && is_actual_dup && !isnothing(match(rm_dst, src))
         #printstyled(" rm src $src", color=:red)
         #if !dry_run
         #   run(`rm $src`)
         #end
         printstyled(" rm dst $dst", color=:red)
         if !dry_run
            run(`rm $dst`)
         end
         backuped=false
      elseif !isfile(dst) && !is_actual_dup
          printstyled(" cp $dst", color=:green)
          if !dry_run
              #try
                 #cp(src, dst, follow_symlinks=true)
                 run(`cp -dR --preserve=all $src $dst`)
                 backuped=true
              #catch err
              #   @warn("Cound not copy file: $src\n$err")
              #   continue
              #end
          end
      else
         printstyled(" Skipping dup (already backed up)", color=:green)
         backuped=true
      end
      age_in_years = (dt0 - dt) / ms_in_year
      if rm_src
          print(" rm src")
          if !dry_run
              try
                run(`rm $src`)
              catch err
                 printstyled(" rm src failed: $err", bold=true, color=:red)
              end
          end
      end
      println()
      push!(album, Photo(src, dt, dst, backuped, errmsg))
   end
   return album
end

struct MountStat
   mount
   photos::Vector{Photo}
end

function report_missing(mount_stats::Vector{MountStat})
   open("missing.log", "w") do f
     printstyled("The following files were not backed up (see missing.log):\n", bold=true, color=:red)
     for mount_stat in mount_stats
        src_dir = mount_stat.mount
        if !isdir(src_dir)
           if isnothing(src_dir)
             @warn("Source directory doesn't exist, skipping: 'nothing'")
           else
             @warn("Source directory doesn't exist, skipping: $src_dir")
           end
           continue
        end
        for file in eachline(`find $(src_dir) -type f`)
           if any(regex->occursin(regex, file), [r"Picasa\.ini$", r"album.txt", r"\.json$", r"\.DS_Store$", r"Thumbs\.db$", r"CardThumb\.db$", r"\.nomedia$"])
              continue  # non-important file
           end
           matching_photo_idx = findfirst(photo-> photo.src == file, mount_stat.photos)
           if matching_photo_idx ∉ [nothing, 0]
              photo = mount_stat.photos[matching_photo_idx]
              if !photo.backuped
                 print(file)
                 print(f, file)
                 printstyled(" !backuped", color=:red)
                 print(f, " !backuped")
                 if !ismissing(photo.err)
                    msg = get(photo.err)
                    printstyled(" $msg", color=:red)
                    print(f, " $msg")
                 end
                 println()
                 println(f)
              end
           else
              print(file)
              print(f, file)
              printstyled(" missing", color=:red)
              print(f, " missing")
              println()
              println(f)
           end
        end
     end
   end
end

"""
    organize_photos(src_dirs, dst_root; rm_src, dry_run, photo_suffix, rm_dst)

Move and rename photos in `src_dirs` source directories to an organized `dst_root` destination directory.

The destination directory is organized as follows:

    <root>/YYYY/<season>/YYYYMMDD_HHMMSS.SSS_<camera_model>.<extension>

where `season` is `Spring`, `Summer`, `Fall` or `Winter` (depending of photo's date).

# Arguments
- `src_dirs::Vector{String}`: dirctories containing photos to organize
- `dst_root:String`: the destination directory of organized photos
- `rm_src::Bool`: delete source photos if true
- `rm_dst::Regex`: delete destination photos if the source file matches this regex (to removes files added by accident)
- `dry_run::Bool`: if true then don't change anything, just print what would happen
- `photo_suffix::String`: append string to end of filename (e.g. to show who it came from: "_Glen")

# Examples
```julia-repl
julia> organize_photos(["/home/hertz/Documents.local/Pictures"], "/home/hertz/Pictures/Pictures", rm_src=false, dry_run=true)
```
"""
function organize_photos(src_dirs::Vector{String}, dst_root::String; rm_src::Bool, dry_run::Bool, photo_suffix="", rm_dst::Union{Regex,Nothing}=nothing)
    if dry_run
       @warn("DRY RUN: only printing what would happen")
    end
    mount_stats = Vector{MountStat}()
    for dir in src_dirs
       mount_stat = MountStat(dir, Photo[])
       for photo in _organize_photos(dir, dst_root, rm_src, dry_run, photo_suffix, rm_dst)
          push!(mount_stat.photos, photo)
       end
       push!(mount_stats, mount_stat)
    end
    @info("backed up photos from the following mounts:")
    for stat in mount_stats
       @info(" $(length(stat.photos)): $(stat.mount)")
    end
    total = sum(length(stat.photos) for stat in mount_stats)
    @info(" $total photos in total")
    report_missing(mount_stats)
    if dry_run
       @warn("DRY RUN: only printing what would happen")
    end
end

function organize_photos(src_dir::String, dst_root::String; kwargs...)
    organize_photos([src_dir], dst_root; kwargs...)
end

end # module


text:
using Cropbox
using SimpleCrop
using Test

using CSV
using DataFrames
using TimeZones

loaddata(f) = CSV.File(joinpath(@__DIR__, "data", f)) |> DataFrame

config = @config (
    :Clock => :step => 1u"d",
    :Calendar => :init => ZonedDateTime(1987, 1, 1, tz"UTC"),
    :Weather => :weather_data => loaddata("weather.csv"),
    :SoilWater => :irrigation_data => loaddata("irrigation.csv"),
)

# frequency of printout (days)
FROP = 3u"d"

r = simulate(SimpleCrop.Model;
    config,
    stop = :endsim,
    #snap = s -> iszero(s.DOY' % FROP),
)

@testset "simplecrop" begin
    @testset "plant" begin
        visualize(r, :DATE, :N; kind = :line) |> display
        visualize(r, :DATE, :INT; kind = :line) |> display
        visualize(r, :DATE, :W; kind = :line) |> display
        visualize(r, :DATE, :Wc; kind = :line) |> display
        visualize(r, :DATE, :Wr; kind = :line) |> display
        visualize(r, :DATE, :Wf; kind = :line) |> display
        visualize(r, :DATE, :LAI; kind = :line) |> display
    end

    @testset "soil water" begin
        visualize(r, :DATE, :ROF; kind = :line) |> display
        visualize(r, :DATE, :INF; kind = :line) |> display
        visualize(r, :DATE, :DRN; kind = :line) |> display
        visualize(r, :DATE, :ETp; kind = :line) |> display
        visualize(r, :DATE, :ESa; kind = :line) |> display
        visualize(r, :DATE, :EPa; kind = :line) |> display
        visualize(r, :DATE, :SWC; kind = :line) |> display
        visualize(r, :DATE, :(SWC/DP); yunit = u"mm^3/mm^3", kind = :line) |> display
        visualize(r, :DATE, :SWFAC1; kind = :line) |> display
        visualize(r, :DATE, :SWFAC2; kind = :line) |> display
    end

    @testset "water balance" begin
        println("Initial soil water content: ", r[end, :SWC0])
        println("Final soil water content: ", r[end, :SWC])
        println("Total rainfall depth: ", r[end, :TRAIN])
        println("Total irrigation depth: ", r[end, :TIRR])
        println("Total soil evaporation: ", r[end, :TESa])
        println("Total plant transpiration: ", r[end, :TEPa])
        println("Total surface runoff: ", r[end, :TROF])
        println("Total vertical drainage: ", r[end, :TDRN])
        println("Water balance: ", r[end, :WATBAL])
    end
end


text:
"""
    Zarate

A Sugiyaqma style layout for DAGs and Sankey diagrams.
Based on

Zarate, D. C., Le Bodic, P., Dwyer, T., Gange, G., & Stuckey, P. (2018, April).
Optimal sankey diagrams via integer programming.
In 2018 IEEE Pacific Visualization Symposium (PacificVis) (pp. 135-139). IEEE.

# Fields
 - `time_limit::Dates.Period`: how long to spend trying alternative orderings.
   There are often many possible orderings that have the same amount of crossings.
   There is a chance that one of these will allow a better arrangement than others.
   Mostly the first solution tends to be very good.
   By setting a `time_limit > Second(0)`, multiple will be tried until the time limit is exceeded.
   (Note: that this is not a maximum time, but rather a limit that once exceeded no more
   attempts will be made.).
   If you have a `time_limit` greater than `Second(0)` set then the result is no longer deterministic.
   Note also that this is heavily affected by first call compilation time.
"""
Base.@kwdef struct Zarate <: AbstractLayout
    time_limit::Dates.Period = Dates.Second(0)
    ordering_solver::Any = ()->Cbc.Optimizer(; randomSeed=1, randomCbcSeed=1)
    arranging_solver::Any = ECOS.Optimizer
end

"""
    solve_positions(::Zarate, graph)

Returns:
 - `xs`: the xs coordinates of vertices in the layout
 - `ys`: the ys coordinates of vertices in the layout
 - `paths`: a Dict which, for each edge in `graph`, contains a Tuple of coordinate vectors (xs, ys).

The layout is calculated on a graph where dummy nodes can be added to the different layers.
As a result, plotting edges as straight lines between two nodes can result in
more crossings than optimal : edges should instead be routed through these different dummy nodes.
`paths` contains for each edge, a Tuple of vectors, representing that route through the
different nodes as x and y coordinates.

# Example:
```julia
using LightGraphs, Plots

g = random_orientation_dag(complete_graph(5))
xs, ys, paths = solve_positions(Zarate(), g)
scatter(xs, ys)
for e in edges(g)
    e_xs, e_ys = paths[e]
    plot!(e_xs, e_ys)
end
```
"""
function solve_positions(layout::Zarate, original_graph)
    graph = copy(original_graph)

    # 1. Layer Assigment
    layer2nodes = layer_by_longest_path_to_source(graph)
    is_dummy_mask, edge_to_path = add_dummy_nodes!(graph, layer2nodes)

    # 2. Layer Ordering
    start_time = Dates.now()
    min_total_distance = Inf
    min_num_crossing = Inf
    local best_pos
    ordering_model, is_before = ordering_problem(layout, graph, layer2nodes)
    for round in 1:typemax(Int)
        round > 1 && forbid_solution!(ordering_model, is_before)

        optimize!(ordering_model)
        # No need to keep banning solutions if not finding optimal ones anymore
        round > 1 && termination_status(ordering_model) != MOI.OPTIMAL && break
        num_crossings = objective_value(ordering_model)
        # we are not interested in arrangements that have more crossings, only in
        # alternatives with same number of crossings.
        num_crossings > min_num_crossing && break
        min_num_crossing = num_crossings
        order_layers!(layer2nodes, is_before)

        # 3. Node Arrangement
        xs, ys, total_distance = assign_coordinates(layout, graph, layer2nodes)
        if total_distance < min_total_distance
            min_total_distance = total_distance
            best_pos = (xs, ys)
        end
        Dates.now() - start_time > layout.time_limit && break
    end
    xs, ys = best_pos
    return xs[.!is_dummy_mask], ys[.!is_dummy_mask], Dict(edge => (xs[path], ys[path]) for (edge, path) in edge_to_path)
end

"""
    ordering_problem(::Zarate, graph, layer2nodes)

Formulates the problem of working out optimal ordering as a MILP.

Returns:
 - `model::Model`: the JuMP model that when optized will find the optimal ordering
 - `is_before::AbstractVector{AbstractVector{Variable}}`: the variables of the model,
   which once solved will have `value(is_before[n1][n2]) == true`
   if `n1` is best arrange before `n2`.
"""
function ordering_problem(layout::Zarate, graph, layer2nodes)
    m = Model(layout.ordering_solver)
    set_silent(m)

    node_is_before = Vector{Any}(undef, nv(graph))
    for (layer, nodes) in enumerate(layer2nodes)
        before = @variable(m, [nodes, nodes], Bin, base_name="befores_$layer")
        for n1 in nodes
            node_is_before[n1] = before[n1, :]
            for n2 in nodes
                n1 === n2 && continue
                # can't have n1<n2 and n2<n1
                @constraint(m, before[n1, n2] + before[n2, n1] == 1)
                for n3 in nodes
                    (n1 === n3 || n2 === n3) && continue
                    # at most two of these 3 hold
                    @constraint(m , before[n1, n2] + before[n2, n3] + before[n3, n1] <= 2)
                end
            end
        end
    end

    weights_mat = weights(graph)

    function crossings(src_layer)
        total = AffExpr(0)
        TDict{V} = Dict{Tuple{Int,Int}, V}
        crossing_vars = TDict{TDict{VariableRef}}()
        for src1 in src_layer, src2 in src_layer
            for dst1 in outneighbors(graph, src1), dst2 in outneighbors(graph, src2)
                # Can't cross if share end-point
                (src1 === src2 || dst1 === dst2) && continue
                # Zarate et al section "Further improvements with branching priorities"
                # we don't make this binary even though it is, because that makes the branchs happen
                # at wrong places. It only needs to branch the `before_*` variables.
                # the crossing will be binary as a result
                crossing = @variable(m, binary=false, integer=false, base_name="cross $src1-$dst2 x $src1-$dst2")
                get!(TDict{VariableRef}, crossing_vars, (src1, dst1))[src2, dst2] = crossing
                # two edges cross if the src1 is before scr2; but dst1 is after dest2
                @constraint(m, node_is_before[src1][src2] + node_is_before[dst2][dst1] - 1 <= crossing)

                # for Sankey diagrams we minimise not just crossing but area of crossing
                # treating the weights of the graph as the widths of the line and ignoring skew
                w1 = weights_mat[src1, dst1]
                w2 = weights_mat[src2, dst2]
                area = w1*w2
                add_to_expression!(total, area, crossing)
            end
        end

        # perfomance optimizations based on Zarate eq 8 and 9
        for ((u1,v1), other_edges) in crossing_vars
            for ((u2, v2), c1) in other_edges
                c2 = crossing_vars[(u2,v2)][(u1,v1)]
                @constraint(m, c1==c2)  # Zarate eq 8

                cx = try_get(try_get(crossing_vars, (u1,v2)), (u2,v1))
                cx === nothing && continue
                @constraint(m, c1 + cx == 1)# Zarate eq 9
            end
        end
        return total
    end

    @objective(m, Min, sum(crossings, layer2nodes))
    #@show m
    return m, node_is_before
end

function order_layers!(layer2nodes, is_before)
    # Cunning trick: we need to go from the `before` matrix to an actual order list
    # we can do this by sorting when having `lessthan` read from the `before` matrix
    is_before_func(n1, n2) = Bool(value(is_before[n1][n2]))
    for layer in layer2nodes
        sort!(layer; lt=is_before_func)
    end
    return layer2nodes
end

"""
    forbid_solution!(m, is_before)

Modifies the JuMP model `m` to forbid the solution present in `is_before`.
`is_before` must contain solved variables.
"""
function forbid_solution!(m, is_before)
    cur_trues = AffExpr(0)
    total_trues = 0
    for var_list in is_before
        for var in var_list
            if value(var) > 0.5
                add_to_expression!(cur_trues, var)
                total_trues += 1
            end
        end
    end
    # for it to be a different order some of the ones that are currently true must swap to being false
    @constraint(m, sum(cur_trues) <= total_trues - 1)

    return m
end

"""
    assign_coordinates(graph, layer2nodes)

Works out the `x` and `y` coordinates for each node in the `graph`.
This is via formulating the problem as a QP, and minimizing total distance
of links.
It maintains the order given in `layer2nodes`s.
returns: `xs, ys, total_distance`
"""
function assign_coordinates(layout, graph, layer2nodes)
    m = Model(layout.arranging_solver)
    set_silent(m)
    set_optimizer_attribute(m, "print_level", 0)  # TODO this can be deleted once the version of IPOpt that actually supports `set_silent` is released

    node2y = Dict{Int, VariableRef}()
    for (layer, nodes) in enumerate(layer2nodes)
        first_node, other_nodes = Iterators.peel(nodes)
        prev_y = node2y[first_node] = @variable(m, base_name="y_$first_node")
        for node in other_nodes
            # each must be greater than the last, with a gap of 1 unit
            y = @variable(m, base_name="y_$node")
            @constraint(m, prev_y + 1.0 <= y)
            prev_y = node2y[node] = y
        end
    end

    all_distances = AffExpr[]
    # minimize link distances
    for cur in vertices(graph)
        for out in outneighbors(graph, cur)
            distance = (node2y[cur] - node2y[out])
            push!(all_distances, distance)
        end
        # to prevent going way off-scale, also keep things near x-axis
    end
    # for first layer minimize distance to origin
    for cur in first(layer2nodes)
        distance = node2y[cur]
        push!(all_distances, distance)
    end
    @variable(m, total_distance)
    distance_cone = [1; total_distance; all_distances]
    @constraint(m, distance_cone in RotatedSecondOrderCone())
    @objective(m, Min, total_distance);
    optimize!(m)
    #termination_status(m)
    score = objective_value(m)

    ###
    xs = Vector{Float64}(undef, nv(graph))
    ys = Vector{Float64}(undef, nv(graph))
    for (xi, nodes) in enumerate(layer2nodes)
        for node in nodes
            xs[node] = xi
            ys[node] = value(node2y[node])
        end
    end
    return xs, ys, score
end


text:
@testset "UDP" begin

@testset "decode" begin
    ep = decode_ethernet(dns_packet)
    ipp = decode_ipv4(ep.payload)
    udpp = decode_udp(ipp.payload)
    @test udpp.header.src_port == 0xe587
    @test udpp.header.dst_port == 0x0035
    @test udpp.header.length == 48
    @test udpp.header.checksum == 0x5b6d
    @test udpp.payload == [
        0xab, 0xc9, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x6d, 0x63, 0x63,
        0x6c, 0x65, 0x6c, 0x6c, 0x61, 0x6e, 0x02, 0x63, 0x73, 0x05, 0x6d, 0x69, 0x61, 0x6d, 0x69, 0x03,
        0x65, 0x64, 0x75, 0x00, 0x00, 0x01, 0x00, 0x01
    ]
end

end

max_stars_repo_name: ancapdev/NetworkProtocols

max_issues_repo_name: ancapdev/NetworkProtocols


text:
"""
  Description
    Methods implemented by components that need to describe the Compounds that occur or can occur in a Material.

  Methods

    getcompoundlist()
    getcompoundconstant!()
    getconstproplist()
    getnumcompounds()
    getpdependentproperty!()
    getpdependentproplist()
    gettdependentproperty!()
    gettdependentproplist()

"""
module ICapeThermoCompounds
    export getconstproplist,gettdependentproplist,getpdependentproplist,getnumcompounds,getcompoundlist
    export getcompoundconstant!,getpdependentproperty!,gettdependentproperty!
    using PhysicalPropertyCalculator,ECapeExceptions,CapeOpen
    import PhysicalPropertyCalculator.TempPropData
    import ThermodynamicsTable.readbinarydatabase
    spp=CapeOpen.perryanalytic
    """
      Description

        Returns the list of supported constant Physical Properties.
      Arguments

        [retval] props::Vector{ASCIIString}
      Exceptions

        ECapeUnknown – The error to be raised when other error(s), specified for the GetConstPropList operation, are not suitable.
    """
    function getconstproplist(
        proppackage::PropertyPackage=spp)
        props=Vector{ASCIIString}()
        try
          props = [keys(proppackage.constantstrings)...; keys(proppackage.constantfloats)...]
          return props
        catch
          throw(ECapeUnknown())
        end
    end

    """
      Description

        Returns the list of supported temperature-dependent Physical Properties.
      Arguments

        [retval]props::Vector{ASCIIString}
      Exceptions

        ECapeUnknown – The error to be raised when other error(s), specified for the GetConstPropList operation, are not suitable.
    """
    function gettdependentproplist(
      proppackage::PropertyPackage=spp)
      props=Vector{ASCIIString}()
      try
        props = [keys(proppackage.tempreturedependents)...]
        return props
      catch
        throw(ECapeUnknown())
      end
    end

    """
      Description

        Returns the list of supported pressure-dependent properties.
      Arguments

        [retval]props::Vector{ASCIIString}
      Exceptions

        ECapeUnknown – The error to be raised when other error(s), specified for the GetConstPropList operation, are not suitable.
    """
    function getpdependentproplist(
      proppackage::PropertyPackage=spp)
      props=Vector{ASCIIString}()
      try
        props = [keys(proppackage.pressuredependents)...]
        return props
      catch
        throw(ECapeUnknown())
      end
    end

    """
      Description

        Returns the number of Compounds supported.
      Arguments

        [in] proppackage::PropertyPackage
        [retval] num::Int32
      Exceptions

        ECapeUnknown – The error to be raised when other error(s), specified for the GetConstPropList operation, are not suitable.
    """
    function getnumcompounds(
      proppackage::PropertyPackage=spp)
      try
        return  spp.tableaddreses["Compounds"][1][2]
      catch
        throw(ECapeUnknown())
      end
    end

    """
      Description

        Returns the list of all Compounds. This includes the Compound identifiers recognised and extra information that can be used to further identify the Compounds.
      Arguments

        [retval] compIds::Vector{ASCIIString} List of Compound identifiers.
        [retval] formulae::Vector{ASCIIString} List of Compound formulae.
        [retval] names::Vector{ASCIIString} List of Compound names.
        [retval] boilTemps::Vector{Float64} List of boiling point temperatures.
        [retval] molwts::Vector{Float64} List of molecular weights.
        [retval] casnos::Vector{ASCIIString}) List of Chemical Abstract Service (CAS) Registry numbers.
      Exceptions

        ECapeUnknown – The error to be raised when other error(s), specified for the GetConstPropList operation, are not suitable.
      Note

        If any item cannot be returned then the value should be set to UNDEFINED,0%Int16,Float64(NaN) for different types.
    """
    function getcompoundlist(
      proppackage::PropertyPackage=spp)
      try
        size=spp.tableaddreses["Compounds"][1][2]
        compIds=Vector{UInt16}(size)
        formulae=Vector{ASCIIString}(size)
        names=Vector{ASCIIString}(size)
        boilTemps=Vector{Float64}(size)
        molwts=Vector{Float64}(size)
        casnos=Vector{ASCIIString}(size)
        for id = 1:size
          compIds[id]=id%UInt16
          data=readbinarydatabase(compIds[id],"Compounds",0%UInt8)
          formulae[id]=copy(rstrip(ASCIIString(data[2])))
          names[id]=copy(rstrip(ASCIIString(data[1])))
          casnos[id]=copy(rstrip(ASCIIString(data[3])))
          molwts[id]=data[4]
          boilTemps[id]=data[5]
        end
        return compIds,formulae,names,boilTemps,molwts,casnos
      catch err
        throw(ECapeUnknown())
      end
    end

    """
      Description

        Returns the values of constant Physical Properties for the specified Compounds.
      Arguments

        [in] props::Vector{ASCIIString}
        [in] compIds::Vector{UInt16}) List of Compound identifiers for which constants are to be retrieved. Set compIds to 0%UInt16 to denote all Compounds in the component that implements the ICapeThermoCompounds interface.
        [in out] propvals::Vector{Any}
      Exceptions

        ECapeThrmPropertyNotAvailable – At least one item in the list of Physical Properties is not available for a particular Compound. This exception is meant to be treated as a warning rather than as an error.
        ECapeLimitedImpl – One or more Physical Properties are not supported by the component that implements this interface.
        This exception should also be raised if any element of the props argument is not recognised.
        ECapeInvalidArgument – To be used when an invalid argument value is passed, for example, an unrecognised Compound identifier or UNDEFINED for the props argument.
        ECapeUnknown – The error to be raised when other error(s), specified for the operation, are not suitable.
      Note

        The GetConstPropList method can be used in order to check which constant Physical Properties are available.
        If the number of requested Physical Properties is P and the number of Compounds is C, the propvals array will contain C*P variants.
        The first C variants will be the values for the first requested Physical Property (one for each Compound) followed by C values of constants for the second Physical Property, and so on.
        Physical Properties are returned in a fixed set of units as specified in section 7.5.2.
        If the compIds argument is set to 0%UInt16 this is a request to return property values for all compounds in the component that implements the ICapeThermoCompounds interface with the compound order the same as that returned by the GetCompoundList method.
        If any Physical Property is not available for one or more Compounds, then UNDEFINED,Float64(NaN) values must be returned for those combinations and an ECapeThrmPropertyNotAvailable exception must be raised.
    """
    function getcompoundconstant!(
      props::Vector{ASCIIString},
      compIds::Vector{UInt16},
      propvals::Vector{Union{ASCIIString,Float64}},
      proppackage::PropertyPackage=spp)
      thrownotavailable::Bool=false
      (compIds==[0%UInt16]) && (compIds=[i for i=1%UInt16:345%UInt16])
      allprops=getconstproplist(proppackage)
      for prop in props
        !(prop in allprops) && throw(ECapeInvalidArgument())
        unc::Float64=unitconvertion(proppackage,prop)
        for compId in compIds
            (compId>345%UInt16 || compId==0%UInt16) && throw(ECapeInvalidArgument())
          try
            data=getconstpropdata(proppackage,prop,compId)
            propval=calculate(prop,data)
            push!(propvals, isa(propval,ASCIIString) ? copy(rstrip(propval)) : (propval*unc))
            isundefied(propval) && throw(ECapeThrmPropertyNotAvailable())
          catch err
            if isa(err,ECapeThrmPropertyNotAvailable)
              thrownotavailable=true
            elseif isa(err,ECapeInvalidArgument)
              throw(err)
            elseif isa(err,ECapeLimitedImpl)
              throw(err)
            else
              throw(ECapeUnknown())
            end
          end
        end
      end
      thrownotavailable && throw(ECapeThrmPropertyNotAvailable())
      return propvals
    end

    """
      Description

        Returns the values of pressure-dependent Physical Properties for the specified pure Compounds.
      Arguments

        [in] props::Vector{ASCIIString}
        [in] pressure::Float64
        [in] compIds::Vector{UInt16}) List of Compound identifiers for which constants are to be retrieved. Set compIds to 0%UInt16 to denote all Compounds in the component that implements the ICapeThermoCompounds interface.
        [in out] propvals::Vector{Any}
      Exceptions

        ECapeThrmPropertyNotAvailable – at least one item in the properties list is not available for a particular compound.
        ECapeLimitedImpl – One or more Physical Properties are not supported by the component that implements this interface.
        This exception should also be raised if any element of the props argument is not recognised.
        ECapeInvalidArgument – To be used when an invalid argument value is passed, for example UNDEFINED for argument props.
        ECapeOutOfBounds – The value of the temperature is outside of the range of values accepted by the Property Package.
        ECapeUnknown – The error to be raised when other error(s), specified for the operation, are not suitable.
      Note

        The GetTDependentPropList method can be used in order to check which Physical Properties are available.
        If the number of requested Physical Properties is P and the number of Compounds is C, the propvals array will contain C*P values.
        The first C will be the values for the first requested Physical Property followed by C values for the second Physical Property, and so on.
        Properties are returned in a fixed set of units as specified in section 7.5.3.
        If the compIds argument is set to 0%UInt16 this is a request to return property values for all compounds in the component that implements the ICapeThermoCompounds interface with the compound order the same as that returned by the GetCompoundList method.
        If any Physical Property is not available for one or more Compounds, then UNDEFINED,Float64(NaN) values must be returned for those combinations and an ECapeThrmPropertyNotAvailable exception must be raised.
    """
    function getpdependentproperty!(
      props::Vector{ASCIIString},
      pressure::Float64,
      compIds::Vector{UInt16},
      propvals::Vector{Float64},
      proppackage::PropertyPackage=spp)
      thrownotavailable::Bool=false
      (compIds==[0%UInt16]) && (compIds=[i for i=1%UInt16:345%UInt16])
      allprops=getpdependentproplist(proppackage)
      try
        for prop in props
          !(prop in allprops) && throw(ECapeInvalidArgument())
          unc::Float64=unitconvertion(proppackage,prop)
          for compId in compIds
            (compId>345%UInt16 || compId==0%UInt16) && throw(ECapeInvalidArgument())
            temppropdata::TempPropData=TempPropData(proppackage,prop,compId)
            (pressure<0) && throw(ECapeOutOfBounds())
            propval::Float64=calculate(temppropdata,pressure)
            push!(propvals, propval*unc)
            isnan(propval) && throw(ECapeThrmPropertyNotAvailable())
          end
        end
      catch err
        if isa(err,ECapeThrmPropertyNotAvailable)
          thrownotavailable=true
        elseif isa(err,ECapeOutOfBounds)
          thrownotavailable=true
          push!(propvals, NaN)
        elseif isa(err,ECapeInvalidArgument)
          throw(err)
        elseif isa(err,ECapeLimitedImpl)
          throw(err)
        else
          throw(ECapeUnknown())
        end
      end
      thrownotavailable && throw(ECapeThrmPropertyNotAvailable())
      return propvals
    end

    """
      Description

        Returns the values of temperature-dependent Physical Properties for the specified pure Compounds.
      Arguments

        [in] props::Vector{ASCIIString}
        [in] temperature::Float64
        [in] compIds::Vector{UInt16}) List of Compound identifiers for which constants are to be retrieved. Set compIds to 0%UInt16 to denote all Compounds in the component that implements the ICapeThermoCompounds interface.
        [in out] propvals::Vector{Any}
      Exceptions

        ECapeThrmPropertyNotAvailable – at least one item in the properties list is not available for a particular compound.
        ECapeLimitedImpl – One or more Physical Properties are not supported by the component that implements this interface.
        This exception should also be raised if any element of the props argument is not recognised.
        ECapeInvalidArgument – To be used when an invalid argument value is passed, for example UNDEFINED for argument props.
        ECapeOutOfBounds – The value of the temperature is outside of the range of values accepted by the Property Package.
        ECapeUnknown – The error to be raised when other error(s), specified for the operation, are not suitable.
      Note

        The GetTDependentPropList method can be used in order to check which Physical Properties are available.
        If the number of requested Physical Properties is P and the number of Compounds is C, the propvals array will contain C*P values.
        The first C will be the values for the first requested Physical Property followed by C values for the second Physical Property, and so on.
        Properties are returned in a fixed set of units as specified in section 7.5.3.
        If the compIds argument is set to 0%UInt16 this is a request to return property values for all compounds in the component that implements the ICapeThermoCompounds interface with the compound order the same as that returned by the GetCompoundList method.
        If any Physical Property is not available for one or more Compounds, then UNDEFINED,Float64(NaN) values must be returned for those combinations and an ECapeThrmPropertyNotAvailable exception must be raised.
    """
    function gettdependentproperty!(
      props::Vector{ASCIIString},
      temperature::Float64,
      compIds::Vector{UInt16},
      propvals::Vector{Float64},
      proppackage::PropertyPackage=spp)
      thrownotavailable::Bool=false
      (compIds==[0%UInt16]) && (compIds=[i for i=1%UInt16:345%UInt16])
      allprops=gettdependentproplist(proppackage)
      for prop in props
        !(prop in allprops) && throw(ECapeInvalidArgument())
        unc::Float64=unitconvertion(proppackage,prop)
        for compId in compIds
          (compId>345%UInt16 || compId==0%UInt16) && throw(ECapeInvalidArgument())
          try
            temppropdata::TempPropData=TempPropData(proppackage,prop,compId)
            (temperature<0 || temperature>6000) && throw(ECapeOutOfBounds())
            temppropdata.t=temperature
            propval::Float64=calculate(temppropdata)
            push!(propvals, propval*unc)
            isnan(propval) && throw(ECapeThrmPropertyNotAvailable())
          catch err
            if isa(err,ECapeThrmPropertyNotAvailable)
              thrownotavailable=true
            elseif isa(err,ECapeOutOfBounds)
              thrownotavailable=true
              push!(propvals, NaN)
            elseif isa(err,ECapeInvalidArgument)
              throw(err)
            elseif isa(err,ECapeLimitedImpl)
              throw(err)
            else
              throw(ECapeUnknown())
            end
          end
        end
      end
      thrownotavailable && throw(ECapeThrmPropertyNotAvailable())
      return propvals
    end

  #***********************************************
  #                 Privates
  #***********************************************
  function getconstpropdata(proppackage::PropertyPackage, prop::ASCIIString, compId::UInt16)
    table::ASCIIString
    if haskey(proppackage.constantstrings, prop)
      table=proppackage.constantstrings[prop]
    elseif haskey(proppackage.constantfloats, prop)
      table=proppackage.constantfloats[prop]
    end
    return readbinarydatabase(compId,table,0%UInt8)
  end

  function TempPropData(proppackage::PropertyPackage, prop::ASCIIString, compId::UInt16, skipdata::UInt8=0%UInt8)
    table::ASCIIString
    floatdata::Vector{Float64}
    temppropdata::TempPropData
    if (haskey(proppackage.tempreturedependents,prop))
      table=proppackage.tempreturedependents[prop]
    elseif (haskey(proppackage.pressuredependents,prop))
      table=proppackage.pressuredependents[prop]
    else
      throw(ECapeInvalidArgument())
    end
    data=readbinarydatabase(compId,table,skipdata)
    floatdata=data[1]
    temppropdata=TempPropData(prop,floatdata[2:end],compId,floatdata[1])
    if (prop in["idealGasEntropy","idealGasEnthalpy","volumeOfLiquid","heatCapacityOfLiquid","idealGasHeatCapacity"])
      temppropdata.eqno=data[2]
    end
    if (prop in ["heatCapacityOfLiquid","heatOfVaporization"])
      temppropdata.tc=calculate(prop,getconstpropdata(proppackage,"criticalTemperature",compId))
    end
    if (prop in ["heatCapacityOfLiquid","heatOfVaporization","idealGasHeatCapacity","thermalConductivityOfLiquid","vaporPressure","boilingPointTemperature","viscosityOfLiquid"])
      temppropdata.test=temppropdata.c[6:9]
    elseif (prop in ["thermalConductivityOfVapor","viscosityOfVapor","volumeOfLiquid"])
      temppropdata.test=temppropdata.c[5:8]
    elseif (prop in ["idealGasEnthalpy", "idealGasEntropy"])
      temppropdata.test=[temppropdata.c[6],NaN,temppropdata.c[8],NaN]
    end
    return temppropdata
  end

  """
    Return convertion factor to convert units from perry to cape-open standard, for other properties default value of 1.0 is used

    6-  criticalDensity =>  mol/m3 mol/dm3 *1E+3
    7-  criticalPressure =>  Pa MPa *1E6
    9-  criticalVolume => m3/mol m3/Kmol /1E3
    15- heatOfVaporizationAtNormalBoilingPoint => J/mol J/kmol /1E3
    16- idealGasGibbsFreeEnergyOfFormationAt25C => J/mol J/kmol /1E3
    18- liquidVolumeAt25C => m3/mol dm3/mol /1E3
    24- standardEntropyGas => J/mol J/Kmol /1E3
    28- standardFormationEnthalpyGas => J/mol  J/Kmol /1E3
    31- standardFormationGibbsEnergyGas => J/mol J/Kmol /1E3
    6-  heatCapacityOfLiquid => J/(mol K) J/(Kmol K) /1E3
    11- heatOfVaporization => J/mol J/kmol /1E3
    12- idealGasEnthalpy => J/mol J/Kmol /1E3
    13- idealGasEntropy =>  J/(mol K) J/(Kmol K) /1E3
    14- idealGasHeatCapacity =>  J/(mol K) J/(Kmol K) /1E3
    32- volumeOfLiquid => m3/mol dm3/mol /1E3
  """
  function unitconvertion(proppackage::PropertyPackage, prop::ASCIIString)
    haskey(proppackage.convertions, prop) && return proppackage.convertions[prop]
    return 1.0
  end

  isundefied(x::Float64)=isnan(x)
  isundefied(x::ASCIIString)=(x=="UNDEFINED")

end


text:
using Base.Test

include("../examples/easy_test.jl")

@testset "easy_test" begin
    include("../examples/easy_test.jl")
end

@testset "harder_test" begin
    include("../examples/harder_test.jl")
end

@testset "large_simple_test" begin
    include("../examples/large_simple_test.jl")
end

@testset "explicit_stochastic" begin
    include("../examples/explicit_stochastic.jl")
end

@testset "transportation" begin
    include("../examples/transportation.jl")
end

@testset "parmodel1" begin
    include("../examples/parmodel1.jl")
    using Ipopt
    setsolver(firststage, IpoptSolver(print_level=0))
    solve(firststage)
end

@testset "parmodel2" begin
    include("../examples/parmodel2.jl")
    using Ipopt
    setsolver(firststage, IpoptSolver(print_level=0))
    solve(firststage)
end

@testset "parmodel3" begin
    include("../examples/parmodel3.jl")
    using Ipopt
    setsolver(firststage, IpoptSolver(print_level=0))
    solve(firststage)
end

@testset "parmodel4" begin
    include("../examples/parmodel4.jl")
    using Ipopt
    setsolver(firststage, IpoptSolver(print_level=0))
    solve(firststage)
end

#=
@testset "stochpdegas" begin
    include("../examples/stochpdegas.jl")
end
=#

#=
@testset "simple" begin
    include("../examples/simple.jl")
end
=#


text:
# AUTO GENERATED FILE - DO NOT EDIT

export vtk_geometryrepresentation

"""
    vtk_geometryrepresentation(;kwargs...)
    vtk_geometryrepresentation(children::Any;kwargs...)
    vtk_geometryrepresentation(children_maker::Function;kwargs...)

A GeometryRepresentation component.
GeometryRepresentation is responsible to convert a vtkPolyData into rendering
It takes the following set of properties:
  - actor: Properties to assign to the vtkActor
  - mapper: Properties to assign to the vtkMapper
  - property: Properties to assign to the vtkProperty (actor.getProperty())
  - colorMapPreset: Name of the preset to use for controlling the color mapping
  - colorDataRange: Range to use for the color scale
Keyword arguments:
- `children` (Array of a list of or a singular dash component, string or numbers | a list of or a singular dash component, string or number; optional)
- `id` (String; optional): The ID used to identify this component.
- `actor` (Dict; optional): Properties to set to the actor
- `colorDataRange` (Array of Reals; optional): Data range use for the colorMap
- `colorMapPreset` (String; optional): Preset name for the lookup table color map
- `cubeAxesStyle` (Dict; optional): Configure cube Axes style by overriding the set of properties defined
https://github.com/Kitware/vtk-js/blob/HEAD/Sources/Rendering/Core/CubeAxesActor/index.js#L703-L719
- `mapper` (Dict; optional): Properties to set to the actor
- `property` (Dict; optional): Properties to set to the actor.property
- `showCubeAxes` (Bool; optional): Show/Hide Cube Axes for the given representation
"""
function vtk_geometryrepresentation(; kwargs...)
        available_props = Symbol[:children, :id, :actor, :colorDataRange, :colorMapPreset, :cubeAxesStyle, :mapper, :property, :showCubeAxes]
        wild_props = Symbol[]
        return Component("vtk_geometryrepresentation", "GeometryRepresentation", "dash_vtk", available_props, wild_props; kwargs...)
end

vtk_geometryrepresentation(children::Any; kwargs...) = vtk_geometryrepresentation(;kwargs..., children = children)
vtk_geometryrepresentation(children_maker::Function; kwargs...) = vtk_geometryrepresentation(children_maker(); kwargs...)

max_stars_repo_name: rodrigomologni/dash-vtk

max_issues_repo_name: rodrigomologni/dash-vtk

max_forks_repo_name: rodrigomologni/dash-vtk