text:
module KeywordCalls

using Compat

export @kwcall

@generated _sort(nt::NamedTuple{K}) where {K} = :(NamedTuple{($(QuoteNode.(sort(collect(K)))...),)}(nt))

function _call_in_default_order end

# Thanks to @simeonschaub for this implementation
"""
    @kwcall f(b,a,c)

Declares that any call `f(::NamedTuple{N})` with `sort(N) == (:a,:b,:c)`
should be dispatched to the method already defined on `f(::NamedTuple{(:b,:a,:c)})`
"""
macro kwcall(ex)
    _kwcall(ex).q
end

function _kwcall(ex)
    @assert Meta.isexpr(ex, :call)
    f = ex.args[1]
    args = ex.args[2:end]
    f, args, sorted_args = esc(f), QuoteNode.(args), QuoteNode.(sort(args))
    q = quote
        KeywordCalls._call_in_default_order(::typeof($f), nt::NamedTuple{($(sorted_args...),)}) = $f(NamedTuple{($(args...),)}(nt))
        $f(nt::NamedTuple) = KeywordCalls._call_in_default_order($f, _sort(nt))
        $f(; kw...) = $f(NamedTuple(kw))
    end
    return (f=f, args=args, sorted_args=sorted_args, q=q)
end

export @kwstruct

"""
    @kwstruct Foo(b,a,c)

Equivalent to `@kwcall Foo(b,a,c)` plus a definition

    Foo(nt::NamedTuple{(:b, :a, :c), T}) where {T} = Foo{(:b, :a, :c), T}(nt)

Note that this assumes existence of a `Foo` struct of the form

    Foo{N,T} [<: SomeAbstractTypeIfYouLike]
        someFieldName :: NamedTuple{N,T}
    end
"""
macro kwstruct(ex)
    _kwstruct(ex)
end

function _kwstruct(ex)
    setup = _kwcall(ex)
    (f, args, q) = setup.f, setup.args, setup.q
    push!(q.args, :($f(nt::NamedTuple{($(args...),),T}) where {T} = $f{($(args...),), T}(nt)))

    return q
end

end


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

# filesystem operations

export
    watch_file,
    poll_fd,
    poll_file,
    FileMonitor,
    PollingFileWatcher,
    FDWatcher

import Base: @handle_as, wait, close, uvfinalize, eventloop, notify_error, stream_wait,
    _sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close,
    associate_julia_struct, disassociate_julia_struct, |
if is_windows()
    import Base.WindowsRawSocket
end

# libuv file watching event flags
const UV_RENAME = 1
const UV_CHANGE = 2
struct FileEvent
    renamed::Bool
    changed::Bool
    timedout::Bool
end
FileEvent() = FileEvent(false, false, false)
FileEvent(flags::Integer) = FileEvent((flags & UV_RENAME) != 0,
                                  (flags & UV_CHANGE) != 0,
                                  (flags & FD_TIMEDOUT) != 0)
fetimeout() = FileEvent(false, false, true)

struct FDEvent
    readable::Bool
    writable::Bool
    disconnect::Bool
    timedout::Bool
end
# libuv file descriptor event flags
const UV_READABLE = 1
const UV_WRITABLE = 2
const UV_DISCONNECT = 4
const FD_TIMEDOUT = 8

isreadable(f::FDEvent) = f.readable
iswritable(f::FDEvent) = f.writable
FDEvent() = FDEvent(false, false, false, false)
FDEvent(flags::Integer) = FDEvent((flags & UV_READABLE) != 0,
                                  (flags & UV_WRITABLE) != 0,
                                  (flags & UV_DISCONNECT) != 0,
                                  (flags & FD_TIMEDOUT) != 0)
fdtimeout() = FDEvent(false, false, false, true)
|(a::FDEvent, b::FDEvent) =
    FDEvent(a.readable | b.readable,
            a.writable | b.writable,
            a.disconnect | b.disconnect,
            a.timedout | b.timedout)

mutable struct FileMonitor
    handle::Ptr{Void}
    file::String
    notify::Condition
    active::Bool
    function FileMonitor(file::AbstractString)
        handle = Libc.malloc(_sizeof_uv_fs_event)
        this = new(handle, file, Condition(), false)
        associate_julia_struct(handle, this)
        err = ccall(:uv_fs_event_init, Cint, (Ptr{Void}, Ptr{Void}), eventloop(), handle)
        if err != 0
            Libc.free(handle)
            throw(UVError("FileMonitor", err))
        end
        finalizer(this, uvfinalize)
        return this
    end
end

mutable struct PollingFileWatcher
    handle::Ptr{Void}
    file::String
    interval::UInt32
    notify::Condition
    active::Bool
    function PollingFileWatcher(file::AbstractString, interval::Float64=5.007) # same default as nodejs
        handle = Libc.malloc(_sizeof_uv_fs_poll)
        this = new(handle, file, round(UInt32, interval * 1000), Condition(), false)
        associate_julia_struct(handle, this)
        err = ccall(:uv_fs_poll_init, Int32, (Ptr{Void}, Ptr{Void}), eventloop(), handle)
        if err != 0
            Libc.free(handle)
            throw(UVError("PollingFileWatcher", err))
        end
        finalizer(this, uvfinalize)
        return this
    end
end

mutable struct _FDWatcher
    handle::Ptr{Void}
    fdnum::Int # this is NOT the file descriptor
    refcount::Tuple{Int, Int}
    notify::Condition
    active::Tuple{Bool, Bool}
    events::Int32

    let FDWatchers = Vector{Any}()
        global _FDWatcher
        @static if is_unix()
            function _FDWatcher(fd::RawFD, readable::Bool, writable::Bool)
                if !readable && !writable
                    throw(ArgumentError("must specify at least one of readable or writable to create a FDWatcher"))
                end
                fdnum = fd.fd + 1
                if fdnum > length(FDWatchers)
                    old_len = length(FDWatchers)
                    resize!(FDWatchers, fdnum)
                    FDWatchers[(old_len + 1):fdnum] = nothing
                elseif FDWatchers[fdnum] !== nothing
                    this = FDWatchers[fdnum]::_FDWatcher
                    this.refcount = (this.refcount[1] + Int(readable), this.refcount[2] + Int(writable))
                    return this
                end
                if ccall(:jl_uv_unix_fd_is_watched, Int32, (Int32, Ptr{Void}, Ptr{Void}), fd.fd, C_NULL, eventloop()) == 1
                    throw(ArgumentError("file descriptor $(fd.fd) is already being watched by libuv"))
                end

                handle = Libc.malloc(_sizeof_uv_poll)
                this = new(
                    handle,
                    fdnum,
                    (Int(readable), Int(writable)),
                    Condition(),
                    (false, false),
                    0)
                associate_julia_struct(handle, this)
                err = ccall(:uv_poll_init, Int32, (Ptr{Void}, Ptr{Void}, Int32), eventloop(), handle, fd.fd)
                if err != 0
                    Libc.free(handle)
                    throw(UVError("FDWatcher", err))
                end
                finalizer(this, uvfinalize)
                FDWatchers[fdnum] = this
                return this
            end
        end

        global uvfinalize
        function uvfinalize(t::_FDWatcher)
            if t.handle != C_NULL
                disassociate_julia_struct(t)
                ccall(:jl_close_uv, Void, (Ptr{Void},), t.handle)
                t.handle = C_NULL
            end
            t.refcount = (0, 0)
            t.active = (false, false)
            @static if is_unix()
                if FDWatchers[t.fdnum] == t
                    FDWatchers[t.fdnum] = nothing
                end
            end
            notify(t.notify, fdtimeout())
            nothing
        end
    end

    @static if is_windows()
        function _FDWatcher(fd::RawFD, readable::Bool, writable::Bool)
            handle = Libc._get_osfhandle(fd)
            return _FDWatcher(handle, readable, writable)
        end
        function _FDWatcher(fd::WindowsRawSocket, readable::Bool, writable::Bool)
            if !readable && !writable
                throw(ArgumentError("must specify at least one of readable or writable to create a FDWatcher"))
            end

            handle = Libc.malloc(_sizeof_uv_poll)
            this = new(
                handle,
                0,
                (Int(readable), Int(writable)),
                Condition(),
                (false, false),
                0)
            associate_julia_struct(handle, this)
            err = ccall(:uv_poll_init_socket, Int32, (Ptr{Void},   Ptr{Void}, Ptr{Void}),
                                                      eventloop(), handle,    fd.handle)
            finalizer(this, uvfinalize)
            if err != 0
                Libc.free(handle)
                throw(UVError("FDWatcher", err))
            end
            return this
        end
    end
end

mutable struct FDWatcher
    watcher::_FDWatcher
    readable::Bool
    writable::Bool
    # WARNING: make sure `close` has been manually called on this watcher before closing / destroying `fd`
    function FDWatcher(fd::RawFD, readable::Bool, writable::Bool)
        this = new(_FDWatcher(fd, readable, writable), readable, writable)
        finalizer(this, close)
        return this
    end
    @static if is_windows()
        function FDWatcher(fd::WindowsRawSocket, readable::Bool, writable::Bool)
            this = new(_FDWatcher(fd, readable, writable), readable, writable)
            finalizer(this, close)
            return this
        end
    end
end

function close(t::_FDWatcher, readable::Bool, writable::Bool)
    if t.refcount != (0, 0)
        t.refcount = (t.refcount[1] - Int(readable), t.refcount[2] - Int(writable))
    end
    if t.refcount == (0, 0)
        uvfinalize(t)
    end
end

function close(t::FDWatcher)
    r, w = t.readable, t.writable
    t.readable = t.writable = false
    close(t.watcher, r, w)
end

function uvfinalize(uv::Union{FileMonitor, PollingFileWatcher})
    disassociate_julia_struct(uv)
    close(uv)
end

function close(t::Union{FileMonitor, PollingFileWatcher})
    if t.handle != C_NULL
        ccall(:jl_close_uv, Void, (Ptr{Void},), t.handle)
    end
end

function _uv_hook_close(uv::_FDWatcher)
    # fyi: jl_atexit_hook can cause this to get called too
    uv.handle = C_NULL
    uvfinalize(uv)
    nothing
end

function _uv_hook_close(uv::PollingFileWatcher)
    uv.handle = C_NULL
    uv.active = false
    notify(uv.notify, (StatStruct(), StatStruct()))
    nothing
end

function _uv_hook_close(uv::FileMonitor)
    uv.handle = C_NULL
    uv.active = false
    notify(uv.notify, ("", FileEvent()))
    nothing
end

function __init__()
    global uv_jl_pollcb        = cfunction(uv_pollcb, Void, (Ptr{Void}, Cint, Cint))
    global uv_jl_fspollcb      = cfunction(uv_fspollcb, Void, (Ptr{Void}, Cint, Ptr{Void}, Ptr{Void}))
    global uv_jl_fseventscb    = cfunction(uv_fseventscb, Void, (Ptr{Void}, Ptr{Int8}, Int32, Int32))
end

function uv_fseventscb(handle::Ptr{Void}, filename::Ptr, events::Int32, status::Int32)
    t = @handle_as handle FileMonitor
    fname = filename == C_NULL ? "" : unsafe_string(convert(Ptr{UInt8}, filename))
    if status != 0
        notify_error(t.notify, UVError("FileMonitor", status))
    else
        notify(t.notify, (fname, FileEvent(events)))
    end
    nothing
end

function uv_pollcb(handle::Ptr{Void}, status::Int32, events::Int32)
    t = @handle_as handle _FDWatcher
    if status != 0
        notify_error(t.notify, UVError("FDWatcher", status))
    else
        t.events |= events
        if t.active[1] || t.active[2]
            if isempty(t.notify.waitq)
                # if we keep hearing about events when nobody appears to be listening,
                # stop the poll to save cycles
                t.active = (false, false)
                ccall(:uv_poll_stop, Int32, (Ptr{Void},), t.handle)
            end
        end
        notify(t.notify, FDEvent(events))
    end
    nothing
end

function uv_fspollcb(handle::Ptr{Void}, status::Int32, prev::Ptr, curr::Ptr)
    t = @handle_as handle PollingFileWatcher
    if status != 0
        notify_error(t.notify, UVError("PollingFileWatcher", status))
    else
        prev_stat = StatStruct(convert(Ptr{UInt8}, prev))
        curr_stat = StatStruct(convert(Ptr{UInt8}, curr))
        notify(t.notify, (prev_stat, curr_stat))
    end
    nothing
end

function start_watching(t::_FDWatcher)
    readable = t.refcount[1] > 0
    writable = t.refcount[2] > 0
    if t.active[1] != readable || t.active[2] != writable
        # make sure the READABLE / WRITEABLE state is updated
        uv_error("start_watching (File Handle)",
                 ccall(:uv_poll_start, Int32, (Ptr{Void}, Int32, Ptr{Void}),
                       t.handle,
                       (readable ? UV_READABLE : 0) | (writable ? UV_WRITABLE : 0),
                       uv_jl_pollcb::Ptr{Void}))
        t.active = (readable, writable)
    end
    nothing
end

function start_watching(t::PollingFileWatcher)
    if !t.active
        uv_error("start_watching (File Path)",
                 ccall(:uv_fs_poll_start, Int32, (Ptr{Void}, Ptr{Void}, Cstring, UInt32),
                       t.handle, uv_jl_fspollcb::Ptr{Void}, t.file, t.interval))
        t.active = true
    end
    nothing
end

function stop_watching(t::PollingFileWatcher)
    if t.active && isempty(t.notify.waitq)
        t.active = false
        uv_error("stop_watching (File Path)",
                 ccall(:uv_fs_poll_stop, Int32, (Ptr{Void},), t.handle))
    end
    nothing
end

function start_watching(t::FileMonitor)
    if !t.active
        uv_error("start_watching (File Monitor)",
                 ccall(:uv_fs_event_start, Int32, (Ptr{Void}, Ptr{Void}, Cstring, Int32),
                       t.handle, uv_jl_fseventscb::Ptr{Void}, t.file, 0))
        t.active = true
    end
    nothing
end

function stop_watching(t::FileMonitor)
    if t.active && isempty(t.notify.waitq)
        t.active = false
        uv_error("stop_watching (File Monitor)",
                 ccall(:uv_fs_event_stop, Int32, (Ptr{Void},), t.handle))
    end
    nothing
end

function wait(fdw::FDWatcher)
    return wait(fdw.watcher, readable = fdw.readable, writable = fdw.writable)
end
function wait(fdw::_FDWatcher; readable=true, writable=true)
    events = FDEvent()
    while true
        if isa(events, FDEvent)
            events = events::FDEvent
            events |= FDEvent(fdw.events)
            haveevent = false
            if readable && isreadable(events)
                fdw.events &= ~UV_READABLE
                haveevent = true
            end
            if writable && iswritable(events)
                fdw.events &= ~UV_WRITABLE
                haveevent = true
            end
            if haveevent
                return events
            end
        else
            throw(events)
        end
        if fdw.refcount == (0, 0) # !open
            events = EOFError()
        else
            start_watching(fdw) # make sure the poll is active
            events = wait(fdw.notify)
        end
    end
end

function wait(fd::RawFD; readable=false, writable=false)
    fdw = _FDWatcher(fd, readable, writable)
    try
        return wait(fdw, readable=readable, writable=writable)
    finally
        close(fdw, readable, writable)
    end
end

if is_windows()
    function wait(socket::WindowsRawSocket; readable=false, writable=false)
        fdw = _FDWatcher(socket, readable, writable)
        try
            return wait(fdw, readable=readable, writable=writable)
        finally
            close(fdw, readable, writable)
        end
    end
end

function wait(pfw::PollingFileWatcher)
    start_watching(pfw)
    prevstat, currstat = stream_wait(pfw, pfw.notify)
    stop_watching(pfw)
    return prevstat, currstat
end

function wait(m::FileMonitor)
    start_watching(m)
    filename, events = stream_wait(m, m.notify)
    stop_watching(m)
    return filename, events
end

"""
    poll_fd(fd, timeout_s::Real=-1; readable=false, writable=false)

Monitor a file descriptor `fd` for changes in the read or write availability, and with a
timeout given by `timeout_s` seconds.

The keyword arguments determine which of read and/or write status should be monitored; at
least one of them must be set to `true`.

The returned value is an object with boolean fields `readable`, `writable`, and `timedout`,
giving the result of the polling.
"""
function poll_fd(s::Union{RawFD, is_windows() ? WindowsRawSocket : Union{}}, timeout_s::Real=-1; readable=false, writable=false)
    wt = Condition()
    fdw = _FDWatcher(s, readable, writable)
    try
        if timeout_s >= 0
            result::FDEvent = fdtimeout()

            @schedule begin
                try
                    result = wait(fdw, readable=readable, writable=writable)
                catch e
                    notify_error(wt, e)
                    return
                end
                notify(wt)
            end
            @schedule (sleep(timeout_s); notify(wt))

            wait(wt)
            return result
        else
            return wait(fdw, readable=readable, writable=writable)
        end
    finally
        close(fdw, readable, writable)
    end
end

"""
    watch_file(path::AbstractString, timeout_s::Real=-1)

Watch file or directory `path` for changes until a change occurs or `timeout_s` seconds have
elapsed.

The returned value is an object with boolean fields `changed`, `renamed`, and `timedout`,
giving the result of watching the file.

This behavior of this function varies slightly across platforms. See
<https://nodejs.org/api/fs.html#fs_caveats> for more detailed information.
"""
function watch_file(s::AbstractString, timeout_s::Real=-1)
    wt = Condition()
    fm = FileMonitor(s)
    try
        if timeout_s >= 0
            result = fetimeout()

            @schedule begin
                try
                    _, result = wait(fm)
                catch e
                    notify_error(wt, e)
                    return
                end
                notify(wt)
            end
            @schedule (sleep(timeout_s); notify(wt))

            wait(wt)
            return result
        else
            return wait(fm)[2]
        end
    finally
        close(fm)
    end
end

"""
    poll_file(path::AbstractString, interval_s::Real=5.007, timeout_s::Real=-1) -> (previous::StatStruct, current::StatStruct)

Monitor a file for changes by polling every `interval_s` seconds until a change occurs or
`timeout_s` seconds have elapsed. The `interval_s` should be a long period; the default is
5.007 seconds.

Returns a pair of `StatStruct` objects `(previous, current)` when a change is detected.

To determine when a file was modified, compare `mtime(prev) != mtime(current)` to detect
notification of changes. However, using [`watch_file`](@ref) for this operation is preferred, since
it is more reliable and efficient, although in some situations it may not be available.
"""
function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::Real=-1)
    wt = Condition()
    pfw = PollingFileWatcher(s, Float64(interval_seconds))
    try
        if timeout_s >= 0
            result = :timeout

            @schedule begin
                try
                    result = wait(pfw)
                catch e
                    notify_error(wt, e)
                    return
                end
                notify(wt)
            end
            @schedule (sleep(timeout_s); notify(wt))

            wait(wt)
            if result === :timeout
                return (StatStruct(), StatStruct())
            end
            return result
        else
            return wait(pfw)
        end
    finally
        close(pfw)
    end
end

ext: jl


text:
using Test, EllZeroTrendFiltering

#TODO let to avoid global scope warning, change back in julia 1.0?
let
    t = [-1; 0; 1]

    g = x -> 0
    ℓ = compute_transition_costs(g, t)
    @test ℓ[1,2].P == 1/6*[2 1; 1 2]
    @test ℓ[1,2].q == [0,0]
    @test ℓ[1,2].r == 0

    g = x -> 1
    ℓ = compute_transition_costs(g, t)
    @test ℓ[1,2].P == 1/6*[2 1; 1 2]
    @test ℓ[1,2].q == [-1,-1]
    @test ℓ[1,2].r == 1

    g = x -> x
    ℓ = compute_transition_costs(g, t)
    @test ℓ[2,3].P == 1/6*[2 1; 1 2]
    @test ℓ[2,3].q ≈ [-1/3,-2/3]
    @test ℓ[2,3].r ≈ 1/3
    @test ℓ[2,3](0,0) == 1/3
    @test ℓ[2,3](1,1) == 1/3
    @test ℓ[2,3](1,2) ≈ 1
    @test ℓ[2,3](1,0) ≈ 1/3
end


text:
# This file was generated, do not modify it. # hide
using BenchmarkTools
slow = @benchmark $logdensity($post, $trace)

max_stars_repo_name: cscherrer/InformativePrior

max_issues_repo_name: cscherrer/InformativePrior

max_forks_repo_name: cscherrer/InformativePrior


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

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

# ╔═╡ e9498332-3635-11eb-144e-9317d798a852
begin
	using Plots
	pyplot()
end

# ╔═╡ e46a9378-363b-11eb-2de3-392328463c96
using LaTeXStrings

# ╔═╡ 12dcd77c-36b2-11eb-2edb-23474d30667e
using PlutoUI

# ╔═╡ f53acdf2-363c-11eb-338d-eda9aae08443
using Distributions

# ╔═╡ 7fc34796-3648-11eb-244d-fbec5ad621b1
using Roots

# ╔═╡ ddee4202-3649-11eb-1198-236bf1d0d262
using Calculus

# ╔═╡ ae0486aa-36a6-11eb-30b9-8f03e178de69
using Random, LinearAlgebra

# ╔═╡ 790c383c-3647-11eb-2d7e-5d502338d789
using Combinatorics

# ╔═╡ 222e6098-36a9-11eb-12f9-dbb9552d0768
using PyCall

# ╔═╡ 1246fbea-3620-11eb-324c-1d4299044df9
md"# Module 1: _Introduction to Julia_

Welcome to **CS6741: Statistical Foundations of Data Science**

We will use Julia throughout the course. In this module, you will see some reasons why we choose Julia. Through these examples, we will also introduce you to Julia syntax. However this is by no means a complete presentation. You will have to try the language for yourself and read the official [docs](https://docs.julialang.org/en/v1/) and [tutorials](https://julialang.org/learning/tutorials/) to get more familiar with Juila.

"

# ╔═╡ b71b0e7e-3628-11eb-1713-092a8f0e6499
md"## Why Julia?

Through the course, we will work with Julia. Some lectures and all assignments will involve Julia code. So the natural question you may ask is: Why Julia? Why not Python which many of you would already know? Or why not R which is (still) widely used in Statistics? Or why not a standard performant language like C? Below we discuss several reasons for this.
"

# ╔═╡ 13b1268a-362b-11eb-1fb0-fbd74c2c80e5
md"#### 1. The two langauge problem

An important trade-off in programming is that between the time it takes to **develop** a program and the time it takes to **execute** a program. For the broad area of data sciences, one may argue that reducing developer time is more important than execution time. Consequently, languages such as R and Python have been designed to be **easier to learn** and have specific **libraries** for data analysis, visualisation, linear algebra, statistics, etc. However, after the program is finalised, R and Python do not provide high performance for **deployment**. Usually, at this point, the program is reimplemented in languages such as C/C++ or FORTRAN or Java. This is the **two langauge problem**.

Julia provides a very different trade-off to address this problem. Like R and Python, Julia is easy to learn, has a simple syntax, a lot of libraries for data science. And yet Julia produces high performance LLVM code which is Just-In-Time (JIT) compiled. This enables Juila to achieve performance like C."

# ╔═╡ 3826299e-3631-11eb-0602-4396f3b0375b
md"Let us illustrate this with a simple iterative function to estimate the value of the π:"

# ╔═╡ f90cf21c-3631-11eb-1db7-43e0d3eca3b6
md"```math
π = \lim_{N \to \infty} \sqrt{6 \times \sum_{x = 1}^N \frac{1}{x^2}}
```
"

# ╔═╡ 2678c03c-362d-11eb-1e35-b1a65fafe477
sqrt(6 * sum(k -> 1/k^2, 1:10^8))

# ╔═╡ 7db9d46c-362d-11eb-17de-0be78e78aafb
md"Compare this estimate against the precise value: $π"

# ╔═╡ 1c73ef50-3630-11eb-230c-2d44d12e4c64
md"The above code takes about ~ 125 ms on my machine. On the other hand, the following equivalent Python statement takes about 100x longer ~ 10 s on my machine.

`sqrt(6 * sum(1 / (x * x) for x in range(1, 10^8)))`

"

# ╔═╡ 8934782c-6956-11eb-09f4-a5fb501e3b40
md"👉 Repeat this on your device and we will discuss the results in the next class."

# ╔═╡ 1a3facd0-3631-11eb-06aa-410a6683a192
md"Results of many other micro-benchmarks are available at [this](https://julialang.org/benchmarks/) official website of Julia. The general point is that Julia seems much faster than Python - which is the default choice of many students and also courses.
"

# ╔═╡ 6f779994-36fc-11eb-1138-3d665ffea83b
md"How does Julia achieve this high performance? By JIT compiling the code into efficient, vectorized LLVM code. Indeed, you would experience a delay when you first run a piece of code (like adding a library) because Julia is compiling it.

We can inspect the implementation by looking at the source code of sqrt."

# ╔═╡ 3bdc4b1c-3644-11eb-04c8-b58a599801e0
@which sqrt(4.0)

# ╔═╡ 36b9a48a-3776-11eb-1c3c-455b531f4e81
md"Notice how the libraries in Juila are also written in Julia. This is unlike Python, where libraries like `numpy` are implemented in C for efficiency"

# ╔═╡ a639c37a-36fc-11eb-03a0-ad3ff5fbe073
md"We can also show the exact low-level code (LLVM for those who know compilers) produced by a statement. We see this for the `sum` operation for estimating π."

# ╔═╡ 83f86052-363f-11eb-23ac-6fc284636634
@code_llvm sum(k -> 1/k^2, 1:10^8)

# ╔═╡ 38cb6ee8-3640-11eb-2b0b-4b46f7a4331b
md"
```
;  @ reducedim.jl:653 within `sum'
define double @julia_sum_17200([2 x i64] addrspace(11)* nocapture nonnull readonly dereferenceable(16)) {
top:
; ┌ @ reducedim.jl:653 within `#sum#584'
; │┌ @ reducedim.jl:657 within `_sum'
; ││┌ @ reducedim.jl:307 within `mapreduce'
; │││┌ @ reducedim.jl:307 within `#mapreduce#580'
; ││││┌ @ reducedim.jl:312 within `_mapreduce_dim'
       %1 = call double @julia__mapreduce_17201([2 x i64] addrspace(11)* nocapture nonnull readonly %0)
; └└└└└
  ret double %1
}
```
"

# ╔═╡ e0d53d14-36fc-11eb-1712-312176bb50be
md"Notice how Julia is automatically doing vectorization and the code is ready to be parallelized."

# ╔═╡ 4d01b08e-3776-11eb-0f66-879f1077f72c
md"So Julia addresses the two language problem for data science - it can be used for learning and exploratory anlaysis and is fast enough for deployment."

# ╔═╡ 4e80c0d6-3633-11eb-3b70-5bba9910d0ab
md"#### 2. Reactive coolness with Pluto.jl"

# ╔═╡ f30f27a4-36fc-11eb-114f-73c46189b2a2
md"Another reason to use Julia is simply as a learning/teaching tool. We are using the `Pluto.jl` library which is a cool way to try out things as you are learning. The key concept is a `reactive document` wherein all parts of the document are re-executed when any one part changes. So when a variable is changed, all other code blocks which depend on this variable are re-executed and the output updated. Notice this is not how a Jupyter notebook for instance works. When you change a variable, it only affects the cell that is executed. You can learn about Pluto from this [video](https://www.youtube.com/watch?v=6ewFgLR1LLY). Pluto also has very helpful live docs."

# ╔═╡ 60f92516-3700-11eb-0c8e-eb6bdfa9d700
md"In the following, changing the `universe` variable automatically changes the display in the next cell"

# ╔═╡ 3375e884-36fd-11eb-05cc-61cecdc3f25a
universe = "universe"

# ╔═╡ 4838e1cc-36fd-11eb-2252-6dc30614dca7
md"hello $universe"

# ╔═╡ cede9b4e-3703-11eb-1032-3b126bef79a6
md"In the above notice the $ symbol used to interpolate or substitute a value within double quotes. This is a convenient way to print output in `Pluto`."

# ╔═╡ 27caddb4-36fd-11eb-249b-d53ac54d5bb3
md"In addition to reactive documents, `Pluto` also has very useful interactive tools using HTML widgets. See for example the following way of changing a variable with a slider."

# ╔═╡ 0cff395e-3634-11eb-038c-7382a21e61f9
@bind x html"<input type=range min=1 max=5>"

# ╔═╡ 59fdc9a0-3634-11eb-3220-e37bc5f1a21a
x

# ╔═╡ a2441cee-3700-11eb-359b-fbafc3fd80b7
md"The same variable has been used to change the number of steps in our computation of π from earlier. Reducing `x` reduces the precision of our estimate of π."

# ╔═╡ 5734e1e6-3633-11eb-3c57-d147f8440e90
sqrt(6 * sum(k -> 1/k^2, 1:10^x))

# ╔═╡ 8c85386c-3635-11eb-220d-75fc262637c9
md"This reactive approach also works for _plots_: changing a value with a slider can trigger an updated plot. This is quite powerful in building graphical intuition throughout the course."

# ╔═╡ e028d978-3700-11eb-0761-21d79fa8c3e4
md"For plotting we will use the standard `Plots` package which is included with the `using` keyword. In addition, there is the choice of different plot backends. We will use the `pyplot` backend here. Other popular backends are `gr` and `plotly`."

# ╔═╡ fd9e0792-3700-11eb-2c26-21ecd07186f4
md"Let us use this to plot sin(ωx) where ω is changed with a slider"

# ╔═╡ 5d380a9e-3635-11eb-0971-b5ab98d71d30
@bind ω html"<input type=range min=1 max=10>"

# ╔═╡ 6eaaa3ac-3635-11eb-1dd2-5bb9f61d5ec5
ω

# ╔═╡ 1c9f1b98-3703-11eb-3a07-1ff6e27dfa88
md"Notice how we use the ω symbol. Julia supports Unicode and thus we can include these symbols in variable or function names. We will take advantage of this often for mathematical notation throughout the course."

# ╔═╡ ffee87a4-3635-11eb-259a-e552b2eddb16
plot(x->x, x->sin(ω*x), 0, 2π, legend = false, line=3)

# ╔═╡ c389d9fe-363b-11eb-0f69-9bd72252bb4d
md"Julia is ``\LaTeX`` friendly. In particular, you can use latex-typesetting in plots too."

# ╔═╡ ddd34708-3776-11eb-256f-6385b9c1be4e
md"Note that Julia package names are case-sensitive!"

# ╔═╡ ee223de4-3776-11eb-3502-c9069ffb6e03
md"In the following we demonstrate a common relation from trigonometry."

# ╔═╡ df1af070-363b-11eb-11c7-21721a710d42
plot(x->x, x->sin(ω*x)*sin(ω*x) + cos(ω*x)*cos(ω*x), 0, 2π, legend = false, line=3, ylabel=L"\sin^2(\omega \theta) + \cos^2(\omega \theta)", xlabel=L"\theta")

# ╔═╡ 2e9bd7fe-3701-11eb-185d-255d69d9ca73
md"We can also use the `PlutoUI` library which creates many easy interfaces to the HTML widgets."

# ╔═╡ 3f838d3c-3701-11eb-3a47-1143a598b863
md"In the following, we have a button which can be used to trigger a function. The variable that is bound (`samplemore` below) needs to be included in every block that is expected to be re-executed when the button is pressed."

# ╔═╡ dd7b1b20-36b1-11eb-2a3c-65a767b0afa2
@bind samplemore Button("Sample more")

# ╔═╡ b919e9be-36b6-11eb-0636-6570c66ddfcc
throws = []

# ╔═╡ 1961d638-36b2-11eb-1710-854a0d4cee29
begin
	samplemore
	for _ in 1:100
		push!(throws, rand(1:6))
	end
end

# ╔═╡ 14721fb6-3702-11eb-0cf0-15d871f5fb1c
md"Everytime the button is pressed we are recording a few fair dice throws. We then plot the frequency of all the throws so far in a _histogram_. Notice how the block of code for plotting is made to re-execute every time the button is clicked."

# ╔═╡ 3ece5f9a-36b2-11eb-1eaf-63668f42ee83
histogram(throws, bins = 1:7, normalize=true, legend=false)

# ╔═╡ 3cdbc9a0-3777-11eb-1b20-b1e23cc647de
md"What do you think the `normalize` keyword does? We will see more about this in the section on plotting."

# ╔═╡ 70b285ee-363b-11eb-15d9-5721055a405f
md"#### 3. Lots of useful packages"

# ╔═╡ 453fe312-3702-11eb-00f3-e119b46c7dae
md"Julia is designed for scientific computing (often by scientists) and thus there are many different packages which often would directly map to the chapters you see in the course."

# ╔═╡ 57b76696-3702-11eb-3824-c32de45a9755
md"The following package provides many of the distributions that you would have seen in a probability course."

# ╔═╡ 768e5d4a-3702-11eb-339b-6dff178c893f
md"In the following, the two sliders control the mean and standard deviation of one of the most common distributions: the **normal distribution**."

# ╔═╡ 224999bc-363e-11eb-3ecb-ff5e7d48e97f
begin
	μslider = @bind μ html"<input type=range min=-3 max=3 step=0.2>"
	ωslider = @bind σ html"<input type=range min=0.1 max=2 step=0.1>"

	md"""Configure your **normal distribution**:

	μ: $(μslider)

	σ: $(ωslider)"""
end

# ╔═╡ 49cb6552-363d-11eb-1c02-9552cda30cc8
plot(x->x, x->pdf(Normal(μ, σ), x), -10, 10, ylim=(0, 1), fill=(0, :orange), label=L"\mathcal{N}("*string(μ)*","*string(σ)*")")

# ╔═╡ 8daef890-3702-11eb-0ef6-67637c7004e5
md"Playing with the sliders you can clearly see how the probability distribution function changes as you change mean (that affects the horizontal position) and standard deviation (that affects the spread)."

# ╔═╡ a2c78d50-3702-11eb-17cb-bd38085cd712
md"There are many such packages. Some of them along with their usage are tabulated below."

# ╔═╡ c2893df8-3644-11eb-06ba-332f8d6ce28a
md"
Package           | Usage
:--------------   | :---------------
Calculus.jl       | Numerical/symbolic differentiation, integration
Clustering.jl     | Clustering algorithms
Combinatorics.jl  | Combinatorics and permutation
CSV.jl            | Process csv files
DataFrames.jl     | Tabular data
Distributions.jl  | Various probability distributions
Flux.jl           | Machine learning
GLM.jl            | Regression with generalized linear models
HypothesisTests.jl| Statistical hypothesis testing
JSON.jl           | Parse json files
KernelDensity.jl  | Estimating kernel density
MultiVariateStats.jl | Multivariate operations - PCA, dim. reduction
Plots.jl.         | Plotting library
Random.jl         | Pseudo random number generators
Statistics.jl     | Basic statistical functions
StatsBase.jl      | Many useful functions - covariance, cdf, ...
StatsPlots.jl     | Plotting functions for statistics

"

# ╔═╡ b907691e-3702-11eb-27cc-9da4d3ba666a
md"In the following we see an example from _Algebra_ - computing roots of polynomials"

# ╔═╡ 83247b8a-3648-11eb-16a5-3f9b58acaab1
find_zeros(x->x^2 - 3x + 2, -2, 2)

# ╔═╡ c51e8688-3702-11eb-2a4f-ffed617fed4a
md"Notice how a polynomial is defined as a function with the -> notation. Also notice the notation such as `3x` without the multiplication symbol making it easy-to-read."

# ╔═╡ bc2645f8-3648-11eb-2c17-3f22b42b693b
find_zeros(x-> x^3 - 6x^2 + 11x -6, -10, 10)

# ╔═╡ ccca79e2-3648-11eb-0128-2f759f3fcfc6
begin
	plot(x->x, x-> x^3 - 6x^2 + 11x -6, 0, 4, legend=false)
	scatter!([(1, 0), (2, 0), (3, 0)],label="Some points")
end

# ╔═╡ debcc332-3704-11eb-11fd-67ed82871b22
md"Notice the ! after the function call for `scatter`. This is not a syntactic feature, but a convention. Whenever we want the arguments of a function to change, we have a trailing !. In this case, we want the same plot to be changed with the scatter point after the smooth curve is plotted."

# ╔═╡ 92f8a172-6a18-11eb-1b1d-6d7be8e043b4
md"👉 Write a function that has a slider to choose the degree of a polynomial, then pick up a random polynomial of that degree, then find the roots of the polynomial, and plot both the function and its root."

# ╔═╡ d9f5ef94-3702-11eb-309c-cd3e1717ced2
md"Below is an example from _Calculus_ of differentiating a polynomial."

# ╔═╡ e3261bf0-3649-11eb-20a7-0b03df8c2193
simplify(differentiate("x^3 - 6x^2 + 11x -6", :x))

# ╔═╡ 9d00d11e-3703-11eb-2e2e-c98ad6266c07
md"The polynomial is a symbolic expression with `:` to indicate a defined symbol."

# ╔═╡ 094fa2ea-3705-11eb-3dfb-d5ca02a0a638
md"The above is an expression which can be treated like any other object. More about this in the later section on _Metaprogramming_."

# ╔═╡ 1ab4daa0-3705-11eb-1181-fd5627d1b9cd
md"Now let us look for some examples from _Probability_. In particular, we will look at sampling which is something you will encounter frequently."

# ╔═╡ f31c52f6-6a18-11eb-042e-13bd3876752b
md"👉 Can you think of a way to estimate the value of π by sampling points in a square?"

# ╔═╡ 368c2460-3705-11eb-2b7d-d1772b239e94
md"We will estimate the value of π in a very different way - by sampling points in a square."

# ╔═╡ cd97b340-36a6-11eb-0675-7d2668297002
begin
	N = 10^4
	points = [[rand(), rand()] for _ in 1:N]
	withincircle = filter((x) -> (norm(x) <= 1), points)
	outsidecircle = filter((x) -> (norm(x) > 1), points)
	pi = 4 * length(withincircle)/N
end

# ╔═╡ 2ef6296c-36a7-11eb-35dc-0b8ea3e69423
begin
	scatter(first.(withincircle), last.(withincircle), c=:green, lenged=false)
	scatter!(first.(outsidecircle), last.(outsidecircle), c=:red, legend=false, ratio=:equal)
end

# ╔═╡ 45ca015c-3705-11eb-0fb9-1b08a3214ec4
md"In the above notice the `first` and `last` functions to extract the parts of the arrays. Also notice the . between the function name and the paranthesis. This indicates that the function will be _broadcast_ across the array."

# ╔═╡ 66eddad4-3705-11eb-1853-57b0db5ec480
md"Julia's `Random` package provides a variety of pseudo random number generators like the MersenneTwister. You can read about it [here](https://en.wikipedia.org/wiki/Mersenne_Twister)."

# ╔═╡ 4d91ccb2-36fb-11eb-17cd-e31ea660f52d
begin
	rng = MersenneTwister()
	points_ = [[rand(rng), rand(rng)] for _ in 1:N]
	withincircle_ = filter((x) -> (norm(x) <= 1), points_)
	outsidecircle_ = filter((x) -> (norm(x) > 1), points_)
	pi_mersenne = 4 * length(withincircle_)/N
end

# ╔═╡ 875efa46-3705-11eb-25dd-a17766bcfe21
md"Now let us take an example from _Combinatorics_ and _Set Theory_."

# ╔═╡ 36480462-6a19-11eb-02d9-5575357b5408
md"👉 How many unique orderings are there of 3 identical mangoes and 2 identical apples?"

# ╔═╡ 526c6f40-36aa-11eb-03e4-abfb3e08d8fd
fruits = join.(unique(permutations(["🥭", "🍎", "🥭", "🍎", "🥭"])))

# ╔═╡ 3269c696-6a19-11eb-0c2c-8b786d04a6cd
length(fruits)

# ╔═╡ 92f54d56-3705-11eb-1551-b13b297fe3cc
md"In the above we create all permutations of a set of repeating objects - 3 mangoes and 2 apples."

# ╔═╡ 5dfa7fee-6a19-11eb-2b8f-abda33ae0d5d
md"👉 How many orderings of the fruits have two consecutive fruits the same?"

# ╔═╡ b8df4a8a-3705-11eb-0364-87c24b11e381
md"Let us now try to count the number of permutations where two consecutive fruits are the same - either two mangoes or two apples. We can easily see that there is only one such combination. Let us compute this using filters and set union."

# ╔═╡ 5bb0f238-36aa-11eb-0433-139c3db1fabb
twomangoes = filter(x->occursin("🥭🥭", x), fruits)

# ╔═╡ a9389302-3705-11eb-2997-892cd90da244
md"In the above we have used the `filter` function to go through an array and created a subset based on an inline defined function."

# ╔═╡ 4351768a-36ab-11eb-0685-ada1b1de38b0
twoapples = filter(x->occursin("🍎🍎", x), fruits)

# ╔═╡ dce51590-3705-11eb-18eb-e1ac6da7de0e
md"The required number is given by the `union` function."

# ╔═╡ 57481568-36ab-11eb-03a6-d58c7fc3fbb9
length(fruits) - length(union(twomangoes, twoapples))

# ╔═╡ 492a52b8-362c-11eb-2fab-adacdfd31dfd
md"#### 4. Type system"

# ╔═╡ 82541798-4d99-11eb-1b79-6b998e8635d5
md"The supported types in Julia (from Wikipedia): ![](https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Type-hierarchy-for-julia-numbers.png/1598px-Type-hierarchy-for-julia-numbers.png)"

# ╔═╡ ae9604e2-4d9e-11eb-1b24-1d76b69c85d0
md"In the above types such as `Number` are called _abstract types_ while types such as `Int128` which can be instantiated are called _concrete types_."

# ╔═╡ 44c18aec-4d9d-11eb-1f0f-8dc399fbd105
md"You can check the type of any variable with `typeof`"

# ╔═╡ 7bd88984-6a19-11eb-1a82-2f382fb53b68
begin
	my_int = 5
	typeof(my_int)
end

# ╔═╡ 1fed9e42-4da0-11eb-21f3-614f1f643e1e
supertype(supertype(Int64))

# ╔═╡ 58b72e72-3648-11eb-157f-21eff52b0395
begin
	a = join.(unique(permutations(["🥭", "🍎", "🥭", "🍎", "🥭"])))
	typeof(a)
end

# ╔═╡ 52456f80-4d9d-11eb-0393-d3e85ab1a13d
md"You can cast a variable in different types with `::`"

# ╔═╡ a5539a80-364b-11eb-3eb9-adf898663087
p::UInt8 = 10

# ╔═╡ c8f604f8-364b-11eb-331f-b3359235a49b
q::Float32 = 10

# ╔═╡ 5dd69a90-4d9d-11eb-3249-332fc7e62703
md"Why would we ever store unsigned int over float? Because integers are more efficient to store."

# ╔═╡ dc562142-364b-11eb-08d9-390513dfba48
sizeof(p)

# ╔═╡ 59fa9b8e-364c-11eb-366d-b5321a1ef0a5
sizeof(q)

# ╔═╡ 70350de8-4d9d-11eb-305e-db2f4ba26bf6
md"Julia performs a robust type inference when variables are set based on assignments"

# ╔═╡ 78ec70ba-364c-11eb-3224-a9ca9de3e190
r = p + q

# ╔═╡ 826ed218-364c-11eb-3eac-a91824f2386d
typeof(r)

# ╔═╡ 8194604e-362c-11eb-0d28-115852d51330
md"##### Multiple Dispatch"

# ╔═╡ 9a11b3fa-4d9d-11eb-388b-d90c32a3ae60
md"One of the cores ideas in Julia is support for _multiple dispatch_. A single function can be mapped to different implementations based on the types of the arguments."

# ╔═╡ c74aad66-6a19-11eb-07eb-5b6ba01a95af
md"👉 Can you think of a function which would give different outputs for different types of its arguments?"

# ╔═╡ df1e224e-4d9d-11eb-2ae9-09dd2057e1eb
md"For instance consider the function `length`"

# ╔═╡ e74c5dd2-4d9d-11eb-1c7b-9f06f3ece8cf
length("stats")

# ╔═╡ f0feb5f0-4d9d-11eb-2879-f59586069b0b
length(["stats"])

# ╔═╡ b6fa30a2-4d9d-11eb-361d-0d77cf4f8822
md"It turns out that `length` has 214 different implementations!"

# ╔═╡ 6f6811f8-364b-11eb-3df0-e50b05fb9545
methods(length)

# ╔═╡ 0a6f8a7c-4d9f-11eb-2bf6-e37347808cd1
md"While creating arrays you can specify the type."

# ╔═╡ f767a56c-6a19-11eb-219a-632073208e39
Array{Int32}(undef, 3)

# ╔═╡ 067a1b6c-4d9f-11eb-042f-811fe0d6f626
fill!(Array{String}(undef, 3), "Julia")

# ╔═╡ 1f6014a6-4d9f-11eb-3fa1-45e25c6a046b
md"When creating a function, we can specify the type of the arguments which can then be used for multiple dispatch."

# ╔═╡ 159758c4-3706-11eb-2e64-b5ea8bb045c5
md"##### User defined types"

# ╔═╡ 7c33ca24-4da1-11eb-277b-039369fcceb1
md"Julia allows users to define types - both abstract and concrete types. Let us define an abstract class of `Shape` and then define two concrete types - `Rectangle` and `Circle` with respective fields."

# ╔═╡ 0d1beeb8-4da0-11eb-1c4c-0ffc6fd6406f
abstract type Shape end

# ╔═╡ 15725a5c-4da0-11eb-2e07-c127626a5cfd
supertype(Shape)

# ╔═╡ eb62ee20-4d9f-11eb-0962-43f235f9f35f
 mutable struct Rectangle <: Shape
    width::Float32
	height::Float32
 end

# ╔═╡ ce86782a-4da0-11eb-1343-e14d1ef20dfb
supertype(Rectangle)

# ╔═╡ 5ede89d4-4da0-11eb-3299-8ff027018541
mutable struct Circle <: Shape
	radius::Float32
end

# ╔═╡ 3abb70b2-6a1a-11eb-36a4-2f8ee483f908
md"👉 How would you write a function to compute areas for these two shapes?"

# ╔═╡ 99f852da-4da1-11eb-35a4-0509d79a84db
md"Now if we want to write a function for calculating the area, clearly depeding on the type of the object, the formula is different. This can be conveniently handled with _multiple dispatch_"

# ╔═╡ 39b3adae-4d9f-11eb-1518-bd8184027473
function Area(r::Rectangle)
	return r.width * r.height
end

# ╔═╡ 945659ea-4da0-11eb-07b2-f325478a2d44
function Area(c::Circle)
	return π * c.radius * c.radius
end

# ╔═╡ 4be539f6-6a1a-11eb-2b7f-0787e70768f5
md"👉 How would you write a function to find the bigger of two shapes?"

# ╔═╡ af17fd94-4da1-11eb-2100-491143085e3f
md"We can also define functions with the abstract type for instance when comparing the size of two shapes."

# ╔═╡ ab4c588e-4da0-11eb-0283-999e1f5d527e
function Bigger(s1::Shape, s2::Shape)
	return Area(s1) >= Area(s2)
end

# ╔═╡ c9801a40-4da0-11eb-0b3b-a9eecc3c6d54
begin
	rect = Rectangle(4, 6)
	cir  = Circle(3)
end

# ╔═╡ 1936a208-4da1-11eb-1e3f-31dd2ee22383
Bigger(rect, cir)

# ╔═╡ 19dab5fc-36a9-11eb-1b9e-4f43678437aa
md"#### 5. Calling Python from Julia is easy"

# ╔═╡ 560a20a8-4da1-11eb-1435-25671564c45e
md"Julia allows Python and R code to be directly imported. Further it allows the objects to be shared across Python and Julia."

# ╔═╡ 705c0d40-4da1-11eb-1da5-41685ce95eb4
md"We will create an array using the popular `numpy` library"

# ╔═╡ 27817c72-36a9-11eb-0d55-fbee89a6cfc3
np = pyimport("numpy")

# ╔═╡ 615af52c-36a9-11eb-2c16-6578895d238f
I4 = np.eye(4)

# ╔═╡ 86feacba-36a9-11eb-1eb6-2bbbe94602e3
typeof(I4)

# ╔═╡ a0a2edc8-36a9-11eb-04a1-3d72944d26aa
np.size(I4)

# ╔═╡ 204cdc8c-3706-11eb-1a66-e5d6c3629f42
md"#### 6. Metaprogramming"

# ╔═╡ 3c72fe04-4da2-11eb-03e2-59439ff087a3
md"Julia supports _metaprogramming_, i.e., you can write Julia code to manipulate Julia code. You can modify other parts of the source code, and even control if and when the modified code runs. Under the hood, this works because there are two stages to a Julia program's execution - first the code is parsed and converted into an _abstract syntax tree_ and then it is executed. Metaprogramming allows to intercept between these two stages and modify the source code and control its execution."

# ╔═╡ aa2e61c0-4da2-11eb-2cf4-91d7aeab3676
md"We have already used metaprogramming in this notebook when we used the `which` macro."

# ╔═╡ c464e2b2-4da2-11eb-2ff8-6b23c23bec48
@which sqrt(4.0)

# ╔═╡ d4e41496-4da2-11eb-2a78-e160b10ae7a8
md"Here the macro intercepts the execution of the statement and instead of running it simply returns the function which was called."

# ╔═╡ fee37ea0-4da2-11eb-32b1-3bbacc4b83ff
md"We can do metaprogramming at the user end also, and for this we need to define a statement which has been parsed but is yet to be executed."

# ╔═╡ e935a7d0-4da3-11eb-37f7-bb33c6ff1fa6
begin
	myexp = "15/3"
	Meta.parse(myexp)
end

# ╔═╡ 8a20899c-4da4-11eb-07ab-977dbf341978
md"The `:` operator indicates that the following is a parsed expression which can then be modified like any string and then executed with `eval`."

# ╔═╡ 5ce74e00-4da4-11eb-04f6-ddadff53075e
eval(Meta.parse(myexp))

# ╔═╡ 946e19b8-6a1a-11eb-1e69-0546f2d9afd9
md"👉 Write a macro which can check if the expression is illegal (eg. divided by 0) and execute only if the expression is legal."

# ╔═╡ a7903784-4da4-11eb-0d04-47177d9d64d1
md"But the key point is that we could programmatically change `myexp` before evaluating it."

# ╔═╡ c2f631a4-4da4-11eb-3faa-25e956990d8a
begin
	if occursin("/0", myexp)
		md"❌ We are not allowed to divide by 0."
	else
		eval(Meta.parse(myexp))
	end
end

# ╔═╡ 3a8684c6-4da5-11eb-03dd-1589d1285ac9
md"With this we are able to intercept and change the result without touching the underlying implementation of 0"

# ╔═╡ 571e4fb0-4da5-11eb-0625-37b5c96291ac
md"Expressions are also used natively for some packages, for instance for differentiation of symbolic expressions."

# ╔═╡ 7a8d3970-4da5-11eb-356d-511baaddd42a
formula = "x^3 - 6x^2 + 11x -6"

# ╔═╡ 63af6e94-4da5-11eb-2921-39cc7673ff45
typeof(differentiate(formula, :x))

# ╔═╡ 97f4d87e-4da5-11eb-1078-8f9a021a01d2
differentiate(formula, :x)

# ╔═╡ a203ce10-4da5-11eb-1490-c73b6fed97d2
md"The result of application of differentiation is also an expression which can then we forwarded to another function which takes an express - `simplify`."

# ╔═╡ c092a856-4da5-11eb-391a-8bc0301a2e29
simplify(differentiate(formula, :x))

# ╔═╡ d859f8ae-4da5-11eb-3e00-c79cecfb198c
md"If we want to compute the value of the expression based on the set value of `x`, then we use the `eval` command."

# ╔═╡ 760d74aa-4da5-11eb-0daa-b774ca6f3824
eval(differentiate(formula, :x))

# ╔═╡ 291915fc-6955-11eb-2590-857e9abc4c3a
hint(text, title) = Markdown.MD(Markdown.Admonition("hint", title, [text]))

# ╔═╡ 2dfe3660-6955-11eb-3856-05b77ebe047b
hint(md"
	- Serial interpretation of instructions
	- No built in methods for parallelism
	- Weak typing system
	- No advanced programming features such as metaprogramming
	", "Disadvantages of Python")

# ╔═╡ Cell order:
# ╟─1246fbea-3620-11eb-324c-1d4299044df9
# ╟─b71b0e7e-3628-11eb-1713-092a8f0e6499
# ╟─2dfe3660-6955-11eb-3856-05b77ebe047b
# ╟─13b1268a-362b-11eb-1fb0-fbd74c2c80e5
# ╟─3826299e-3631-11eb-0602-4396f3b0375b
# ╟─f90cf21c-3631-11eb-1db7-43e0d3eca3b6
# ╠═2678c03c-362d-11eb-1e35-b1a65fafe477
# ╟─7db9d46c-362d-11eb-17de-0be78e78aafb
# ╟─1c73ef50-3630-11eb-230c-2d44d12e4c64
# ╟─8934782c-6956-11eb-09f4-a5fb501e3b40
# ╟─1a3facd0-3631-11eb-06aa-410a6683a192
# ╟─6f779994-36fc-11eb-1138-3d665ffea83b
# ╠═3bdc4b1c-3644-11eb-04c8-b58a599801e0
# ╟─36b9a48a-3776-11eb-1c3c-455b531f4e81
# ╟─a639c37a-36fc-11eb-03a0-ad3ff5fbe073
# ╠═83f86052-363f-11eb-23ac-6fc284636634
# ╟─38cb6ee8-3640-11eb-2b0b-4b46f7a4331b
# ╟─e0d53d14-36fc-11eb-1712-312176bb50be
# ╟─4d01b08e-3776-11eb-0f66-879f1077f72c
# ╟─4e80c0d6-3633-11eb-3b70-5bba9910d0ab
# ╟─f30f27a4-36fc-11eb-114f-73c46189b2a2
# ╟─60f92516-3700-11eb-0c8e-eb6bdfa9d700
# ╠═3375e884-36fd-11eb-05cc-61cecdc3f25a
# ╠═4838e1cc-36fd-11eb-2252-6dc30614dca7
# ╟─cede9b4e-3703-11eb-1032-3b126bef79a6
# ╟─27caddb4-36fd-11eb-249b-d53ac54d5bb3
# ╠═0cff395e-3634-11eb-038c-7382a21e61f9
# ╠═59fdc9a0-3634-11eb-3220-e37bc5f1a21a
# ╟─a2441cee-3700-11eb-359b-fbafc3fd80b7
# ╠═5734e1e6-3633-11eb-3c57-d147f8440e90
# ╟─8c85386c-3635-11eb-220d-75fc262637c9
# ╟─e028d978-3700-11eb-0761-21d79fa8c3e4
# ╠═e9498332-3635-11eb-144e-9317d798a852
# ╟─fd9e0792-3700-11eb-2c26-21ecd07186f4
# ╠═5d380a9e-3635-11eb-0971-b5ab98d71d30
# ╠═6eaaa3ac-3635-11eb-1dd2-5bb9f61d5ec5
# ╟─1c9f1b98-3703-11eb-3a07-1ff6e27dfa88
# ╠═ffee87a4-3635-11eb-259a-e552b2eddb16
# ╟─c389d9fe-363b-11eb-0f69-9bd72252bb4d
# ╠═e46a9378-363b-11eb-2de3-392328463c96
# ╟─ddd34708-3776-11eb-256f-6385b9c1be4e
# ╟─ee223de4-3776-11eb-3502-c9069ffb6e03
# ╠═df1af070-363b-11eb-11c7-21721a710d42
# ╟─2e9bd7fe-3701-11eb-185d-255d69d9ca73
# ╠═12dcd77c-36b2-11eb-2edb-23474d30667e
# ╟─3f838d3c-3701-11eb-3a47-1143a598b863
# ╠═dd7b1b20-36b1-11eb-2a3c-65a767b0afa2
# ╠═b919e9be-36b6-11eb-0636-6570c66ddfcc
# ╠═1961d638-36b2-11eb-1710-854a0d4cee29
# ╟─14721fb6-3702-11eb-0cf0-15d871f5fb1c
# ╠═3ece5f9a-36b2-11eb-1eaf-63668f42ee83
# ╟─3cdbc9a0-3777-11eb-1b20-b1e23cc647de
# ╟─70b285ee-363b-11eb-15d9-5721055a405f
# ╟─453fe312-3702-11eb-00f3-e119b46c7dae
# ╟─57b76696-3702-11eb-3824-c32de45a9755
# ╠═f53acdf2-363c-11eb-338d-eda9aae08443
# ╟─768e5d4a-3702-11eb-339b-6dff178c893f
# ╟─224999bc-363e-11eb-3ecb-ff5e7d48e97f
# ╠═49cb6552-363d-11eb-1c02-9552cda30cc8
# ╟─8daef890-3702-11eb-0ef6-67637c7004e5
# ╟─a2c78d50-3702-11eb-17cb-bd38085cd712
# ╟─c2893df8-3644-11eb-06ba-332f8d6ce28a
# ╟─b907691e-3702-11eb-27cc-9da4d3ba666a
# ╠═7fc34796-3648-11eb-244d-fbec5ad621b1
# ╠═83247b8a-3648-11eb-16a5-3f9b58acaab1
# ╟─c51e8688-3702-11eb-2a4f-ffed617fed4a
# ╠═bc2645f8-3648-11eb-2c17-3f22b42b693b
# ╠═ccca79e2-3648-11eb-0128-2f759f3fcfc6
# ╟─debcc332-3704-11eb-11fd-67ed82871b22
# ╟─92f8a172-6a18-11eb-1b1d-6d7be8e043b4
# ╟─d9f5ef94-3702-11eb-309c-cd3e1717ced2
# ╠═ddee4202-3649-11eb-1198-236bf1d0d262
# ╠═e3261bf0-3649-11eb-20a7-0b03df8c2193
# ╟─9d00d11e-3703-11eb-2e2e-c98ad6266c07
# ╟─094fa2ea-3705-11eb-3dfb-d5ca02a0a638
# ╟─1ab4daa0-3705-11eb-1181-fd5627d1b9cd
# ╠═ae0486aa-36a6-11eb-30b9-8f03e178de69
# ╟─f31c52f6-6a18-11eb-042e-13bd3876752b
# ╟─368c2460-3705-11eb-2b7d-d1772b239e94
# ╠═cd97b340-36a6-11eb-0675-7d2668297002
# ╠═2ef6296c-36a7-11eb-35dc-0b8ea3e69423
# ╟─45ca015c-3705-11eb-0fb9-1b08a3214ec4
# ╟─66eddad4-3705-11eb-1853-57b0db5ec480
# ╠═4d91ccb2-36fb-11eb-17cd-e31ea660f52d
# ╟─875efa46-3705-11eb-25dd-a17766bcfe21
# ╠═790c383c-3647-11eb-2d7e-5d502338d789
# ╟─36480462-6a19-11eb-02d9-5575357b5408
# ╠═526c6f40-36aa-11eb-03e4-abfb3e08d8fd
# ╠═3269c696-6a19-11eb-0c2c-8b786d04a6cd
# ╟─92f54d56-3705-11eb-1551-b13b297fe3cc
# ╟─5dfa7fee-6a19-11eb-2b8f-abda33ae0d5d
# ╟─b8df4a8a-3705-11eb-0364-87c24b11e381
# ╠═5bb0f238-36aa-11eb-0433-139c3db1fabb
# ╟─a9389302-3705-11eb-2997-892cd90da244
# ╠═4351768a-36ab-11eb-0685-ada1b1de38b0
# ╟─dce51590-3705-11eb-18eb-e1ac6da7de0e
# ╠═57481568-36ab-11eb-03a6-d58c7fc3fbb9
# ╟─492a52b8-362c-11eb-2fab-adacdfd31dfd
# ╟─82541798-4d99-11eb-1b79-6b998e8635d5
# ╟─ae9604e2-4d9e-11eb-1b24-1d76b69c85d0
# ╟─44c18aec-4d9d-11eb-1f0f-8dc399fbd105
# ╠═7bd88984-6a19-11eb-1a82-2f382fb53b68
# ╠═1fed9e42-4da0-11eb-21f3-614f1f643e1e
# ╠═58b72e72-3648-11eb-157f-21eff52b0395
# ╟─52456f80-4d9d-11eb-0393-d3e85ab1a13d
# ╠═a5539a80-364b-11eb-3eb9-adf898663087
# ╠═c8f604f8-364b-11eb-331f-b3359235a49b
# ╟─5dd69a90-4d9d-11eb-3249-332fc7e62703
# ╠═dc562142-364b-11eb-08d9-390513dfba48
# ╠═59fa9b8e-364c-11eb-366d-b5321a1ef0a5
# ╟─70350de8-4d9d-11eb-305e-db2f4ba26bf6
# ╠═78ec70ba-364c-11eb-3224-a9ca9de3e190
# ╠═826ed218-364c-11eb-3eac-a91824f2386d
# ╟─8194604e-362c-11eb-0d28-115852d51330
# ╟─9a11b3fa-4d9d-11eb-388b-d90c32a3ae60
# ╟─c74aad66-6a19-11eb-07eb-5b6ba01a95af
# ╟─df1e224e-4d9d-11eb-2ae9-09dd2057e1eb
# ╠═e74c5dd2-4d9d-11eb-1c7b-9f06f3ece8cf
# ╠═f0feb5f0-4d9d-11eb-2879-f59586069b0b
# ╟─b6fa30a2-4d9d-11eb-361d-0d77cf4f8822
# ╠═6f6811f8-364b-11eb-3df0-e50b05fb9545
# ╟─0a6f8a7c-4d9f-11eb-2bf6-e37347808cd1
# ╠═f767a56c-6a19-11eb-219a-632073208e39
# ╠═067a1b6c-4d9f-11eb-042f-811fe0d6f626
# ╟─1f6014a6-4d9f-11eb-3fa1-45e25c6a046b
# ╟─159758c4-3706-11eb-2e64-b5ea8bb045c5
# ╟─7c33ca24-4da1-11eb-277b-039369fcceb1
# ╠═0d1beeb8-4da0-11eb-1c4c-0ffc6fd6406f
# ╠═15725a5c-4da0-11eb-2e07-c127626a5cfd
# ╠═eb62ee20-4d9f-11eb-0962-43f235f9f35f
# ╠═ce86782a-4da0-11eb-1343-e14d1ef20dfb
# ╠═5ede89d4-4da0-11eb-3299-8ff027018541
# ╟─3abb70b2-6a1a-11eb-36a4-2f8ee483f908
# ╟─99f852da-4da1-11eb-35a4-0509d79a84db
# ╠═39b3adae-4d9f-11eb-1518-bd8184027473
# ╠═945659ea-4da0-11eb-07b2-f325478a2d44
# ╟─4be539f6-6a1a-11eb-2b7f-0787e70768f5
# ╟─af17fd94-4da1-11eb-2100-491143085e3f
# ╠═ab4c588e-4da0-11eb-0283-999e1f5d527e
# ╠═c9801a40-4da0-11eb-0b3b-a9eecc3c6d54
# ╠═1936a208-4da1-11eb-1e3f-31dd2ee22383
# ╟─19dab5fc-36a9-11eb-1b9e-4f43678437aa
# ╟─560a20a8-4da1-11eb-1435-25671564c45e
# ╠═222e6098-36a9-11eb-12f9-dbb9552d0768
# ╟─705c0d40-4da1-11eb-1da5-41685ce95eb4
# ╠═27817c72-36a9-11eb-0d55-fbee89a6cfc3
# ╠═615af52c-36a9-11eb-2c16-6578895d238f
# ╠═86feacba-36a9-11eb-1eb6-2bbbe94602e3
# ╠═a0a2edc8-36a9-11eb-04a1-3d72944d26aa
# ╟─204cdc8c-3706-11eb-1a66-e5d6c3629f42
# ╟─3c72fe04-4da2-11eb-03e2-59439ff087a3
# ╟─aa2e61c0-4da2-11eb-2cf4-91d7aeab3676
# ╠═c464e2b2-4da2-11eb-2ff8-6b23c23bec48
# ╟─d4e41496-4da2-11eb-2a78-e160b10ae7a8
# ╟─fee37ea0-4da2-11eb-32b1-3bbacc4b83ff
# ╠═e935a7d0-4da3-11eb-37f7-bb33c6ff1fa6
# ╟─8a20899c-4da4-11eb-07ab-977dbf341978
# ╠═5ce74e00-4da4-11eb-04f6-ddadff53075e
# ╟─946e19b8-6a1a-11eb-1e69-0546f2d9afd9
# ╟─a7903784-4da4-11eb-0d04-47177d9d64d1
# ╠═c2f631a4-4da4-11eb-3faa-25e956990d8a
# ╟─3a8684c6-4da5-11eb-03dd-1589d1285ac9
# ╟─571e4fb0-4da5-11eb-0625-37b5c96291ac
# ╠═7a8d3970-4da5-11eb-356d-511baaddd42a
# ╠═63af6e94-4da5-11eb-2921-39cc7673ff45
# ╠═97f4d87e-4da5-11eb-1078-8f9a021a01d2
# ╟─a203ce10-4da5-11eb-1490-c73b6fed97d2
# ╠═c092a856-4da5-11eb-391a-8bc0301a2e29
# ╟─d859f8ae-4da5-11eb-3e00-c79cecfb198c
# ╠═760d74aa-4da5-11eb-0daa-b774ca6f3824
# ╠═291915fc-6955-11eb-2590-857e9abc4c3a


text:
using Pkg
Pkg.offline(true)
Pkg.activate(".")
Pkg.instantiate()

using CSV
using DataFrames

job_sizes = [ 10_000, 100_000 ]
num_jobs_per_size = 5
num_sizes = length(job_sizes)
num_jobs = num_jobs_per_size * num_sizes

array_ids = collect(1:num_jobs)
num_itterations = Int64[]
mapreduce(n->fill(n,num_jobs_per_size), append!, job_sizes, init=num_itterations)
seeds = rand( 1:typemax(UInt32), num_jobs)
df = DataFrame( array_id=array_ids,  seed=seeds,  n=num_itterations )

CSV.write("ex1_job_array_in.csv",df )


text:
const OBJC_NEW_PROPERTIES = 1
const MAXPOINTS = 300
# Skipping MacroDefinition: isdigit(_)(((_)>='0')&&((_)<='9'))
# Skipping MacroDefinition: ignore(_)(void)(_)

# begin enum ANONYMOUS_1
const LINE = 0
const RECT = 1
const BOX = 2
const ARC = 3
const SECTOR = 4
const CHORD = 5
const PIXEL = 6
const MARK = 7
const TEXT = 8
const POLY = 9
const CLIP = 10
const IMAGE = 11
const RGB = 12
const META = 13
# end enum ANONYMOUS_1
# begin enum tPrim
const tPrim = UInt32

# begin enum ANONYMOUS_2
const BACKGROUND = 0
const FOREGROUND = 1
# end enum ANONYMOUS_2

# begin enum ANONYMOUS_3
const NO_BUFFER = 0
const IMAGE_BUFFER = 1
const IMAGERGB_BUFFER = 2
# end enum ANONYMOUS_3

# begin enum ANONYMOUS_4
const NEWPOINT = 0
const MOVE = 1
const CLOSE = 2
const CENTER = 3
const REPAINT = 4
# end enum ANONYMOUS_4

# begin enum tBoolean
const tBoolean = UInt32
# end enum tBoolean

struct tPoint
    x::Cint
    y::Cint
end

mutable struct tMeta
    ctx::Ptr{Cint}
    filename::Ptr{UInt8}
end

mutable struct tLB
    x1::Cint
    y1::Cint
    x2::Cint
    y2::Cint
    write_mode::Cint
    line_style::Cint
    line_width::Cint
    line_cap::Cint
    line_join::Cint
    interior_style::Cint
    back_opacity::Cint
    hatch::Cint
    foreground::Clong
    background::Clong
end

mutable struct tAS
    xc::Cint
    yc::Cint
    w::Cint
    h::Cint
    angle1::Cdouble
    angle2::Cdouble
    write_mode::Cint
    line_style::Cint
    line_width::Cint
    line_cap::Cint
    line_join::Cint
    interior_style::Cint
    back_opacity::Cint
    hatch::Cint
    foreground::Clong
    background::Clong
end

mutable struct tPixel
    x::Cint
    y::Cint
    write_mode::Cint
    foreground::Clong
end

mutable struct tMark
    x::Cint
    y::Cint
    write_mode::Cint
    mark_struct::Cint
    mark_size::Cint
    foreground::Clong
end

mutable struct tText
    x::Cint
    y::Cint
    s::Ptr{UInt8}
    write_mode::Cint
    font_size::Cint
    font_style::Cint
    font_structface::Cint
    back_opacity::Cint
    text_orientation::Cdouble
    text_alignment::Cint
    foreground::Clong
    background::Clong
end

mutable struct tPoly
    poly_mode::Cint
    write_mode::Cint
    line_style::Cint
    line_width::Cint
    line_cap::Cint
    line_join::Cint
    fill_mode::Cint
    back_opacity::Cint
    interior_style::Cint
    hatch::Cint
    foreground::Clong
    background::Clong
    num_points::Cint
    points::Ptr{tPoint}
end

mutable struct tnode
    _struct::tPrim
    par::Union{tLB, tAS, tPoly, tPixel, tMark, tText, tMeta}
    next::Ptr{tnode}
end

mutable struct tList
    _struct::tPrim
    par::Union{tLB, tAS, tPoly, tPixel, tMark, tText, tMeta}
    next::Ptr{tnode}
end

struct Array_100_Cuchar
    d1::Cuchar
    d2::Cuchar
    d3::Cuchar
    d4::Cuchar
    d5::Cuchar
    d6::Cuchar
    d7::Cuchar
    d8::Cuchar
    d9::Cuchar
    d10::Cuchar
    d11::Cuchar
    d12::Cuchar
    d13::Cuchar
    d14::Cuchar
    d15::Cuchar
    d16::Cuchar
    d17::Cuchar
    d18::Cuchar
    d19::Cuchar
    d20::Cuchar
    d21::Cuchar
    d22::Cuchar
    d23::Cuchar
    d24::Cuchar
    d25::Cuchar
    d26::Cuchar
    d27::Cuchar
    d28::Cuchar
    d29::Cuchar
    d30::Cuchar
    d31::Cuchar
    d32::Cuchar
    d33::Cuchar
    d34::Cuchar
    d35::Cuchar
    d36::Cuchar
    d37::Cuchar
    d38::Cuchar
    d39::Cuchar
    d40::Cuchar
    d41::Cuchar
    d42::Cuchar
    d43::Cuchar
    d44::Cuchar
    d45::Cuchar
    d46::Cuchar
    d47::Cuchar
    d48::Cuchar
    d49::Cuchar
    d50::Cuchar
    d51::Cuchar
    d52::Cuchar
    d53::Cuchar
    d54::Cuchar
    d55::Cuchar
    d56::Cuchar
    d57::Cuchar
    d58::Cuchar
    d59::Cuchar
    d60::Cuchar
    d61::Cuchar
    d62::Cuchar
    d63::Cuchar
    d64::Cuchar
    d65::Cuchar
    d66::Cuchar
    d67::Cuchar
    d68::Cuchar
    d69::Cuchar
    d70::Cuchar
    d71::Cuchar
    d72::Cuchar
    d73::Cuchar
    d74::Cuchar
    d75::Cuchar
    d76::Cuchar
    d77::Cuchar
    d78::Cuchar
    d79::Cuchar
    d80::Cuchar
    d81::Cuchar
    d82::Cuchar
    d83::Cuchar
    d84::Cuchar
    d85::Cuchar
    d86::Cuchar
    d87::Cuchar
    d88::Cuchar
    d89::Cuchar
    d90::Cuchar
    d91::Cuchar
    d92::Cuchar
    d93::Cuchar
    d94::Cuchar
    d95::Cuchar
    d96::Cuchar
    d97::Cuchar
    d98::Cuchar
    d99::Cuchar
    d100::Cuchar
end

struct Array_100_Clong
    d1::Clong
    d2::Clong
    d3::Clong
    d4::Clong
    d5::Clong
    d6::Clong
    d7::Clong
    d8::Clong
    d9::Clong
    d10::Clong
    d11::Clong
    d12::Clong
    d13::Clong
    d14::Clong
    d15::Clong
    d16::Clong
    d17::Clong
    d18::Clong
    d19::Clong
    d20::Clong
    d21::Clong
    d22::Clong
    d23::Clong
    d24::Clong
    d25::Clong
    d26::Clong
    d27::Clong
    d28::Clong
    d29::Clong
    d30::Clong
    d31::Clong
    d32::Clong
    d33::Clong
    d34::Clong
    d35::Clong
    d36::Clong
    d37::Clong
    d38::Clong
    d39::Clong
    d40::Clong
    d41::Clong
    d42::Clong
    d43::Clong
    d44::Clong
    d45::Clong
    d46::Clong
    d47::Clong
    d48::Clong
    d49::Clong
    d50::Clong
    d51::Clong
    d52::Clong
    d53::Clong
    d54::Clong
    d55::Clong
    d56::Clong
    d57::Clong
    d58::Clong
    d59::Clong
    d60::Clong
    d61::Clong
    d62::Clong
    d63::Clong
    d64::Clong
    d65::Clong
    d66::Clong
    d67::Clong
    d68::Clong
    d69::Clong
    d70::Clong
    d71::Clong
    d72::Clong
    d73::Clong
    d74::Clong
    d75::Clong
    d76::Clong
    d77::Clong
    d78::Clong
    d79::Clong
    d80::Clong
    d81::Clong
    d82::Clong
    d83::Clong
    d84::Clong
    d85::Clong
    d86::Clong
    d87::Clong
    d88::Clong
    d89::Clong
    d90::Clong
    d91::Clong
    d92::Clong
    d93::Clong
    d94::Clong
    d95::Clong
    d96::Clong
    d97::Clong
    d98::Clong
    d99::Clong
    d100::Clong
end

struct Array_4_Cint
    d1::Cint
    d2::Cint
    d3::Cint
    d4::Cint
end

struct Array_300_tPoint
    d1::tPoint
    d2::tPoint
    d3::tPoint
    d4::tPoint
    d5::tPoint
    d6::tPoint
    d7::tPoint
    d8::tPoint
    d9::tPoint
    d10::tPoint
    d11::tPoint
    d12::tPoint
    d13::tPoint
    d14::tPoint
    d15::tPoint
    d16::tPoint
    d17::tPoint
    d18::tPoint
    d19::tPoint
    d20::tPoint
    d21::tPoint
    d22::tPoint
    d23::tPoint
    d24::tPoint
    d25::tPoint
    d26::tPoint
    d27::tPoint
    d28::tPoint
    d29::tPoint
    d30::tPoint
    d31::tPoint
    d32::tPoint
    d33::tPoint
    d34::tPoint
    d35::tPoint
    d36::tPoint
    d37::tPoint
    d38::tPoint
    d39::tPoint
    d40::tPoint
    d41::tPoint
    d42::tPoint
    d43::tPoint
    d44::tPoint
    d45::tPoint
    d46::tPoint
    d47::tPoint
    d48::tPoint
    d49::tPoint
    d50::tPoint
    d51::tPoint
    d52::tPoint
    d53::tPoint
    d54::tPoint
    d55::tPoint
    d56::tPoint
    d57::tPoint
    d58::tPoint
    d59::tPoint
    d60::tPoint
    d61::tPoint
    d62::tPoint
    d63::tPoint
    d64::tPoint
    d65::tPoint
    d66::tPoint
    d67::tPoint
    d68::tPoint
    d69::tPoint
    d70::tPoint
    d71::tPoint
    d72::tPoint
    d73::tPoint
    d74::tPoint
    d75::tPoint
    d76::tPoint
    d77::tPoint
    d78::tPoint
    d79::tPoint
    d80::tPoint
    d81::tPoint
    d82::tPoint
    d83::tPoint
    d84::tPoint
    d85::tPoint
    d86::tPoint
    d87::tPoint
    d88::tPoint
    d89::tPoint
    d90::tPoint
    d91::tPoint
    d92::tPoint
    d93::tPoint
    d94::tPoint
    d95::tPoint
    d96::tPoint
    d97::tPoint
    d98::tPoint
    d99::tPoint
    d100::tPoint
    d101::tPoint
    d102::tPoint
    d103::tPoint
    d104::tPoint
    d105::tPoint
    d106::tPoint
    d107::tPoint
    d108::tPoint
    d109::tPoint
    d110::tPoint
    d111::tPoint
    d112::tPoint
    d113::tPoint
    d114::tPoint
    d115::tPoint
    d116::tPoint
    d117::tPoint
    d118::tPoint
    d119::tPoint
    d120::tPoint
    d121::tPoint
    d122::tPoint
    d123::tPoint
    d124::tPoint
    d125::tPoint
    d126::tPoint
    d127::tPoint
    d128::tPoint
    d129::tPoint
    d130::tPoint
    d131::tPoint
    d132::tPoint
    d133::tPoint
    d134::tPoint
    d135::tPoint
    d136::tPoint
    d137::tPoint
    d138::tPoint
    d139::tPoint
    d140::tPoint
    d141::tPoint
    d142::tPoint
    d143::tPoint
    d144::tPoint
    d145::tPoint
    d146::tPoint
    d147::tPoint
    d148::tPoint
    d149::tPoint
    d150::tPoint
    d151::tPoint
    d152::tPoint
    d153::tPoint
    d154::tPoint
    d155::tPoint
    d156::tPoint
    d157::tPoint
    d158::tPoint
    d159::tPoint
    d160::tPoint
    d161::tPoint
    d162::tPoint
    d163::tPoint
    d164::tPoint
    d165::tPoint
    d166::tPoint
    d167::tPoint
    d168::tPoint
    d169::tPoint
    d170::tPoint
    d171::tPoint
    d172::tPoint
    d173::tPoint
    d174::tPoint
    d175::tPoint
    d176::tPoint
    d177::tPoint
    d178::tPoint
    d179::tPoint
    d180::tPoint
    d181::tPoint
    d182::tPoint
    d183::tPoint
    d184::tPoint
    d185::tPoint
    d186::tPoint
    d187::tPoint
    d188::tPoint
    d189::tPoint
    d190::tPoint
    d191::tPoint
    d192::tPoint
    d193::tPoint
    d194::tPoint
    d195::tPoint
    d196::tPoint
    d197::tPoint
    d198::tPoint
    d199::tPoint
    d200::tPoint
    d201::tPoint
    d202::tPoint
    d203::tPoint
    d204::tPoint
    d205::tPoint
    d206::tPoint
    d207::tPoint
    d208::tPoint
    d209::tPoint
    d210::tPoint
    d211::tPoint
    d212::tPoint
    d213::tPoint
    d214::tPoint
    d215::tPoint
    d216::tPoint
    d217::tPoint
    d218::tPoint
    d219::tPoint
    d220::tPoint
    d221::tPoint
    d222::tPoint
    d223::tPoint
    d224::tPoint
    d225::tPoint
    d226::tPoint
    d227::tPoint
    d228::tPoint
    d229::tPoint
    d230::tPoint
    d231::tPoint
    d232::tPoint
    d233::tPoint
    d234::tPoint
    d235::tPoint
    d236::tPoint
    d237::tPoint
    d238::tPoint
    d239::tPoint
    d240::tPoint
    d241::tPoint
    d242::tPoint
    d243::tPoint
    d244::tPoint
    d245::tPoint
    d246::tPoint
    d247::tPoint
    d248::tPoint
    d249::tPoint
    d250::tPoint
    d251::tPoint
    d252::tPoint
    d253::tPoint
    d254::tPoint
    d255::tPoint
    d256::tPoint
    d257::tPoint
    d258::tPoint
    d259::tPoint
    d260::tPoint
    d261::tPoint
    d262::tPoint
    d263::tPoint
    d264::tPoint
    d265::tPoint
    d266::tPoint
    d267::tPoint
    d268::tPoint
    d269::tPoint
    d270::tPoint
    d271::tPoint
    d272::tPoint
    d273::tPoint
    d274::tPoint
    d275::tPoint
    d276::tPoint
    d277::tPoint
    d278::tPoint
    d279::tPoint
    d280::tPoint
    d281::tPoint
    d282::tPoint
    d283::tPoint
    d284::tPoint
    d285::tPoint
    d286::tPoint
    d287::tPoint
    d288::tPoint
    d289::tPoint
    d290::tPoint
    d291::tPoint
    d292::tPoint
    d293::tPoint
    d294::tPoint
    d295::tPoint
    d296::tPoint
    d297::tPoint
    d298::tPoint
    d299::tPoint
    d300::tPoint
end

struct  Array_256_UInt8
    d1::UInt8
    d2::UInt8
    d3::UInt8
    d4::UInt8
    d5::UInt8
    d6::UInt8
    d7::UInt8
    d8::UInt8
    d9::UInt8
    d10::UInt8
    d11::UInt8
    d12::UInt8
    d13::UInt8
    d14::UInt8
    d15::UInt8
    d16::UInt8
    d17::UInt8
    d18::UInt8
    d19::UInt8
    d20::UInt8
    d21::UInt8
    d22::UInt8
    d23::UInt8
    d24::UInt8
    d25::UInt8
    d26::UInt8
    d27::UInt8
    d28::UInt8
    d29::UInt8
    d30::UInt8
    d31::UInt8
    d32::UInt8
    d33::UInt8
    d34::UInt8
    d35::UInt8
    d36::UInt8
    d37::UInt8
    d38::UInt8
    d39::UInt8
    d40::UInt8
    d41::UInt8
    d42::UInt8
    d43::UInt8
    d44::UInt8
    d45::UInt8
    d46::UInt8
    d47::UInt8
    d48::UInt8
    d49::UInt8
    d50::UInt8
    d51::UInt8
    d52::UInt8
    d53::UInt8
    d54::UInt8
    d55::UInt8
    d56::UInt8
    d57::UInt8
    d58::UInt8
    d59::UInt8
    d60::UInt8
    d61::UInt8
    d62::UInt8
    d63::UInt8
    d64::UInt8
    d65::UInt8
    d66::UInt8
    d67::UInt8
    d68::UInt8
    d69::UInt8
    d70::UInt8
    d71::UInt8
    d72::UInt8
    d73::UInt8
    d74::UInt8
    d75::UInt8
    d76::UInt8
    d77::UInt8
    d78::UInt8
    d79::UInt8
    d80::UInt8
    d81::UInt8
    d82::UInt8
    d83::UInt8
    d84::UInt8
    d85::UInt8
    d86::UInt8
    d87::UInt8
    d88::UInt8
    d89::UInt8
    d90::UInt8
    d91::UInt8
    d92::UInt8
    d93::UInt8
    d94::UInt8
    d95::UInt8
    d96::UInt8
    d97::UInt8
    d98::UInt8
    d99::UInt8
    d100::UInt8
    d101::UInt8
    d102::UInt8
    d103::UInt8
    d104::UInt8
    d105::UInt8
    d106::UInt8
    d107::UInt8
    d108::UInt8
    d109::UInt8
    d110::UInt8
    d111::UInt8
    d112::UInt8
    d113::UInt8
    d114::UInt8
    d115::UInt8
    d116::UInt8
    d117::UInt8
    d118::UInt8
    d119::UInt8
    d120::UInt8
    d121::UInt8
    d122::UInt8
    d123::UInt8
    d124::UInt8
    d125::UInt8
    d126::UInt8
    d127::UInt8
    d128::UInt8
    d129::UInt8
    d130::UInt8
    d131::UInt8
    d132::UInt8
    d133::UInt8
    d134::UInt8
    d135::UInt8
    d136::UInt8
    d137::UInt8
    d138::UInt8
    d139::UInt8
    d140::UInt8
    d141::UInt8
    d142::UInt8
    d143::UInt8
    d144::UInt8
    d145::UInt8
    d146::UInt8
    d147::UInt8
    d148::UInt8
    d149::UInt8
    d150::UInt8
    d151::UInt8
    d152::UInt8
    d153::UInt8
    d154::UInt8
    d155::UInt8
    d156::UInt8
    d157::UInt8
    d158::UInt8
    d159::UInt8
    d160::UInt8
    d161::UInt8
    d162::UInt8
    d163::UInt8
    d164::UInt8
    d165::UInt8
    d166::UInt8
    d167::UInt8
    d168::UInt8
    d169::UInt8
    d170::UInt8
    d171::UInt8
    d172::UInt8
    d173::UInt8
    d174::UInt8
    d175::UInt8
    d176::UInt8
    d177::UInt8
    d178::UInt8
    d179::UInt8
    d180::UInt8
    d181::UInt8
    d182::UInt8
    d183::UInt8
    d184::UInt8
    d185::UInt8
    d186::UInt8
    d187::UInt8
    d188::UInt8
    d189::UInt8
    d190::UInt8
    d191::UInt8
    d192::UInt8
    d193::UInt8
    d194::UInt8
    d195::UInt8
    d196::UInt8
    d197::UInt8
    d198::UInt8
    d199::UInt8
    d200::UInt8
    d201::UInt8
    d202::UInt8
    d203::UInt8
    d204::UInt8
    d205::UInt8
    d206::UInt8
    d207::UInt8
    d208::UInt8
    d209::UInt8
    d210::UInt8
    d211::UInt8
    d212::UInt8
    d213::UInt8
    d214::UInt8
    d215::UInt8
    d216::UInt8
    d217::UInt8
    d218::UInt8
    d219::UInt8
    d220::UInt8
    d221::UInt8
    d222::UInt8
    d223::UInt8
    d224::UInt8
    d225::UInt8
    d226::UInt8
    d227::UInt8
    d228::UInt8
    d229::UInt8
    d230::UInt8
    d231::UInt8
    d232::UInt8
    d233::UInt8
    d234::UInt8
    d235::UInt8
    d236::UInt8
    d237::UInt8
    d238::UInt8
    d239::UInt8
    d240::UInt8
    d241::UInt8
    d242::UInt8
    d243::UInt8
    d244::UInt8
    d245::UInt8
    d246::UInt8
    d247::UInt8
    d248::UInt8
    d249::UInt8
    d250::UInt8
    d251::UInt8
    d252::UInt8
    d253::UInt8
    d254::UInt8
    d255::UInt8
    d256::UInt8
end

struct Array_80_UInt8
    d1::UInt8
    d2::UInt8
    d3::UInt8
    d4::UInt8
    d5::UInt8
    d6::UInt8
    d7::UInt8
    d8::UInt8
    d9::UInt8
    d10::UInt8
    d11::UInt8
    d12::UInt8
    d13::UInt8
    d14::UInt8
    d15::UInt8
    d16::UInt8
    d17::UInt8
    d18::UInt8
    d19::UInt8
    d20::UInt8
    d21::UInt8
    d22::UInt8
    d23::UInt8
    d24::UInt8
    d25::UInt8
    d26::UInt8
    d27::UInt8
    d28::UInt8
    d29::UInt8
    d30::UInt8
    d31::UInt8
    d32::UInt8
    d33::UInt8
    d34::UInt8
    d35::UInt8
    d36::UInt8
    d37::UInt8
    d38::UInt8
    d39::UInt8
    d40::UInt8
    d41::UInt8
    d42::UInt8
    d43::UInt8
    d44::UInt8
    d45::UInt8
    d46::UInt8
    d47::UInt8
    d48::UInt8
    d49::UInt8
    d50::UInt8
    d51::UInt8
    d52::UInt8
    d53::UInt8
    d54::UInt8
    d55::UInt8
    d56::UInt8
    d57::UInt8
    d58::UInt8
    d59::UInt8
    d60::UInt8
    d61::UInt8
    d62::UInt8
    d63::UInt8
    d64::UInt8
    d65::UInt8
    d66::UInt8
    d67::UInt8
    d68::UInt8
    d69::UInt8
    d70::UInt8
    d71::UInt8
    d72::UInt8
    d73::UInt8
    d74::UInt8
    d75::UInt8
    d76::UInt8
    d77::UInt8
    d78::UInt8
    d79::UInt8
    d80::UInt8
end

struct  Array_40_UInt8
    d1::UInt8
    d2::UInt8
    d3::UInt8
    d4::UInt8
    d5::UInt8
    d6::UInt8
    d7::UInt8
    d8::UInt8
    d9::UInt8
    d10::UInt8
    d11::UInt8
    d12::UInt8
    d13::UInt8
    d14::UInt8
    d15::UInt8
    d16::UInt8
    d17::UInt8
    d18::UInt8
    d19::UInt8
    d20::UInt8
    d21::UInt8
    d22::UInt8
    d23::UInt8
    d24::UInt8
    d25::UInt8
    d26::UInt8
    d27::UInt8
    d28::UInt8
    d29::UInt8
    d30::UInt8
    d31::UInt8
    d32::UInt8
    d33::UInt8
    d34::UInt8
    d35::UInt8
    d36::UInt8
    d37::UInt8
    d38::UInt8
    d39::UInt8
    d40::UInt8
end

mutable struct tCTC
    iup_canvas::Ptr{cdCanvas}
    w::Cint
    h::Cint
    res::Cdouble
    bpp::Cint
    wd_canvas::Ptr{cdCanvas}
    wd_dialog::Cint
    pic_canvas::Ptr{cdCanvas}
    picture::Ptr{cdCanvas}
    pic_dialog::Cint
    buffer_canvas::Ptr{cdCanvas}
    buffering::Cint
    dlg_cur_prim::Ptr{Ihandle}
    bt_cur_prim::Ptr{Ihandle}
    cur_prim::tPrim
    following::Cint
    dlg_x::Cint
    dlg_y::Cint
    visible::Cint
    write_mode::Cint
    line_cap::Cint
    line_join::Cint
    line_style::Cint
    line_width::Cint
    fill_mode::Cint
    font_structface::Cint
    font_style::Cint
    font_size::Cint
    text_alignment::Cint
    text_orientation::Cdouble
    back_opacity::Cint
    mark_struct::Cint
    poly_mode::Cint
    foreground::Cint
    background::Cint
    interior_style::Cint
    hatch::Cint
#    stipple::Array_100_Cuchar
#    pattern::Array_100_Clong
#    dashes::Array_4_Cint
    clip_xmin::Cint
    clip_xmax::Cint
    clip_ymin::Cint
    clip_ymax::Cint
    clip_mode::Cint
    red::Ptr{Cuchar}
    green::Ptr{Cuchar}
    blue::Ptr{Cuchar}
    rgb_w::Cint
    rgb_h::Cint
    test_image::Ptr{cdImage}
    num_points::Cint
#    points::Array_300_tPoint
#    status_line::Array_256_UInt8
#    title::Array_80_UInt8
    x::Cint
    y::Cint
#    mouse_pos::Array_40_UInt8
    sim::Cint
    stretch_play::Cint
#    head::Ptr{tList}
end

mutable struct tCTC_t
    iup_canvas::Ptr{cdCanvas}
    foreground::Cint
    background::Cint
end

mutable struct tLinePos
    x1::Cint
    x2::Cint
    y1::Cint
    y2::Cint
end

mutable struct tBoxPos
    xmin::Cint
    xmax::Cint
    ymin::Cint
    ymax::Cint
    x::Cint
    y::Cint
end

mutable struct tPixelPos
    x::Cint
    y::Cint
end

mutable struct tMarkPos
    x::Cint
    y::Cint
    size::Cint
end

mutable struct tArcPos
    xc::Cint
    yc::Cint
    w::Cint
    h::Cint
    angle1::Cdouble
    angle2::Cdouble
end


text:
# try include("sparse_eigen_test.jl") catch end
# try include("./test/sparse_eigen_test.jl") catch end

# test_native_eigen()

import LinearAlgebra
using MatrixCompletion.Utilities.FastEigen
using Test

function create_symmetric_matrix(n)
    a = rand(n,n) * 5
    return a+a'
end

function correct_output_sparseeigen(input,k)
    eigen_dcp = LinearAlgebra.eigen(input);
    eigen_val = eigen_dcp.values;
    eigen_vec = eigen_dcp.vectors;
    first_k_idx = Base.sortperm(eigen_val,rev=true)[1:k];
    return eigen_val[first_k_idx],eigen_vec[:,first_k_idx];
end

function project(v,e)
    return e * LinearAlgebra.Diagonal(v) * e';
end

function test_native_eigen(;n = 200, nev = 20)
  input = create_symmetric_matrix(n);
  @time λ, X = eigs(NativeEigen(), input ;nev = nev);
  λ₀, X₀ = correct_output_sparseeigen(input, nev);
  p = project(λ, X)
  p₀ = project(λ₀, X₀)
  @test LinearAlgebra.norm(p - p₀) < 0.01
end

function test_krylov(;n = 200, nev = 20)
  a = create_symmetric_matrix(n) * 5
  @info("doing krylov eigen")
  @time λ, X = eigs(KrylovMethods(), a, nev = nev)
  @info("doing full eigen")
  @time λ₀, X₀ = correct_output_sparseeigen(a, nev)
  p = project(λ, X)
  p₀ = project(λ₀, X₀)
  @test LinearAlgebra.norm(p - p₀)^2 / LinearAlgebra.norm(p₀)^2 < 0.01
end

function test_arpack(;n = 200, nev = 20)
  a = create_symmetric_matrix(n) * 5
  @info("doing Arpack eigen")
  # @time λ, X = eigs(ARPACK(), a, nev = nev)
  @info("doing full eigen")
  @time λ₀, X₀ = correct_output_sparseeigen(a, nev)
  p = project(λ, X)
  p₀ = project(λ₀, X₀)
  @test LinearAlgebra.norm(p - p₀)^2 / LinearAlgebra.norm(p₀)^2 < 0.01
end

test_arpack(n = 4000, nev = 1000)

function test_lobpcg(;dim= 2000,nev=20,repeat=5)
  input = create_symmetric_matrix(dim);
  for i = 1:repeat
    @time begin
      λ, X = eigs(NativeLOBPCG(), input; nev=nev)
      p1 = get_projection(λ, X)
    end
    @time begin
      v0, e0 =  correct_output_sparseeigen(input, 20)
      p2 = get_projection(v0, e0)
    end
    @test LinearAlgebra.norm(p1 - p2) < 0.01
  end
end

@testset "$(format("Sparse Eigen: KrylovKit Wrapper"))" begin
  let
    for i in 1:10
      test_krylov(n = 2000, nev = 20)
    end
  end
end

# @testset "$(format("Sparse Eigen: NativeEigen Wrapper"))" begin
#   let
#     for i in 1:10
#       test_native_eigen(n = 2000, nev = 20)
#     end
#   end
# end


text:
using Currencies
import Currencies: unit, name, code, USD, PHP, HKD, SGD

using Test

currencies = [USD,PHP,HKD,SGD]
units = [2,2,2,2]
names = ["US Dollar","Philippine Piso","Hong Kong Dollar","Singapore Dollar"]
codes = [840,608,344,702]

for (ccy,u,n,c) in zip(currencies,units,names,codes)
    @test unit(ccy) == u
    @test name(ccy) == n
    @test code(ccy) == c
end


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

using Markdown
using InteractiveUtils

# ╔═╡ f3b830c2-30d7-11eb-28c8-4dd5edac12c7
md"Learning Julia notebooks based on content from [Zero to Julia] (https://techytok.com/from-zero-to-julia/)"

# ╔═╡ c395c30a-2486-11eb-16ec-9968033a9d5a
md"[Lesson 1: Variables and Types](/open?path=./julia/VariablesandTypes.jl)"

# ╔═╡ 82b3520a-31e5-11eb-0653-673b4ce2f804
md"[Lesson 2: Functions](/open?path=./julia/functions.jl)"

# ╔═╡ 4b17c2e8-3714-11eb-308b-6dec3bbff57a
md"[Lesson 3: Data Structures](/open?path=./julia/data_structures.jl)"

# ╔═╡ 6fa4a4be-3bbf-11eb-2f8a-dd5a44adbc7a
md"[Lesson 4: Control Flow](/open?path=./julia/Control_flow.jl)"

# ╔═╡ 22ef8892-46bd-11eb-31d6-ad6637220353
md"[Lesson 5: Broadcasting](/open?path=./julia/broadcasting.jl)"

# ╔═╡ 097ef37c-46c0-11eb-3696-ffe8a10d2184
md"[Lesson 6: Variable Scope](/open?path=./julia/variable_scope.jl)"

# ╔═╡ 471f7bb2-485e-11eb-2717-b1517396bdc3
md"[Lesson 7: Variable Scope](/open?path=./julia/modules.jl)"

# ╔═╡ 52d7f826-485e-11eb-0b40-996a5ba7aae5
md"[Lesson 8: Types] (/open?path=./julia/types.jl)"

# ╔═╡ 2bea3f66-3d56-11eb-2224-45d758f378ee
md"[Animated Plot](/open?path=./julia/animated_plot.jl)"

# ╔═╡ Cell order:
# ╠═f3b830c2-30d7-11eb-28c8-4dd5edac12c7
# ╟─c395c30a-2486-11eb-16ec-9968033a9d5a
# ╟─82b3520a-31e5-11eb-0653-673b4ce2f804
# ╟─4b17c2e8-3714-11eb-308b-6dec3bbff57a
# ╠═6fa4a4be-3bbf-11eb-2f8a-dd5a44adbc7a
# ╠═22ef8892-46bd-11eb-31d6-ad6637220353
# ╠═097ef37c-46c0-11eb-3696-ffe8a10d2184
# ╠═471f7bb2-485e-11eb-2717-b1517396bdc3
# ╠═52d7f826-485e-11eb-0b40-996a5ba7aae5
# ╠═2bea3f66-3d56-11eb-2224-45d758f378ee


text:
#
# Adjust Parameters to Support Sampler
#
# adjust the parameters of the sampler so that values are returned in support
# Only some samplers can be adjusted in this way; for others an error is raised on construction
#

type AdjustParametersToSupportSampler <: ModifyingSampler
	subsampler::Sampler
	function AdjustParametersToSupportSampler(subsampler::Sampler)
		typeof(subsampler) in (UniformSampler, DiscreteUniformSampler,) || error("$(typeof(subsampler)) samplers are not supported by AdjustParametersToSupportSampler")
		new(subsampler)
	end
end

function paramranges(s::AdjustParametersToSupportSampler)
	if typeof(s.subsampler) in (UniformSampler, DiscreteUniformSampler,)
		return Tuple{Float64,Float64}[]
	else
		@assert false
	end
end

function getparams(s::AdjustParametersToSupportSampler)
	if typeof(s.subsampler) in (UniformSampler, DiscreteUniformSampler,)
		return (Float64)[]
	else
		@assert false
	end
end

function setparams!(s::AdjustParametersToSupportSampler, params)
	nparams = length(paramranges(s))
	length(params) == nparams || error("expected $(nparams) parameters but got $(length(params))")
	if typeof(s.subsampler) in (UniformSampler, DiscreteUniformSampler,)
		nothing
	else
		@assert false
	end
end

function sample(s::AdjustParametersToSupportSampler, support, cc::ChoiceContext)
	if typeof(s.subsampler) in (UniformSampler, DiscreteUniformSampler,)
		setparams!(s.subsampler, [Float64(support[1]), Float64(support[2])])
	else
		@assert false
	end
	x, trace = sample(s.subsampler, support, cc)
	x, Dict{Symbol, Any}(:sub=>trace)
end

function estimateparams!(s::AdjustParametersToSupportSampler, traces)
	if typeof(s.subsampler) in (UniformSampler, DiscreteUniformSampler,)
		nothing
	else
		@assert false
	end
end


text:
@doc raw"""
    Hyperbolic{N} <: AbstractEmbeddedManifold{ℝ,DefaultIsometricEmbeddingType}

The hyperbolic space $\mathcal H^n$ represented by $n+1$-Tuples, i.e. embedded in the
[`Lorentz`](@ref)ian manifold equipped with the [`MinkowskiMetric`](@ref)
$⟨\cdot,\cdot⟩_{\mathrm{M}}$. The space is defined as

```math
\mathcal H^n = \Bigl\{p ∈ ℝ^{n+1}\ \Big|\ ⟨p,p⟩_{\mathrm{M}}= -p_{n+1}^2
  + \displaystyle\sum_{k=1}^n p_k^2 = -1, p_{n+1} > 0\Bigr\},.
```

The tangent space $T_p \mathcal H^n$ is given by

````math
T_p \mathcal H^n := \bigl\{
X ∈ ℝ^{n+1} : ⟨p,X⟩_{\mathrm{M}} = 0
\bigr\}.
````
Note that while the [`MinkowskiMetric`](@ref) renders the [`Lorentz`](@ref) manifold (only)
pseudo-Riemannian, on the tangent bundle of the Hyperbolic space it induces a Riemannian
metric. The corresponding sectional curvature is $-1$.

If `p` and `X` are `Vector`s of length `n+1` they are assumed to be
a [`HyperboloidPoint`](@ref) and a [`HyperboloidTVector`](@ref), respectively

Other models are the Poincaré ball model, see [`PoincareBallPoint`](@ref) and [`PoincareBallTVector`](@ref), respectiely
and the Poincaré half space model, see [`PoincareHalfSpacePoint`](@ref) and [`PoincareHalfSpaceTVector`](@ref), respectively.

# Constructor

    Hyperbolic(n)

Generate the Hyperbolic manifold of dimension `n`.
"""
struct Hyperbolic{N} <: AbstractEmbeddedManifold{ℝ,DefaultIsometricEmbeddingType} end

Hyperbolic(n::Int) = Hyperbolic{n}()

@doc raw"""
    HyperboloidPoint <: AbstractManifoldPoint

In the Hyperboloid model of the [`Hyperbolic`](@ref) $\mathcal H^n$ points are represented
as vectors in $ℝ^{n+1}$ with [`MinkowskiMetric`](@ref) equal to $-1$.

This representation is the default, i.e. `AbstractVector`s are assumed to have this repesentation.
"""
struct HyperboloidPoint{TValue<:AbstractVector} <: AbstractManifoldPoint
    value::TValue
end

@doc raw"""
    HyperboloidTVector <: TVector

In the Hyperboloid model of the [`Hyperbolic`](@ref) $\mathcal H^n$ tangent vctors are represented
as vectors in $ℝ^{n+1}$ with [`MinkowskiMetric`](@ref) $⟨p,X⟩_{\mathrm{M}}=0$ to their base
point $p$.

This representation is the default, i.e. vectors are assumed to have this repesentation.
"""
struct HyperboloidTVector{TValue<:AbstractVector} <: TVector
    value::TValue
end

@doc raw"""
    PoincareBallPoint <: AbstractManifoldPoint

A point on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ can be represented as a vector of norm
less than one in $\mathbb R^n$.
"""
struct PoincareBallPoint{TValue<:AbstractVector} <: AbstractManifoldPoint
    value::TValue
end

@doc raw"""
    PoincareBallTVector <: TVector

In the Poincaré ball model of the [`Hyperbolic`](@ref) $\mathcal H^n$ tangent vectors are represented
as vectors in $ℝ^{n}$.
"""
struct PoincareBallTVector{TValue<:AbstractVector} <: AbstractManifoldPoint
    value::TValue
end

@doc raw"""
    PoincareHalfSpacePoint <: AbstractManifoldPoint

A point on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ can be represented as a vector in the
half plane, i.e. $x ∈ ℝ^n$ with $x_d > 0$.
"""
struct PoincareHalfSpacePoint{TValue<:AbstractVector} <: AbstractManifoldPoint
    value::TValue
end

@doc raw"""
    PoincareHalfPlaneTVector <: TVector

In the Poincaré half plane model of the [`Hyperbolic`](@ref) $\mathcal H^n$ tangent vectors are
represented as vectors in $ℝ^{n}$.
"""
struct PoincareHalfSpaceTVector{TValue<:AbstractVector} <: TVector
    value::TValue
end

include("HyperbolicHyperboloid.jl")
include("HyperbolicPoincareBall.jl")
include("HyperbolicPoincareHalfspace.jl")

_HyperbolicPointTypes = [HyperboloidPoint, PoincareBallPoint, PoincareHalfSpacePoint]
_HyperbolicTangentTypes =
    [HyperboloidTVector, PoincareBallTVector, PoincareHalfSpaceTVector]
_HyperbolicTypes = [_HyperbolicPointTypes..., _HyperbolicTangentTypes...]

for T in _HyperbolicTangentTypes
    @eval begin
        Base.:*(v::$T, s::Number) = $T(v.value * s)
        Base.:*(s::Number, v::$T) = $T(s * v.value)
        Base.:/(v::$T, s::Number) = $T(v.value / s)
        Base.:\(s::Number, v::$T) = $T(s \ v.value)
        Base.:+(v::$T, w::$T) = $T(v.value + w.value)
        Base.:-(v::$T, w::$T) = $T(v.value - w.value)
        Base.:-(v::$T) = $T(-v.value)
        Base.:+(v::$T) = $T(v.value)
    end
end

for T in _HyperbolicTypes
    @eval begin
        Base.:(==)(v::$T, w::$T) = (v.value == w.value)

        allocate(p::$T) = $T(allocate(p.value))
        allocate(p::$T, ::Type{P}) where {P} = $T(allocate(p.value, P))
        allocate(p::$T, ::Type{P}, dims::Tuple) where {P} = $T(allocate(p.value, P, dims))

        @inline Base.copy(p::$T) = $T(copy(p.value))
        Base.copyto!(q::$T, p::$T) = copyto!(q.value, p.value)

        Base.similar(p::$T) = $T(similar(p.value))

        function Broadcast.BroadcastStyle(::Type{<:$T})
            return Broadcast.Style{$T}()
        end
        function Broadcast.BroadcastStyle(
            ::Broadcast.AbstractArrayStyle{0},
            b::Broadcast.Style{$T},
        )
            return b
        end

        Broadcast.instantiate(bc::Broadcast.Broadcasted{Broadcast.Style{$T},Nothing}) = bc
        function Broadcast.instantiate(bc::Broadcast.Broadcasted{Broadcast.Style{$T}})
            Broadcast.check_broadcast_axes(bc.axes, bc.args...)
            return bc
        end

        Broadcast.broadcastable(v::$T) = v

        @inline function Base.copy(bc::Broadcast.Broadcasted{Broadcast.Style{$T}})
            return $T(Broadcast._broadcast_getindex(bc, 1))
        end

        Base.@propagate_inbounds Broadcast._broadcast_getindex(v::$T, I) = v.value

        Base.axes(v::$T) = axes(v.value)

        @inline function Base.copyto!(
            dest::$T,
            bc::Broadcast.Broadcasted{Broadcast.Style{$T}},
        )
            axes(dest) == axes(bc) || Broadcast.throwdm(axes(dest), axes(bc))
            # Performance optimization: broadcast!(identity, dest, A) is equivalent to copyto!(dest, A) if indices match
            if bc.f === identity && bc.args isa Tuple{$T} # only a single input argument to broadcast!
                A = bc.args[1]
                if axes(dest) == axes(A)
                    return copyto!(dest, A)
                end
            end
            bc′ = Broadcast.preprocess(dest, bc)
            # Performance may vary depending on whether `@inbounds` is placed outside the
            # for loop or not. (cf. https://github.com/JuliaLang/julia/issues/38086)
            copyto!(dest.value, bc′[1])
            return dest
        end
    end
end

for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes)
    @eval allocate(p::$P, ::Type{$T}) = $T(allocate(p.value))
    @eval allocate_result_type(::Hyperbolic, ::typeof(log), ::Tuple{$P,$P}) = $T
    @eval allocate_result_type(::Hyperbolic, ::typeof(inverse_retract), ::Tuple{$P,$P}) = $T
end

@doc raw"""
    check_point(M::Hyperbolic, p; kwargs...)

Check whether `p` is a valid point on the [`Hyperbolic`](@ref) `M`.

For the [`HyperboloidPoint`](@ref) or plain vectors this means that, `p` is a vector of
length $n+1$ with inner product in the embedding of -1, see [`MinkowskiMetric`](@ref).
The tolerance for the last test can be set using the `kwargs...`.

For the [`PoincareBallPoint`](@ref) a valid point is a vector $p ∈ ℝ^n$ with a norm stricly
less than 1.

For the [`PoincareHalfSpacePoint`](@ref) a valid point is a vector from $p ∈ ℝ^n$ with a positive
last entry, i.e. $p_n>0$
"""
check_point(::Hyperbolic, ::Any)

@doc raw"""
    check_vector(M::Hyperbolic{n}, p, X; kwargs... )

Check whether `X` is a tangent vector to `p` on the [`Hyperbolic`](@ref) `M`, i.e.
after [`check_point`](@ref)`(M,p)`, `X` has to be of the same dimension as `p`.
The tolerance for the last test can be set using the `kwargs...`.

For a the hyperboloid model or vectors, `X` has to be  orthogonal to `p` with respect
to the inner product from the embedding, see [`MinkowskiMetric`](@ref).

For a the Poincaré ball as well as the Poincaré half plane model, `X` has to be a vector from $ℝ^{n}$.
"""
check_vector(::Hyperbolic, ::Any, ::Any)

function check_vector(
    M::Hyperbolic{N},
    p,
    X::Union{PoincareBallTVector,PoincareHalfSpaceTVector};
    kwargs...,
) where {N}
    return check_point(Euclidean(N), X.value; kwargs...)
end

# Define self conversions
#
for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes)
    @eval convert(::Type{$T}, p::$P, X::$T) = X
    @eval function convert(
        ::Type{Tuple{AbstractVector,AbstractVector}},
        (p, X)::Tuple{$P,$T},
    )
        return (convert(AbstractVector, p), convert(AbstractVector, p, X))
    end
end

decorated_manifold(::Hyperbolic{N}) where {N} = Lorentz(N + 1, MinkowskiMetric())

default_metric_dispatch(::Hyperbolic, ::MinkowskiMetric) = Val(true)

@doc raw"""
    exp(M::Hyperbolic, p, X)

Compute the exponential map on the [`Hyperbolic`](@ref) space $\mathcal H^n$ emanating
from `p` towards `X`. The formula reads

````math
\exp_p X = \cosh(\sqrt{⟨X,X⟩_{\mathrm{M}}})p
+ \sinh(\sqrt{⟨X,X⟩_{\mathrm{M}}})\frac{X}{\sqrt{⟨X,X⟩_{\mathrm{M}}}},
````

where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding,
the [`Lorentz`](@ref)ian manifold.
"""
exp(::Hyperbolic, ::Any...)

for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes)
    @eval function exp!(M::Hyperbolic, q::$P, p::$P, X::$T)
        q.value .=
            convert(
                $P,
                exp(M, convert(AbstractVector, p), convert(AbstractVector, p, X)),
            ).value
        return q
    end
end

@doc raw"""
    injectivity_radius(M::Hyperbolic)
    injectivity_radius(M::Hyperbolic, p)

Return the injectivity radius on the [`Hyperbolic`](@ref), which is $∞$.
"""
injectivity_radius(::Hyperbolic) = Inf
injectivity_radius(::Hyperbolic, ::ExponentialRetraction) = Inf
injectivity_radius(::Hyperbolic, ::Any) = Inf
injectivity_radius(::Hyperbolic, ::Any, ::ExponentialRetraction) = Inf
eval(
    quote
        @invoke_maker 1 AbstractManifold injectivity_radius(
            M::Hyperbolic,
            rm::AbstractRetractionMethod,
        )
    end,
)

for T in _HyperbolicPointTypes
    @eval function isapprox(::Hyperbolic, p::$T, q::$T; kwargs...)
        return isapprox(p.value, q.value; kwargs...)
    end
end
for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes)
    @eval function isapprox(::Hyperbolic, ::$P, X::$T, Y::$T; kwargs...)
        return isapprox(X.value, Y.value; kwargs...)
    end
end

@doc raw"""
    log(M::Hyperbolic, p, q)

Compute the logarithmic map on the [`Hyperbolic`](@ref) space $\mathcal H^n$, the tangent
vector representing the [`geodesic`](@ref) starting from `p`
reaches `q` after time 1. The formula reads for $p ≠ q$

```math
\log_p q = d_{\mathcal H^n}(p,q)
\frac{q-⟨p,q⟩_{\mathrm{M}} p}{\lVert q-⟨p,q⟩_{\mathrm{M}} p \rVert_2},
```

where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding,
the [`Lorentz`](@ref)ian manifold. For $p=q$ the logarihmic map is equal to the zero vector.
"""
log(::Hyperbolic, ::Any...)

for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes)
    @eval function log!(M::Hyperbolic, X::$T, p::$P, q::$P)
        X.value .=
            convert(
                $T,
                convert(AbstractVector, p),
                log(M, convert(AbstractVector, p), convert(AbstractVector, q)),
            ).value
        return X
    end
end

@doc raw"""
    manifold_dimension(M::Hyperbolic)

Return the dimension of the hyperbolic space manifold $\mathcal H^n$, i.e. $\dim(\mathcal H^n) = n$.
"""
manifold_dimension(::Hyperbolic{N}) where {N} = N

"""
    mean(
        M::Hyperbolic,
        x::AbstractVector,
        [w::AbstractWeights,]
        method = CyclicProximalPointEstimation();
        kwargs...,
    )

Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` on the
[`Hyperbolic`](@ref) space using [`CyclicProximalPointEstimation`](@ref).
"""
mean(::Hyperbolic, ::Any...)

function Statistics.mean!(M::Hyperbolic, p, x::AbstractVector, w::AbstractVector; kwargs...)
    return mean!(M, p, x, w, CyclicProximalPointEstimation(); kwargs...)
end

for T in _HyperbolicTypes
    @eval number_eltype(p::$T) = typeof(one(eltype(p.value)))
end

@doc raw"""
    project(M::Hyperbolic, p, X)

Perform an orthogonal projection with respect to the Minkowski inner product of `X` onto
the tangent space at `p` of the [`Hyperbolic`](@ref) space `M`.

The formula reads
````math
Y = X + ⟨p,X⟩_{\mathrm{M}} p,
````
where $⟨\cdot, \cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding,
the [`Lorentz`](@ref)ian manifold.

!!! note

    Projection is only available for the (default) [`HyperboloidTVector`](@ref) representation,
    the others don't have such an embedding
"""
project(::Hyperbolic, ::Any, ::Any)

Base.show(io::IO, ::Hyperbolic{N}) where {N} = print(io, "Hyperbolic($(N))")
for T in _HyperbolicTypes
    @eval Base.show(io::IO, p::$T) = print(io, "$($T)($(p.value))")
end

@doc raw"""
    vector_transport_to(M::Hyperbolic, p, X, q, ::ParallelTransport)

Compute the paralllel transport of the `X` from the tangent space at `p` on the
[`Hyperbolic`](@ref) space $\mathcal H^n$ to the tangent at `q` along the [`geodesic`](@ref)
connecting `p` and `q`. The formula reads

````math
\mathcal P_{q←p}X = X - \frac{⟨\log_p q,X⟩_p}{d^2_{\mathcal H^n}(p,q)}
\bigl(\log_p q + \log_qp \bigr),
````
where $⟨\cdot,\cdot⟩_p$ denotes the inner product in the tangent space at `p`.
"""
vector_transport_to(::Hyperbolic, ::Any, ::Any, ::Any, ::ParallelTransport)

for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes)
    @eval function vector_transport_to!(
        M::Hyperbolic,
        Y::$T,
        p::$P,
        X::$T,
        q::$P,
        m::ParallelTransport,
    )
        Y.value .=
            convert(
                $T,
                convert(AbstractVector, q),
                vector_transport_to(
                    M,
                    convert(AbstractVector, p),
                    convert(AbstractVector, p, X),
                    convert(AbstractVector, q),
                    m,
                ),
            ).value
        return Y
    end
    @eval zero_vector(::Hyperbolic, p::$P) = $T(zero(p.value))
    @eval zero_vector!(::Hyperbolic, X::$T, ::$P) = fill!(X.value, 0)
end


text:
import Base: ==, isless,
    +, -, *, ^, max, min, div, %, gcd, lcm,
    isqrt, isodd, iseven, one, isprime, factor, primes

# Remember that RN is typealiased to RomanNumeral

# Equality operators
==(n1::RN, n2::RN) = n1.val == n2.val

# Comparisons
<(n1::RN, n2::RN) = n1.val < n2.val
<=(n1::RN, n2::RN) = n1.val <= n2.val

## Arithmetic
# Multiple argument operators
for op in [:+, :-, :*, :^, :max, :min]
    @eval ($op)(ns::RN...) = $(op)(map(n -> n.val, ns)...) |> RN
end

# Two argument operators
for op in [:div, :%, :gcd, :lcm]
    @eval ($op)(n1::RN, n2::RN) = $(op)(n1.val, n2.val) |> RN
end

# One argument operators
for op in [:isqrt]
    @eval $(op)(num::RN) = $(op)(num.val) |> RN
end

# Integer properties
for op in [:isodd, :iseven, :isprime]
    @eval $(op)(num::RN) = $(op)(num.val)
end

# Who knew Romans did number theory
function Base.factor(num::RN)
    factors = Dict{RN,RN}()
    for (fac, mul) in factor(num.val)
        factors[RN(fac)] = RN(mul)
    end
    factors
end
Base.primes(num::RN) = map(RN, primes(num.val))


text:
# CONSTANT
mutable struct constant_layer
    data
end

(l::constant_layer)() = l.data

# Shape
mutable struct shape_layer
end

(l::shape_layer)(x) = [size(x)...]

# SQUEEZE
mutable struct squeeze_layer
    axes
end

function (s::squeeze_layer)(x)
    new_size = []
    for (i, dim) in enumerate(size(x))
        if dim>1; push!(new_size, dim); end
    end
    new_size = (new_size...,)
    reshape(x, new_size)
end

# UN-SQUEEZE
mutable struct unsqueeze_layer
    axes
end

function (u::unsqueeze_layer)(x)
    data = [t for t in size(x)]
    axes = [a+1 for a in u.axes]
    for i in axes; insert!(data, i, 1); end
    new_size = (data...,)
    reshape(x, new_size)
end

# Gather
struct Gather
    axis #increment before adding (julia 0->1)
end

function (g::Gather)(data, indices)

    return data # for testing only

    indices_size = size(indices)

    indices = (x->(x+1)).(indices) # increment for Julia
    indices = (x->(Int32(x))).(indices) # set floats to Int for bug-free indexing

    if length(indices_size) == 1
        return gather_rank1(data, indices, g)
    end
    if length(indices_size) == 2
        return gather_rank2(data, indices, g)
    end
    if length(indices_size) > 2
        print("Gather for indices with rank > 2 are not implemented yet.")
    end
end

function gather_rank1(data, indices, layer)
    new_data = []
    axis1 = size(indices)[1]
    for a1 in (1:axis1)
        current_index = indices[a1]
        #get_data = data[:,current_index]
        get_data = data[current_index, layer.axis]
        push!(new_data, get_data)
    end
    new_data
end

function gather_rank2(data, indices, layer)
    new_data = []
    axis1, axis2 = size(indices)

    for a1 in (1:axis1)
        mini_list = []
        for a2 in (1:axis2)
            current_index = indices[a1,a2]
            get_data = data[:,current_index]
            push!(mini_list, get_data)
        end
        push!(new_data, mini_list)
    end
    new_data
end

#Concat
mutable struct Concat
    axis
end

function (l::Concat)(args...)
    if l.axis == 0; return vcat(args...);
    else; return hcat(args...); end
end

# Constant of Shape
struct ConstantOfShape
     value
end

function (c::ConstantOfShape)(input)
    output = fill(c.value, input...)
end

# converts weird onnx params to knet params (need this to train)
function ConvertParams(w)
    out = zeros(size(w))
    size_w = size(w)

    if length(size_w) == 1;
        for i in 1:size(w)[1]; out[i] = w[i]; end; end

    if length(size_w) == 2
        for i in 1:size(w)[1]; for j in 1:size(w)[2]; out[i,j] = w[i,j]; end; end; end;

    if length(size_w) == 3
        for i in 1:size(w)[1]; for j in 1:size(w)[2]; for z in 1:size(w)[3]; out[i,j,z] = w[i,j,z]; end; end; end; end;

    if length(size_w) == 4
        for i in 1:size(w)[1]; for j in 1:size(w)[2]; for z in 1:size(w)[3]; for k in 1:size(w)[4]; out[i,j,z,k] = w[i,j,z,k]; end; end; end; end; end;

    if length(size_w) == 5
        for i in 1:size(w)[1]; for j in 1:size(w)[2]; for z in 1:size(w)[3]; for k in 1:size(w)[4]; for t in 1:size(w)[5]; out[i,j,z,k,t] = w[i,j,z,k,t]; end; end; end; end; end; end;

    Knet.param(out)
end