text:
using Revise
include("model_fit_global.jl")
include("models.jl")
include("helper_functions.jl")

#=  Densities from Zhou
Micropipette	TCR	         CD3de  	CD3ge   	CD3mix
RBC coating	   165.6213223	 406.7805	742.7222634	550.7926
site density   198.7162679	 356.0556	576.7556	415.3837
=#
Ac = 1.0
densities_γT = [576.7556, 198.7162679, Ac]
densities_δT = [356.0556, 198.7162679, Ac]
densities_mixT = [415.3837/2, 198.7162679, 415.3837/2, Ac]
densities_γδ = [576.7556, 356.0556, Ac]
#= bimolecular rates from Zhou
        on rate   off rate    =#
rates_γT = [3.6e-5, 14.1]
rates_δT = [2.3e-5, 11.1]
rates_γδ = [1.6e-4, 14.9]

function solve_model(rates_sds)
    prob = ODEProblem(f,u₀,tspan,rates_sds)
    # prob = remake(prob, p=10 .^ rates)
    sol = solve(prob,Vern7(),saveat=0.01, abs_tol=1e-10,rel_tol=1e-10)
    return sol
end

tspan = (0.0,10.0)
t = collect(0.0:0.1:10)

gr()
# bimolecular TCR-pMHC bonds
u₀ = [0.0]

f = (du,u,p,t) -> bimolecular!(du,u,p,t,densities_γT,[])
γT = solve_model(rates_γT)
plot(γT)

f = (du,u,p,t) -> bimolecular!(du,u,p,t,densities_δT,[])
δT = solve_model(rates_δT)
plot!(δT)

f = (du,u,p,t) -> bimolecular!(du,u,p,t,densities_γδ,[])
γδ = solve_model(rates_γδ)
plot!(γδ)


text:
using JAnki
using REPL.TerminalMenus
using Base: prompt, run

"""

note_selector(tag::String, retag::String, toclip::Bool)

"""
function note_selector(;tags::Array, retag::String, toclip::Bool, strict::Bool)
    if strict == true
        cards = anki_find_note(first(tags))["result"]
    else
        cards = anki_find_notes(tags)["result"]
    end

    if length(cards) == 0
        println("No cards found that match \"$(tags)\". Exiting program")
        return 0
    end

    check_card = anki_get_card([cards[1]])[1]
    field_list = check_card["fields"] |> keys |> collect

    println("Select what fields you wish to query from these cards:")
    selections = MultiSelectMenu(field_list, pagesize = 5) |> request
    selections = field_list[selections.dict |> keys |> collect]
    run(`clear`)

    for (num, card) in enumerate(cards)
        card = anki_get_card([card])[1]
        card_id = card["noteId"]
        field_list = card["fields"] |> keys |> collect

        card_info = ""

        for selection in selections
            if card["fields"][selection]["value"] |> !isempty
                content =
                    "$selection: " * card["fields"][selection]["value"] |>
                    x ->
                        replace(x, r"
|
|||\t|
|
|\n" => "") |>
                        x ->
                            replace(x, r" " => " ") |>
                            x ->
                                replace(x, r"“|”" => "\"") |>
                                x -> replace(x, r"’" => "\'") * "\n"
                println(content)
                card_info = card_info * content
            end
        end
        println("Card $num of $(length(cards))")

        if toclip
            clipboard(card_info)
            anki_retag_note(tags, retag, card_id)
            prompt("When ready for the next note, press ENTER")
            run(`clear`)
        end
    end
end

"""

anki_retag_note(tag::String, retag::String, card_id::String)

"""
function anki_retag_note(tags, retag, card_id)
    println("Would you like to remove $tags and from this card and retag it \"$retag\"?")
    choice = RadioMenu(["Yes", "No"], pagesize = 2) |> request

    if choice == 1
        anki_add_tag(card_id, "$retag")
        anki_remove_tag(card_id, tags)
    end
end

note_selector(tags = ["gtri", "transfer"], retag = "done", toclip = true, strict = false)
println("No more cards to review! Hooray!!! 🎉 🎉 🎉")

ext: jl


text:
struct ExternalFieldFlow{N,T} <: AbstractFlowDynamics
    g::SVector{N,T}
end

function collision_time(ball::MovableBall, Π::FixedPlane, flow::ExternalFieldFlow)

    @unpack r, x, v = ball
    @unpack n, p = Π
    g = flow.g

    b = v ⋅ n

    a = 0.5 * g ⋅ n

    if a == 0   # no effect of field
        return collision_time(ball, Π, FreeFlow())
    end

    c = (x - p) ⋅ n + r

    discriminant = b^2 - 4*a*c

    if discriminant ≥ 0
        d = √discriminant

        t1 = (-b - d) / (2a)
        t2 = (-b + d) / (2a)

        ## TODO: EXCLUDE CASE WHERE t1 IS BASICALLY 0 SINCE TOUCHING PLANE RIGHT NOW

        if t1 > 0 && (v + g*t1) ⋅ n > 0   # if b < 0, moving wrong way so exclude first bounce
            return t1
        end

        if t2 > 0 && (v + g*t2) ⋅ n > 0
            return t2
        end
    end

    return Inf   # no collision
end

"Collision time for moving balls is independent of constant external field."
collision_time(b1::MovableBall, b2::MovableBall, ::ExternalFieldFlow) =
    collision_time(b1, b2, FreeFlow())

"Flow ball for a time t"
function flow!(b::MovableBall, t, flow::ExternalFieldFlow)
    g = flow.g

    b.x += b.v * t + 0.5*g*t^2
    b.v += g * t

    return b
end

max_stars_repo_name: dpsanders/HardSphereDynamics

max_forks_repo_name: dpsanders/HardSphereDynamics


text:
module UnsafeAtomics

using Base.Sys: WORD_SIZE
using Base.Threads: inttypes, llvmtypes
using Core.Intrinsics: llvmcall

const unordered = Val{:unordered}()
const monotonic = Val{:monotonic}()
const acquire = Val{:acquire}()
const release = Val{:release}()
const acq_rel = Val{:acq_rel}()
const seq_cst = Val{:seq_cst}()

const orderings = [:unordered, :monotonic, :acquire, :release, :acq_rel, :seq_cst]

for typ in inttypes
    lt = llvmtypes[typ]
    rt = "$lt, $lt*"

    @eval @inline load(x::Ptr{$typ}) = load(x, seq_cst)
    for ord in orderings
        ord in [:release, :acq_rel] && continue

        @eval function load(x::Ptr{$typ}, ::$(Val{ord}))
            return llvmcall(
                $("""
                %ptr = inttoptr i$WORD_SIZE %0 to $lt*
                %rv = load atomic $rt %ptr $ord, align $(sizeof(typ))
                ret $lt %rv
                """),
                $typ,
                Tuple{Ptr{$typ}},
                x,
            )
        end
    end

    @eval @inline store!(x::Ptr{$typ}, v::$typ) = store!(x, v, seq_cst)
    for ord in orderings
        ord in [:acquire, :acq_rel] && continue

        @eval function store!(x::Ptr{$typ}, v::$typ, ::$(Val{ord}))
            return llvmcall(
                $("""
                %ptr = inttoptr i$WORD_SIZE %0 to $lt*
                store atomic $lt %1, $lt* %ptr $ord, align $(sizeof(typ))
                ret void
                """),
                Cvoid,
                Tuple{Ptr{$typ},$typ},
                x,
                v,
            )
        end
    end

    @eval @inline cas!(x::Ptr{$typ}, cmp::$typ, new::$typ) =
        cas!(x, cmp, new, seq_cst, seq_cst)
    for success_ordering in orderings[2:end],
        failure_ordering in [:monotonic, :acquire, :seq_cst]

        @eval function cas!(
            x::Ptr{$typ},
            cmp::$typ,
            new::$typ,
            ::$(Val{success_ordering}),
            ::$(Val{failure_ordering}),
        )
            return llvmcall(
                $(
                    """
                    %ptr = inttoptr i$WORD_SIZE %0 to $lt*
                    %rs = cmpxchg $lt* %ptr, $lt %1, $lt %2 $success_ordering $failure_ordering
                    %rv = extractvalue { $lt, i1 } %rs, 0
                    ret $lt %rv
                    """
                ),
                $typ,
                Tuple{Ptr{$typ},$typ,$typ},
                x,
                cmp,
                new,
            )
        end
    end

    for rmwop in [:add, :sub, :xchg, :and, :nand, :or, :xor, :max, :min]
        rmw = string(rmwop)
        fn = Symbol(rmw, "!")
        if (rmw == "max" || rmw == "min") && typ <: Unsigned
            # LLVM distinguishes signedness in the operation, not the integer type.
            rmw = "u" * rmw
        end
        @eval @inline $fn(x::Ptr{$typ}, v::$typ) = $fn(x, v, seq_cst)
        for ord in orderings
            @eval function $fn(x::Ptr{$typ}, v::$typ, ::$(Val{ord}))
                return llvmcall(
                    $("""
                    %ptr = inttoptr i$WORD_SIZE %0 to $lt*
                    %rv = atomicrmw $rmw $lt* %ptr, $lt %1 $ord
                    ret $lt %rv
                    """),
                    $typ,
                    Tuple{Ptr{$typ},$typ},
                    x,
                    v,
                )
            end
        end
    end

end

end  # module UnsafeAtomics


text:
function theQuestion()
    return question
end

max_stars_repo_name: takbal/linter-julia

max_issues_repo_name: takbal/linter-julia

max_forks_repo_name: takbal/linter-julia


text:
"""
    IMERGDaily{ST<:AbstractString, DT<:TimeType} <: IMERGDataset

Object containing information on Daily IMERG datasets to be downloaded
"""
struct IMERGDaily{ST<:AbstractString, DT<:TimeType} <: IMERGDataset
	npdID :: ST
	lname :: ST
	doi   :: ST
    dtbeg :: DT
    dtend :: DT
    sroot :: ST
    hroot :: ST
    fpref :: ST
    fsuff :: ST
end

"""
    IMERGEarlyDY(
        ST = String,
        DT = Date;
        dtbeg :: TimeType,
        dtend :: TimeType,
        sroot :: AbstractString = homedir(),
    ) -> npd :: IMERGDaily{ST,DT}

Creates a `IMERGDaily` dataset `npd` to retrieve datasets from the Near Real-Time Early processing runs for Daily output

Keyword Arguments
=================
- `dtbeg` : Date at which download / analysis of the dataset begins
- `dtend` : Date at which download / analysis of the dataset ends
- `sroot` : The directory in which the folder `imergearlydy` will be created for data downloads, storage and analysis, default is the home directoy called by `homedir()`

The following fields in `npd` will be fixed as below:
- `npdID` : imergearlydy
- `lname` : Early IMERG Daily
- `doi`   : 10.5067/GPM/IMERGDE/DAY/06
- `hroot` : https://gpm1.gesdisc.eosdis.nasa.gov/opendap/GPM_L3/GPM_3IMERGDE.06
- `fpref` : 3B-DAY-E.MS.MRG.3IMERG
- `fsuff` : S000000-E235959.V06.nc4
"""
function IMERGEarlyDY(
    ST = String,
    DT = Date;
    dtbeg :: TimeType,
    dtend :: TimeType,
    sroot :: AbstractString = homedir(),
)

	@info "$(modulelog()) - Setting up data structure containing information on Early IMERG Daily data to be downloaded"

    fol = joinpath(sroot,"imergearlydy"); if !isdir(fol); mkpath(fol) end

	dtbeg = Date(year(dtbeg),month(dtbeg),1)
	dtend = Date(year(dtend),month(dtend),daysinmonth(dtend))

    return IMERGDaily{ST,DT}(
		"imergearlydy", "Early IMERG Daily", "10.5067/GPM/IMERGDE/DAY/06",
        dtbeg, dtend,
		joinpath(sroot,"imergearlydy"),
        "https://gpm1.gesdisc.eosdis.nasa.gov/opendap/GPM_L3/GPM_3IMERGDE.06",
        "3B-DAY-E.MS.MRG.3IMERG", "S000000-E235959.V06.nc4",
    )

end

"""
    IMERGLateDY(
        ST = String,
        DT = Date;
        dtbeg :: TimeType,
        dtend :: TimeType,
        sroot :: AbstractString = homedir(),
    ) -> npd :: IMERGDaily{ST,DT}

Creates a `IMERGDaily` dataset `npd` to retrieve datasets from the Near Real-Time Late processing runs for Daily output

Keyword Arguments
=================
- `dtbeg` : Date at which download / analysis of the dataset begins
- `dtend` : Date at which download / analysis of the dataset ends
- `sroot` : The directory in which the folder `imerglatedy` will be created for data downloads, storage and analysis, default is the home directoy called by `homedir()`

The following fields in `npd` will be fixed as below:
- `npdID` : imerglatedy
- `lname` : Late IMERG Daily
- `doi`   : 10.5067/GPM/IMERGDL/DAY/06
- `hroot` : https://gpm1.gesdisc.eosdis.nasa.gov/opendap/GPM_L3/GPM_3IMERGDL.06
- `fpref` : 3B-DAY-L.MS.MRG.3IMERG
- `fsuff` : S000000-E235959.V06.nc4
"""
function IMERGLateDY(
    ST = String,
    DT = Date;
    dtbeg :: TimeType,
    dtend :: TimeType,
    sroot :: AbstractString = homedir(),
)

	@info "$(modulelog()) - Setting up data structure containing information on Late IMERG Daily data to be downloaded"

    fol = joinpath(sroot,"imerglatedy"); if !isdir(fol); mkpath(fol) end

	dtbeg = Date(year(dtbeg),month(dtbeg),1)
	dtend = Date(year(dtend),month(dtend),daysinmonth(dtend))

    return IMERGDaily{ST,DT}(
		"imerglatedy", "Late IMERG Daily", "10.5067/GPM/IMERGDL/DAY/06",
        dtbeg, dtend,
		joinpath(sroot,"imerglatedy"),
        "https://gpm1.gesdisc.eosdis.nasa.gov/opendap/GPM_L3/GPM_3IMERGDL.06",
        "3B-DAY-L.MS.MRG.3IMERG", "S000000-E235959.V06.nc4",
    )

end

"""
    IMERGFinalDY(
        ST = String,
        DT = Date;
        dtbeg :: TimeType,
        dtend :: TimeType,
        sroot :: AbstractString = homedir(),
    ) -> npd :: IMERGDaily{ST,DT}

Creates a `IMERGDaily` dataset `npd` to retrieve datasets from the Final post-processing runs for Daily output

Keyword Arguments
=================
- `dtbeg` : Date at which download / analysis of the dataset begins
- `dtend` : Date at which download / analysis of the dataset ends
- `sroot` : The directory in which the folder `imergfinaldy` will be created for data downloads, storage and analysis, default is the home directoy called by `homedir()`

The following fields in `npd` will be fixed as below:
- `npdID` : imergfinaldy
- `npdID` : Final IMERG Daily
- `doi`   : 10.5067/GPM/IMERGDF/DAY/06
- `hroot` : https://gpm1.gesdisc.eosdis.nasa.gov/opendap/GPM_L3/GPM_3IMERGDF.06
- `fpref` : 3B-DAY.MS.MRG.3IMERG
- `fsuff` : S000000-E235959.V06.nc4
"""
function IMERGFinalDY(
    ST = String,
    DT = Date;
    dtbeg :: TimeType,
    dtend :: TimeType,
    sroot :: AbstractString = homedir(),
)

	@info "$(modulelog()) - Setting up data structure containing information on Final IMERG Daily data to be downloaded"

    fol = joinpath(sroot,"imergfinaldy"); if !isdir(fol); mkpath(fol) end

	dtbeg = Date(year(dtbeg),month(dtbeg),1)
	dtend = Date(year(dtend),month(dtend),daysinmonth(dtend))

    return IMERGDaily{ST,DT}(
		"imergfinaldy", "Final IMERG Daily", "10.5067/GPM/IMERGDF/DAY/06",
        dtbeg, dtend,
		joinpath(sroot,"imergfinaldy"),
        "https://gpm1.gesdisc.eosdis.nasa.gov/opendap/GPM_L3/GPM_3IMERGDF.06",
        "3B-DAY.MS.MRG.3IMERG", "S000000-E235959.V06.nc4",
    )

end

function show(io::IO, npd::IMERGDaily{ST,DT}) where {ST<:AbstractString, DT<:TimeType}
    print(
		io,
		"The NASA Precipitation Dataset {$ST,$DT} has the following properties:\n",
		"    Dataset ID      (npdID) : ", npd.npdID, '\n',
		"    Logging Name    (lname) : ", npd.lname, '\n',
		"    DOI URL          (doi)  : ", npd.doi,   '\n',
		"    Data Directory  (sroot) : ", npd.sroot, '\n',
		"    Date Begin      (dtbeg) : ", npd.dtbeg, '\n',
		"    Date End        (dtend) : ", npd.dtend, '\n',
		"    Timestep                : 1 Day\n",
        "    Data Resolution         : 0.1º\n",
        "    Data Server     (hroot) : ", npd.hroot, '\n',
	)
end

max_stars_repo_name: natgeo-wong/NASAPrecipitation

max_issues_repo_name: natgeo-wong/NASAPrecipitation

max_forks_repo_name: natgeo-wong/NASAPrecipitation


text:
"""
# Usage

    solve(prob::MultiTermsFODEProblem, t, p, ClosedFormHighPercision())

Closed form high precision algorithms for multi term ordinary differential equations.
"""
struct ClosedFormHighPercision <: FractionalDiffEqAlgorithm end

function solve(prob::MultiTermsFODEProblem, h, p, ::ClosedFormHighPercision)
    @unpack parameters, orders, rightfun, rparameters, rorders, tspan =  prob
    t0 = tspan[1]; T = tspan[2]
    t = collect(t0:h:T)
    u = rightfun.(t)
    n = length(t)
    na = orders[:]
    nb = rorders[:]
    a = parameters[:]
    b = rparameters[:]
    vec = [na; nb]
    u = u[:]
    g = genfun(p)
    t = t[:]
    W = zeros(n, n)

    for i=1:length(vec)
        W[i, :] = getvec(vec[i], n, g)
    end

    D1 = b./h.^nb
    nA = length(a)
    y1 = zeros(n)

    #W=W'
    D = sum((a[:].*W[1, 1:nA])./h.^na)

    for i = 2:n
        A = y1[i-1:-1:1]'*W[2:i, 1:nA]
        y1[i] = (u[i]-sum(A.*a./h.^na))/D
    end

    y = zeros(n)

    for i = 2:n
        # Here are some problems with the indexing
        y[i] = (W[1:i, nA+1:end]*D1)'*y1[i:-1:1]
    end

    return y
end


text:
"""
    LaneBoundary
Data structure to represent lanes boundaries such as double yellow lines.
# Fields
    - `style::Symbol` ∈ :solid, :broken, :double
    - `color::Symbol` ∈ :yellow, white
"""
struct LaneBoundary
    style::Symbol # ∈ :solid, :broken, :double
    color::Symbol # ∈ :yellow, white
end
const NULL_BOUNDARY = LaneBoundary(:unknown, :unknown)

#######################
"""
    SpeedLimit
Datastructure to represent a speed limit
# Fields
- `lo::Float64` [m/s] lower speed limit
- `hi::Float64` [m/s] higher speed limit
"""
struct SpeedLimit
    lo::Float64 # [m/s]
    hi::Float64 # [m/s]
end
const DEFAULT_SPEED_LIMIT = SpeedLimit(-Inf, Inf)

#######################
"""
    LaneTag
An identifier for a lane. The lane object can be retrieved by indexing the roadway by the lane tag:
```julia
tag = LaneTag(1, 2) # second lane segment 1
lane = roadway[tag] # returns a Lane object
```
# Fields
- `segment::Int64` segment id
- `lane::Int64` index in segment.lanes of this lane
"""
struct LaneTag
    segment::Int64 # segment id
    lane::Int64 # index in segment.lanes of this lane
end
Base.show(io::IO, tag::LaneTag) = @printf(io, "LaneTag(%d, %d)", tag.segment, tag.lane)
const NULL_LANETAG = LaneTag(0,0)

#######################################

"""
    RoadIndex{I <: Integer, T <: Real}
A data structure to index points in a roadway. Calling `roadway[roadind]` will return the point
associated to the road index.
# Fields
- `ind::CurveIndex{I,T}` the index of the point in the curve
- `tag::LaneTag` the lane tag of the point
"""
struct RoadIndex{I <: Integer, T <: Real}
    ind::CurveIndex{I, T}
    tag::LaneTag
end
const NULL_ROADINDEX = RoadIndex(CurveIndex(-1,NaN), LaneTag(-1,-1))

Base.show(io::IO, r::RoadIndex) = @printf(io, "RoadIndex({%d, %3.f}, {%d, %d})", r.ind.i, r.ind.t, r.tag.segment, r.tag.lane)
Base.write(io::IO, r::RoadIndex) = @printf(io, "%d %.6f %d %d", r.ind.i, r.ind.t, r.tag.segment, r.tag.lane)

#######################################

"""
    LaneConnection{I <: Integer, T <: Real}
Data structure to specify the connection of a lane. It connects `mylane` to the point `target`.
`target` would typically be the starting point of a new lane.
- `downstream::Bool`
- `mylane::CurveIndex{I,T}`
- `target::RoadIndex{I,T}`
"""
struct LaneConnection{I <: Integer, T <: Real}
    downstream::Bool # if true, mylane → target, else target → mylane
    mylane::CurveIndex{I, T}
    target::RoadIndex{I, T}
end

Base.show(io::IO, c::LaneConnection) = print(io, "LaneConnection(", c.downstream ? "D" : "U", ", ", c.mylane, ", ", c.target)
function Base.write(io::IO, c::LaneConnection)
    @printf(io, "%s (%d %.6f) ", c.downstream ? "D" : "U", c.mylane.i, c.mylane.t)
    write(io, c.target)
end
function Base.parse(::Type{LaneConnection}, line::AbstractString)
    cleanedline = replace(line, r"(\(|\))" => "")
    tokens = split(cleanedline, ' ')

    @assert(tokens[1] == "D" || tokens[1] == "U")
    downstream = tokens[1] == "D"
    mylane = CurveIndex(parse(Int, tokens[2]), parse(Float64, tokens[3]))
    target = RoadIndex(
                CurveIndex(parse(Int, tokens[4]), parse(Float64, tokens[5])),
                LaneTag(parse(Int, tokens[6]), parse(Int, tokens[7]))
            )
    LaneConnection{Int64, Float64}(downstream, mylane, target)
end

#######################################

const DEFAULT_LANE_WIDTH = 3.0 # [m]

"""
    Lane
A driving lane on a roadway. It identified by a `LaneTag`. A lane is defined by a curve which
represents a center line and a width. In addition it has attributed like speed limit.
A lane can be connected to other lane in the roadway, the connection are specified in the exits
and entrances fields.

# Fields
- `tag::LaneTag`
- `curve::Curve`
- `width::Float64`  [m]
- `speed_limit::SpeedLimit`
- `boundary_left::LaneBoundary`
- `boundary_right::LaneBoundary`
- `exits::Vector{LaneConnection} # list of exits; put the primary exit (at end of lane) first`
- `entrances::Vector{LaneConnection} # list of entrances; put the primary entrance (at start of lane) first`
"""
mutable struct Lane{T <: Real}
    tag            :: LaneTag
    curve          :: Curve{T}
    width          :: Float64 # [m]
    speed_limit    :: SpeedLimit
    boundary_left  :: LaneBoundary
    boundary_right :: LaneBoundary
    exits          :: Vector{LaneConnection{Int64, T}} # list of exits; put the primary exit (at end of lane) first
    entrances      :: Vector{LaneConnection{Int64, T}} # list of entrances; put the primary entrance (at start of lane) first
end
function Lane(
    tag::LaneTag,
    curve::Curve{T};
    width::Float64 = DEFAULT_LANE_WIDTH,
    speed_limit::SpeedLimit = DEFAULT_SPEED_LIMIT,
    boundary_left::LaneBoundary = NULL_BOUNDARY,
    boundary_right::LaneBoundary = NULL_BOUNDARY,
    exits::Vector{LaneConnection{Int64, T}} = LaneConnection{Int64,T}[],
    entrances::Vector{LaneConnection{Int64, T}} = LaneConnection{Int64,T}[],
    next::RoadIndex=NULL_ROADINDEX,
    prev::RoadIndex=NULL_ROADINDEX,
    ) where T

    lane = Lane{T}(tag,
                curve,
                width,
                speed_limit,
                boundary_left,
                boundary_right,
                exits,
                entrances)

    if next != NULL_ROADINDEX
        pushfirst!(lane.exits, LaneConnection(true, curveindex_end(lane.curve), next))
    end
    if prev != NULL_ROADINDEX
        pushfirst!(lane.entrances, LaneConnection(false, CURVEINDEX_START, prev))
    end

    return lane
end

"""
    has_next(lane::Lane)
returns true if the end of the lane is connected to another lane (i.e. if it has an exit lane)
"""
has_next(lane::Lane) = !isempty(lane.exits) && lane.exits[1].mylane == curveindex_end(lane.curve)

"""
    has_prev(lane::Lane)
returns true if another lane is connected to the beginning of that lane. (i.e. if it has an entrance lane)
"""
has_prev(lane::Lane) = !isempty(lane.entrances) && lane.entrances[1].mylane == CURVEINDEX_START

"""
    is_in_exits(lane::Lane, target::LaneTag)
returns true if `target` is in the exit lanes of `lane`.
"""
is_in_exits(lane::Lane, target::LaneTag) = findfirst(lc->lc.target.tag == target, lane.exits) != nothing

"""
    is_in_entrances(lane::Lane, target::LaneTag)
returns true if `target` is in the entrances lanes of `lane`.
"""
is_in_entrances(lane::Lane, target::LaneTag) = findfirst(lc->lc.target.tag == target, lane.entrances) != nothing

"""
    connect!(source::Lane, dest::Lane)
connect two lanes to each other. Useful for roadway construction.
"""
function connect!(source::Lane, dest::Lane)
    # place these at the front

    cindS = curveindex_end(source.curve)
    cindD = CURVEINDEX_START

    pushfirst!(source.exits,   LaneConnection(true,  cindS, RoadIndex(cindD, dest.tag)))
    pushfirst!(dest.entrances, LaneConnection(false, cindD, RoadIndex(cindS, source.tag)))
    (source, dest)
end
function connect!(source::Lane, cindS::CurveIndex, dest::Lane, cindD::CurveIndex)
    push!(source.exits,   LaneConnection(true,  cindS, RoadIndex(cindD, dest.tag)))
    push!(dest.entrances, LaneConnection(false, cindD, RoadIndex(cindS, source.tag)))
    (source, dest)
end

#######################################
"""
    RoadSegment{T}
a list of lanes forming a single road with a common direction
# Fields
- `id::Int64`
- `lanes::Vector{Lane{T}}` lanes are stored right to left
"""
mutable struct RoadSegment{T<:Real}
    id::Int64
    lanes::Vector{Lane{T}}
end
RoadSegment{T}(id::Int64) where T = RoadSegment{T}(id, Lane{T}[])

#######################################

"""
    Roadway
The main datastructure to represent road network, it consists of a list of `RoadSegment`
# Fields
- `segments::Vector{RoadSegment}`
"""
mutable struct Roadway{T<:Real}
    segments::Vector{RoadSegment{T}}
end
Roadway{T}() where T = Roadway{T}(RoadSegment{T}[])
Roadway() = Roadway{Float64}()

Base.show(io::IO, roadway::Roadway) = @printf(io, "Roadway")
"""
    Base.write(io::IO, ::MIME"text/plain", roadway::Roadway)
write all the roadway information to a text file
"""
function Base.write(io::IO, ::MIME"text/plain", roadway::Roadway)
    # writes to a text file

    println(io, "ROADWAY")
    println(io, length(roadway.segments)) # number of segments
    for seg in roadway.segments
        println(io, seg.id)
        println(io, "\t", length(seg.lanes)) # number of lanes
        for (i,lane) in enumerate(seg.lanes)
            @assert(lane.tag.lane == i)
            @printf(io, "\t%d\n", i)
            @printf(io, "\t\t%.3f\n", lane.width)
            @printf(io, "\t\t%.3f %.3f\n", lane.speed_limit.lo, lane.speed_limit.hi)
            println(io, "\t\t", lane.boundary_left.style, " ", lane.boundary_left.color)
            println(io, "\t\t", lane.boundary_right.style, " ", lane.boundary_right.color)
            println(io, "\t\t", length(lane.exits) + length(lane.entrances))
            for conn in lane.exits
                print(io, "\t\t\t"); write(io, conn); print(io, "\n")
            end
            for conn in lane.entrances
                print(io, "\t\t\t"); write(io, conn); print(io, "\n")
            end
            println(io, "\t\t", length(lane.curve))
            for pt in lane.curve
                @printf(io, "\t\t\t(%.4f %.4f %.6f) %.4f %.8f %.8f\n", pt.pos.x, pt.pos.y, pt.pos.θ, pt.s, pt.k, pt.kd)
            end
        end
    end
end

"""
    Base.read(io::IO, ::MIME"text/plain", ::Type{Roadway})
extract roadway information from a text file and returns a roadway object.
"""
function Base.read(io::IO, ::MIME"text/plain", ::Type{Roadway})
    lines = readlines(io)
    line_index = 1
    if occursin("ROADWAY", lines[line_index])
        line_index += 1
    end

    function advance!()
        line = strip(lines[line_index])
        line_index += 1
        line
    end

    nsegs = parse(Int, advance!())
    roadway = Roadway{Float64}(Array{RoadSegment{Float64}}(undef, nsegs))
    for i_seg in 1:nsegs
        segid = parse(Int, advance!())
        nlanes = parse(Int, advance!())
        seg = RoadSegment(segid, Array{Lane{Float64}}(undef, nlanes))
        for i_lane in 1:nlanes
            @assert(i_lane == parse(Int, advance!()))
            tag = LaneTag(segid, i_lane)
            width = parse(Float64, advance!())

            tokens = split(advance!(), ' ')
            speed_limit = SpeedLimit(parse(Float64, tokens[1]), parse(Float64, tokens[2]))

            tokens = split(advance!(), ' ')
            boundary_left = LaneBoundary(Symbol(tokens[1]), Symbol(tokens[2]))

            tokens = split(advance!(), ' ')
            boundary_right = LaneBoundary(Symbol(tokens[1]), Symbol(tokens[2]))

            exits = LaneConnection{Int64, Float64}[]
            entrances = LaneConnection{Int64, Float64}[]
            n_conns = parse(Int, advance!())
            for i_conn in 1:n_conns
                conn = parse(LaneConnection, advance!())
                conn.downstream ? push!(exits, conn) : push!(entrances, conn)
            end

            npts = parse(Int, advance!())
            curve = Array{CurvePt{Float64}}(undef, npts)
            for i_pt in 1:npts
                line = advance!()
                cleanedline = replace(line, r"(\(|\))" => "")
                tokens = split(cleanedline, ' ')
                x = parse(Float64, tokens[1])
                y = parse(Float64, tokens[2])
                θ = parse(Float64, tokens[3])
                s = parse(Float64, tokens[4])
                k = parse(Float64, tokens[5])
                kd = parse(Float64, tokens[6])
                curve[i_pt] = CurvePt(VecSE2(x,y,θ), s, k, kd)
            end

            seg.lanes[i_lane] = Lane(tag, curve, width=width, speed_limit=speed_limit,
                                     boundary_left=boundary_left,
                                     boundary_right=boundary_right,
                                     entrances=entrances, exits=exits)
        end
        roadway.segments[i_seg] = seg
    end

    roadway
end

"""
    lane[ind::CurveIndex, roadway::Roadway]
Accessor for lanes based on a CurveIndex.
Note that we extend the definition of a CurveIndex,
previously ind.i ∈ [1, length(curve)-1], to:

    ind.i ∈ [0, length(curve)]

where 1 ≤ ind.i ≤ length(curve)-1 is as before, but if the index
is on the section between two lanes, we use:

    ind.i = length(curve), ind.t ∈ [0,1] for the region between curve[end] → next
    ind.i = 0,             ind.t ∈ [0,1] for the region between prev → curve[1]
"""
function Base.getindex(lane::Lane{T}, ind::CurveIndex{I, T}, roadway::Roadway{T}) where {I<:Integer,T<:Real}
    if ind.i == 0
        pt_lo = prev_lane_point(lane, roadway)
        pt_hi = lane.curve[1]
        s_gap = norm(VecE2(pt_hi.pos - pt_lo.pos))
        pt_lo = CurvePt{T}(pt_lo.pos, -s_gap, pt_lo.k, pt_lo.kd)
        return lerp(pt_lo, pt_hi, ind.t)
    elseif ind.i < length(lane.curve)
        return lane.curve[ind]
    else
        pt_hi = next_lane_point(lane, roadway)
        pt_lo = lane.curve[end]
        s_gap = norm(VecE2(pt_hi.pos - pt_lo.pos))
        pt_hi = CurvePt{T}(pt_hi.pos, pt_lo.s + s_gap, pt_hi.k, pt_hi.kd)
        return lerp( pt_lo, pt_hi, ind.t)
    end
end

"""
    Base.getindex(roadway::Roadway, segid::Int)
returns the segment associated with id `segid`
"""
function Base.getindex(roadway::Roadway, segid::Int)
    for seg in roadway.segments
        if seg.id == segid
            return seg
        end
    end
    error("Could not find segid $segid in roadway")
end

"""
    Base.getindex(roadway::Roadway, tag::LaneTag)
returns the lane identified by the tag `LaneTag`
"""
function Base.getindex(roadway::Roadway, tag::LaneTag)
    seg = roadway[tag.segment]
    seg.lanes[tag.lane]
end

"""
    is_between_segments_lo(ind::CurveIndex)
"""
is_between_segments_lo(ind::CurveIndex) = ind.i == 0

"""
    is_between_segments_hi(ind::CurveIndex, curve::Curve)
"""
is_between_segments_hi(ind::CurveIndex, curve::Curve) = ind.i == length(curve)

"""
    is_between_segments(ind::CurveIndex, curve::Curve)
"""
is_between_segments(ind::CurveIndex, curve::Curve) = is_between_segments_lo(ind) || is_between_segments_hi(ind, curve)

"""
    next_lane(lane::Lane, roadway::Roadway)
returns the lane connected to the end `lane`. If `lane` has several exits, it returns the first one
"""
next_lane(lane::Lane, roadway::Roadway) = roadway[lane.exits[1].target.tag]

"""
    prev_lane(lane::Lane, roadway::Roadway)
returns the lane connected to the beginning `lane`. If `lane` has several entrances, it returns the first one
"""
prev_lane(lane::Lane, roadway::Roadway) = roadway[lane.entrances[1].target.tag]

"""
    next_lane_point(lane::Lane, roadway::Roadway)
returns the point of connection between `lane` and its first exit
"""
next_lane_point(lane::Lane, roadway::Roadway) = roadway[lane.exits[1].target]

"""
    prev_lane_point(lane::Lane, roadway::Roadway)
returns the point of connection between `lane` and its first entrance
"""
prev_lane_point(lane::Lane, roadway::Roadway) = roadway[lane.entrances[1].target]

"""
    has_segment(roadway::Roadway, segid::Int)
returns true if `segid` is in `roadway`.
"""
function has_segment(roadway::Roadway, segid::Int)
    for seg in roadway.segments
        if seg.id == segid
            return true
        end
    end
    false
end

"""
    has_lanetag(roadway::Roadway, tag::LaneTag)
returns true if `roadway` contains a lane identified by `tag`
"""
function has_lanetag(roadway::Roadway, tag::LaneTag)
    if !has_segment(roadway, tag.segment)
        return false
    end
    seg = roadway[tag.segment]
    1 ≤ tag.lane ≤ length(seg.lanes)
end

"""
    RoadProjection{I <: Integer, T <: Real}
represents the projection of a point on the roadway
# Fields
- `curveproj::CurveProjection{I, T}`
- `tag::LaneTag`
"""
struct RoadProjection{I <: Integer, T <: Real}
    curveproj::CurveProjection{I, T}
    tag::LaneTag
end

function get_closest_perpendicular_point_between_points(A::VecSE2{T}, B::VecSE2{T}, Q::VecSE2{T};
    tolerance::T = 0.01, # acceptable error in perpendicular component
    max_iter::Int = 50, # maximum number of iterations
    ) where T

    # CONDITIONS: a < b, either f(a) < 0 and f(b) > 0 or f(a) > 0 and f(b) < 0
    # OUTPUT: value which differs from a root of f(x)=0 by less than TOL

    a = convert(T, 0.0)
    b = convert(T, 1.0)

    f_a = inertial2body(Q, A).x
    f_b = inertial2body(Q, B).x

    if sign(f_a) == sign(f_b) # both are wrong - use the old way
        t = get_lerp_time_unclamped(A, B, Q)
        t = clamp(t, 0.0, 1.0)
        return (t, lerp(A,B,t))
    end

    iter = 1
    while iter ≤ max_iter
        c = (a+b)/2 # new midpoint
        footpoint = lerp(A, B, c)
        f_c = inertial2body(Q, footpoint).x
        if abs(f_c) < tolerance # solution found
            return (c, footpoint)
        end
        if sign(f_c) == sign(f_a)
            a, f_a = c, f_c
        else
            b = c
        end
        iter += 1
    end

    # Maximum number of iterations passed
    # This will occur when we project with a point that is not actually in the range,
    # and we converge towards one edge

    if a == 0.0
        return (a, A)
    elseif b == 1.0
        return (b, B)
    else
        warn("get_closest_perpendicular_point_between_points - should not happen")
        c = (a+b)/2 # should not happen
        return (c, lerp(A,B,c))
    end
end

"""
    proj(posG::VecSE2{T}, lane::Lane, roadway::Roadway; move_along_curves::Bool=true) where T <: Real
Return the RoadProjection for projecting posG onto the lane.
This will automatically project to the next or prev curve as appropriate.
if `move_along_curves` is false, will only project to lane.curve
"""
function Vec.proj(posG::VecSE2{T}, lane::Lane{T}, roadway::Roadway{T};
    move_along_curves::Bool = true, # if false, will only project to lane.curve
    ) where T <: Real
    curveproj = proj(posG, lane.curve)
    rettag = lane.tag

    if curveproj.ind == CurveIndex(1,zero(T)) && has_prev(lane)
        pt_lo = prev_lane_point(lane, roadway)
        pt_hi = lane.curve[1]

        t = get_lerp_time_unclamped(pt_lo, pt_hi, posG)
        if t ≤ 0.0 && move_along_curves
            return proj(posG, prev_lane(lane, roadway), roadway)
        elseif t < 1.0 # for t == 1.0 we use the actual end of the lane
            @assert(!move_along_curves || 0.0 ≤ t < 1.0)

            # t was computed assuming a constant angle
            # this is not valid for the large distances and angle disparities between lanes
            # thus we now use a bisection search to find the appropriate location

            t, footpoint = get_closest_perpendicular_point_between_points(pt_lo.pos, pt_hi.pos, posG)

            ind = CurveIndex(0, t)
            curveproj = get_curve_projection(posG, footpoint, ind)
        end
    elseif curveproj.ind == curveindex_end(lane.curve) && has_next(lane)
        pt_lo = lane.curve[end]
        pt_hi = next_lane_point(lane, roadway)

        t = get_lerp_time_unclamped(pt_lo, pt_hi, posG)

        if t ≥ 1.0 && move_along_curves
             # for t == 1.0 we use the actual start of the lane
            return proj(posG, next_lane(lane, roadway), roadway)
        elseif t ≥ 0.0
            @assert(!move_along_curves || 0.0 ≤ t ≤ 1.0)

            # t was computed assuming a constant angle
            # this is not valid for the large distances and angle disparities between lanes
            # thus we now use a bisection search to find the appropriate location

            t, footpoint = get_closest_perpendicular_point_between_points(pt_lo.pos, pt_hi.pos, posG)

            ind = CurveIndex(length(lane.curve), t)
            curveproj = get_curve_projection(posG, footpoint, ind)
        end
    end
    RoadProjection(curveproj, rettag)
end

"""
    proj(posG::VecSE2{T}, seg::RoadSegment, roadway::Roadway) where T <: Real
Return the RoadProjection for projecting posG onto the segment.
Tries all of the lanes and gets the closest one
"""
function Vec.proj(posG::VecSE2{T}, seg::RoadSegment, roadway::Roadway) where T <: Real

    best_dist2 = Inf
    best_proj = RoadProjection{Int64, T}(CurveProjection(CurveIndex(-1,convert(T, -1)), convert(T, NaN), convert(T, NaN)),
                               NULL_LANETAG)

    for lane in seg.lanes
        roadproj = proj(posG, lane, roadway)
        footpoint = roadway[roadproj.tag][roadproj.curveproj.ind, roadway]
        dist2 = norm(VecE2(posG - footpoint.pos))
        if dist2 < best_dist2
            best_dist2 = dist2
            best_proj = roadproj
        end
    end

    best_proj
end

"""
    proj(posG::VecSE2{T}, seg::RoadSegment, roadway::Roadway) where T <: Real
Return the RoadProjection for projecting posG onto the roadway.
Tries all of the lanes and gets the closest one
"""
function Vec.proj(posG::VecSE2{T}, roadway::Roadway) where T <: Real

    best_dist2 = Inf
    best_proj = RoadProjection(CurveProjection(CurveIndex(-1,convert(T,-1.0)),
                                               convert(T, NaN),
                                               convert(T, NaN)),
                                NULL_LANETAG)

    for seg in roadway.segments
        for lane in seg.lanes
            roadproj = proj(posG, lane, roadway, move_along_curves=false)
            targetlane = roadway[roadproj.tag]
            footpoint = targetlane[roadproj.curveproj.ind, roadway]
            dist2 = normsquared(VecE2(posG - footpoint.pos))
            if dist2 < best_dist2
                best_dist2 = dist2
                best_proj = roadproj
            end
        end
    end

    best_proj
end

############################################

RoadIndex(roadproj::RoadProjection) = RoadIndex(roadproj.curveproj.ind, roadproj.tag)

"""
    Base.getindex(roadway::Roadway, roadind::RoadIndex)
returns the CurvePt on the roadway associated to `roadind`
"""
function Base.getindex(roadway::Roadway, roadind::RoadIndex)
    lane = roadway[roadind.tag]
    lane[roadind.ind, roadway]
end

"""
    move_along(roadind::RoadIndex, road::Roadway, Δs::Float64)
Return the RoadIndex at ind's s position + Δs
"""
function move_along(roadind::RoadIndex{I, T},
                    roadway::Roadway,
                    Δs::Float64,
                    depth::Int=0) where {I <: Integer, T <: Real}

    lane = roadway[roadind.tag]
    curvept = lane[roadind.ind, roadway]

    if curvept.s + Δs < 0.0
        if has_prev(lane)
            pt_lo = prev_lane_point(lane, roadway)
            pt_hi = lane.curve[1]
            s_gap = norm(VecE2(pt_hi.pos - pt_lo.pos))

            if curvept.s + Δs < -s_gap
                lane_prev = prev_lane(lane, roadway)
                curveind = curveindex_end(lane_prev.curve)
                roadind = RoadIndex{I,T}(curveind, lane_prev.tag)
                return move_along(roadind, roadway, Δs + curvept.s + s_gap, depth+1)
            else # in the gap between lanes
                t = (s_gap + curvept.s + Δs) / s_gap
                curveind = CurveIndex(0, t)
                RoadIndex{I,T}(curveind, lane.tag)
            end

        else # no prev lane, return the beginning of this one
            curveind = CurveIndex(1, 0.0)
            return RoadIndex{I,T}(curveind, roadind.tag)
        end
    elseif curvept.s + Δs > lane.curve[end].s
        if has_next(lane)
            pt_lo = lane.curve[end]
            pt_hi = next_lane_point(lane, roadway)
            s_gap = norm(VecE2(pt_hi.pos - pt_lo.pos))

            if curvept.s + Δs ≥ pt_lo.s + s_gap # extends beyond the gap
                curveind = lane.exits[1].target.ind
                roadind = RoadIndex{I,T}(curveind, lane.exits[1].target.tag)
                return move_along(roadind, roadway, Δs - (lane.curve[end].s + s_gap - curvept.s))
            else # in the gap between lanes
                t = (Δs - (lane.curve[end].s - curvept.s)) / s_gap
                curveind = CurveIndex(0, t)
                RoadIndex{I,T}(curveind, lane.exits[1].target.tag)
            end
        else # no next lane, return the end of this lane
            curveind = curveindex_end(lane.curve)
            return RoadIndex{I,T}(curveind, roadind.tag)
        end
    else
        if roadind.ind.i == 0
            ind = get_curve_index(CurveIndex(1,0.0), lane.curve, curvept.s+Δs)
        elseif roadind.ind.i == length(lane.curve)
            ind = get_curve_index(curveindex_end(lane.curve), lane.curve, curvept.s+Δs)
        else
            ind = get_curve_index(roadind.ind, lane.curve, Δs)
        end
        RoadIndex{I,T}(ind, roadind.tag)
    end
end

"""
    n_lanes_right(lane::Lane, roadway::Roadway)
returns the number of lanes to the right of `lane`
"""
n_lanes_right(lane::Lane, roadway::Roadway) = lane.tag.lane - 1

"""
    n_lanes_left(lane::Lane, roadway::Roadway)
returns the number of lanes to the left of `lane`
"""
function n_lanes_left(lane::Lane, roadway::Roadway)
    seg = roadway[lane.tag.segment]
    length(seg.lanes) - lane.tag.lane
end

"""
    lanes(roadway::Roadway{T}) where T
return a list of all the lanes present in roadway.
"""
function lanes(roadway::Roadway{T}) where T
    lanes = Lane{T}[]
    for i=1:length(roadway.segments)
        for lane in roadway.segments[i].lanes
            push!(lanes, lane)
        end
    end
    return lanes
end

"""
    lanetags(roadway::Roadway)
return a list of all the lane tags present in roadway.
"""
function lanetags(roadway::Roadway)
    lanetags = LaneTag[]
    for i=1:length(roadway.segments)
        for lane in roadway.segments[i].lanes
            push!(lanetags, lane.tag)
        end
    end
    return lanetags
end


text:
## header to provide surface and context
using Cairo
c = CairoRGBSurface(256,256);
cr = CairoContext(c);

save(cr);
set_source_rgb(cr,0.8,0.8,0.8);    # light gray
rectangle(cr,0.0,0.0,256.0,256.0); # background
fill(cr);
restore(cr);

save(cr);
## original example, following here
select_font_face(cr, "Sans", Cairo.FONT_SLANT_NORMAL,
                 Cairo.FONT_WEIGHT_NORMAL);
set_font_size(cr, 52.0);
extents = text_extents(cr, "cairo");

#
# typedef struct {
#     double x_bearing;
#     double y_bearing;
#     double width;
#     double height;
#     double x_advance;
#     double y_advance;
# } cairo_text_extents_t;

x = 128.0-(extents[3]/2 + extents[1]);
y = 128.0-(extents[4]/2 + extents[2]);

move_to(cr, x, y);
show_text(cr, "cairo");

# draw helping lines
set_source_rgba(cr, 1, 0.2, 0.2, 0.6);
set_line_width(cr, 6.0);
arc(cr, x, y, 10.0, 0, 2*pi);
fill(cr);
move_to(cr, 128.0, 0);
rel_line_to(cr, 0, 256);
move_to(cr, 0, 128.0);
rel_line_to(cr, 256, 0);
stroke(cr);

## mark picture with current date
restore(cr);
move_to(cr,0.0,12.0);
set_source_rgb(cr, 0,0,0);
show_text(cr,Libc.strftime(time()));
write_to_png(c,"sample_text_align_center.png");


text:
apply_palette(p::AbstractVector, uv) = [cycle(p, idx) for idx in eachindex(uv)]
apply_palette(::Automatic, uv) = eachindex(uv)
apply_palette(p, uv) = map(p, eachindex(uv))

# TODO: add more customizations?
struct Wrap end

const wrap = Wrap()

function apply_palette(::Wrap, uv)
    ncols = ceil(Int, sqrt(length(uv)))
    return [fldmod1(idx, ncols) for idx in eachindex(uv)]
end

struct ContinuousScale{T, F}
    f::F
    extrema::Tuple{T, T}
end

rescale(values::AbstractArray{<:Number}, c::ContinuousScale) = values # Is this ideal?
function rescale(values::AbstractArray{<:Union{Date, DateTime}}, c::ContinuousScale)
    @assert c.f === identity
    min, max = c.extrema
    return @. convert(Millisecond, DateTime(values) - DateTime(min)) / Millisecond(1)
end

struct CategoricalScale{S, T}
    data::S
    plot::T
end

function rescale(values, c::CategoricalScale)
    idxs = indexin(values, c.data)
    return c.plot[idxs]
end

Base.length(c::CategoricalScale) = length(c.data)

function default_scale(summary, palette)
    iscont = summary isa Tuple
    return if iscont
        f = palette isa Function ? palette : identity
        ContinuousScale(f, summary)
    else
        plot = apply_palette(palette, summary)
        return CategoricalScale(summary, plot)
    end
end

# Logic to infer good scales
function default_scales(summaries, palettes)
    palettes = merge!(map(_ -> automatic, summaries), palettes)
    return map(default_scale, summaries, palettes)
end

# Logic to create ticks from a scale
# Should take current tick to incorporate information
function ticks(scale::CategoricalScale)
    u = map(string, scale.data)
    return (axes(u, 1), u)
end

function ticks(scale::ContinuousScale)
    return continuousticks(scale.extrema...)
end

continuousticks(min, max) = automatic

function continuousticks(min::T, max::T) where T<:Union{Date, DateTime}
    min_ms::Millisecond, max_ms::Millisecond = DateTime(min), DateTime(max)
    min_pure, max_pure = min_ms/Millisecond(1), max_ms/Millisecond(1)
    dates, labels = optimize_datetime_ticks(min_pure, max_pure)
    return (dates .-  min_pure, labels)
end


text:
using Base.Test
using Clustering
using Distances

srand(34568)

X1 = randn(2, 200) .+ [0., 5.]
X2 = randn(2, 200) .+ [-5., 0.]
X3 = randn(2, 200) .+ [5., 0.]
X = hcat(X1, X2, X3)
n = size(X,2)

D = pairwise(Euclidean(), X)

R = dbscan(D, 1.0, 10)
@test isa(R, DbscanResult)
k = length(R.seeds)
# println("k = $k")
@test k == 3
@test all(R.assignments .<= k)
@test length(R.assignments) == n
@test length(R.counts) == k
for c = 1:k
    @test countnz(R.assignments .== c) == R.counts[c]
end
@test all(R.counts .>= 180)


text:
"""
    BroadcastAxis

An abstract trait that is used to determine how axes are combined when calling `broadcast_axis`.
"""
abstract type BroadcastAxis end

struct BroadcastAxisDefault <: BroadcastAxis end

BroadcastAxis(x) = BroadcastAxis(typeof(x))
BroadcastAxis(::Type{T}) where {T} = BroadcastAxisDefault()

"""
    broadcast_axis(x, y)

Broadcast axis `x` and `y` into a common space. The resulting axis should be equal in length
to both `x` and `y` unless one has a length of `1`, in which case the longest axis will be
equal to the output.

```julia
julia> ArrayInterface.broadcast_axis(1:10, 1:10)

julia> ArrayInterface.broadcast_axis(1:10, 1)
1:10

```
"""
broadcast_axis(x, y) = broadcast_axis(BroadcastAxis(x), x, y)
# stagger default broadcasting in case y has something other than default
broadcast_axis(::BroadcastAxisDefault, x, y) = _broadcast_axis(BroadcastAxis(y), x, y)
function _broadcast_axis(::BroadcastAxisDefault, x, y)
    return One():_combine_length(length(x), length(y))
end
_broadcast_axis(s::BroadcastAxis, x, y) = broadcast_axis(s, x, y)

# we can use a similar trick as we do with `indices` where unequal sizes error and we just
# keep the static value. However, axes can be unequal if one of them is `1` so we have to
# fall back to dynamic values in those cases
_combine_length(x::StaticInt{X}, y::StaticInt{Y}) where {X,Y} = static(_combine_length(X, Y))
_combine_length(x::StaticInt{X}, ::Int) where {X} = x
_combine_length(x::StaticInt{1}, y::Int) = y
_combine_length(x::StaticInt{1}, y::StaticInt{1}) = y
_combine_length(x::Int, y::StaticInt{Y}) where {Y} = y
_combine_length(x::Int, y::StaticInt{1}) = x
@inline function _combine_length(x::Int, y::Int)
    if x === y
        return x
    elseif y === 1
        return x
    elseif x === 1
        return y
    else
        _dimerr(x, y)
    end
end

function _dimerr(@nospecialize(x), @nospecialize(y))
    throw(DimensionMismatch("axes could not be broadcast to a common size; " *
                            "got axes with lengths $(x) and $(y)"))
end


text:
module GLExperiments

include("FirstWindow.jl")

end


text:
function _check_modality_name(s::String)
    if s === "anat"
        return nothing
    elseif s === "beh"
        return nothing
    elseif s === "dwi"
        return nothing
    elseif s === "fmap"
        return nothing
    elseif s === "func"
        return nothing
    elseif s === "perf"
        return nothing
    elseif s === "pet"
        return nothing
    elseif s === "eeg"
        return nothing
    elseif s === "ieeg"
        return nothing
    elseif s === "meg"
        return nothing
    else
        error("$s is not a valid modality directory name.")
    end
end

function _check_data_name(s::String)
    if s === "code"
        return nothing
    elseif s === "rawdata"
        return nothing
    elseif s === "sourcedata"
        return nothing
    elseif s === "stimuli"
        return nothing
    elseif s === "derivatives"
        return nothing
    elseif s === "phenotype"
        return nothing
    else
        error("$s is not a valid associated data directory name.")
    end
end

## load
function load(f, ::Type{String}, error_on_missing::Bool=false)
    if isfile(f)
        return read(f, String)
    elseif error_on_missing
        error("$f is not a file")
    else
        return ""
    end
end
function load(f, ::Type{OrderedDict{Symbol,Any}}, error_on_missing::Bool=false)
    if isfile(f)
        return OrderedDict{Symbol,Any}(JSON3.read(read(f, String)))
    elseif error_on_missing
        error("$f is not a file")
    else
        return OrderedDict{Symbol,Any}()
    end
end
function load(f, ::Type{DataFrame}, error_on_missing::Bool=false)
     if isfile(f)
        return DataFrame(CSV.File(f, delim="\t"))
    elseif error_on_missing
        error("$f is not a file")
    else
        return DataFrame()
    end
end
function load(p, ::Type{StudyMetadata})
    StudyMetadata(
        load(joinpath(p, "README"), String),
        load(joinpath(p, "LICENSE"), String),
        load(joinpath(p, "CHANGES"), String),
        load(joinpath(p, "dataset_description.json"), DataDescription),
        Spreadsheet(
            load(joinpath(p, "participants.json"), OrderedDict{Symbol,Any}),
            load(joinpath(p, "participants.tsv"), DataFrame)
        )
    )
end
function load(f, ::Type{DataDescription}, error_on_missing::Bool=false)
    if isfile(f)
        d = JSON3.read(read(f, String))
        return DataDescription(
            get(d, :Name, ""),
            get(d, :BIDSVersion, ""),
            get(d, :HEDVersion, ""),
            get(d, :License, ""),
            get(d, :Authors, [""]),
            get(d, :Acknowledgements, ""),
            get(d, :HowToAcknowledge, ""),
            get(d, :Funding, ""),
            get(d, :EthicsApprovals, [""]),
            get(d, :ReferencesAndLinks, [""]),
            get(d, :DatasetDOI, "")
        )
    elseif error_on_missing
        error("$f is not a file")
    else
        return DataDescription()
    end
end

## save
function save(f, data::DataDescription)
    open(f, "w") do io
        JSON3.pretty(io, OrderedDict{Symbol,Any}(
            :Name => data.Name,
            :BIDSVersion => data.BIDSVersion,
            :HEDVersion => data.HEDVersion.
            :License => data.License,
            :Authors => data.Authors,
            :Acknowledgements => data.Acknowledgements,
            :HowToAcknowledge => data.HowToAcknowledge,
            :Funding => data.Funding,
            :EthicsApprovals => data.EthicsApprovals,
            :ReferencesAndLinks => data.ReferencesAndLinks,
            :DatasetDOI => data.DatasetDOI
        ))
    end
end
save(f, d::AbstractString) = write(f, d)
save(f, df::DataFrame) = CSV.write(df, delim="\t")
function save(f, d::OrderedDict{Symbol,Any})
    open(sf * ".json", "w") do io
        JSON3.pretty(io, d)
    end
end
function save(p, d::StudyMetadata)
    save(joinpath(p, "README"), d.README),
    save(joinpath(p, "LICENSE"), d.LICENSE),
    save(joinpath(p, "CHANGES"), d.CHANGES),
    save(joinpath(p, "dataset_description.json"), d.dataset_description),
    save(joinpath(p, "participants.json"), d.participants.dict)
    save(joinpath(p, "participants.tsv"), d.participants.data)
end

## metadata
Metadata.metadata(p::StudyPath) = load(String(p), StudyMetadata)
function Metadata.metadata(p::SubjectPath)
    b = basename(dirname(p)) * "_scans"
    rp = repath(p)
    return Spreadsheet(
        load(joinpath(rp, b * ".json"), OrderedDict{Symbol,Any}),
        load(joinpath(rp, b * ".tsv"), DataFrame)
    )
end
function Metadata.metadata(p::SessionPath)
    b = String(basename(dirname(p))) * "_" * String(basename(p)) * "_scans"
    rp = repath(p)
    return Spreadsheet(
        load(joinpath(rp, b * ".json"), OrderedDict{Symbol,Any}),
        load(joinpath(rp, b * ".tsv"), DataFrame)
    )
end

function Metadata.metadata(p::FilePath)
    load(joinpath(repath(dirname(p)), splitext(basename(p))[1] * ".json"), OrderedDict{Symbol,Any})
end


text:
# categorical contains specific exceptions
# if we cannot map to categorical, we map to linear instead

immutable HybridDiscretizer{N<:Real, D<:Integer} <: AbstractDiscretizer{N,D}
    cat :: CategoricalDiscretizer{N,D}
    lin :: LinearDiscretizer{N,D}
end

"""
a hybrid discretizers that maps a special missing value indicator (typically Inf or NaN)
to a discrete bin, but otherwise is a linear discretizer
"""
function datalineardiscretizer{D<:Integer}(binedges::Vector{Float64}, ::Type{D}=Int;
    missing_key::Float64=Inf,
    force_outliers_to_closest::Bool=DEFAULT_LIN_DISC_FORCE_OUTLIERS_TO_CLOSEST,
    )

    categorical = CategoricalDiscretizer(@compat Dict{Float64, Int}(missing_key=>1))
    linear = LinearDiscretizer(binedges, D, force_outliers_to_closest=force_outliers_to_closest)
    HybridDiscretizer{Float64,D}(categorical, linear)
end

function supports_encoding{N<:Real,D<:Integer}(disc::HybridDiscretizer{N,D}, x::N)
    if haskey(disc.cat.n2d, x) ||
        (disc.lin.force_outliers_to_closest && !isnan(x))
        return true
    else
        return disc.lin.binedges[1] ≤ x ≤ disc.lin.binedges[end]
    end
end
supports_decoding{N<:Real,D<:Integer}(disc::HybridDiscretizer{N,D}, d::D) = 1 ≤ d ≤ nlabels(disc)

function encode{N,D<:Integer}(disc::HybridDiscretizer{N,D}, x::N)

    if haskey(disc.cat.n2d, x)
        return encode(disc.cat, x) + disc.lin.nbins
    end

    convert(D, encode(disc.lin, x))
end
encode{N,D}(disc::HybridDiscretizer{N,D}, x) = encode(disc, convert(N, x))::D
function encode{N,D<:Integer}(disc::HybridDiscretizer{N,D}, data::AbstractArray)
    arr = Array(D, length(data))
    for (i,x) in enumerate(data)
        arr[i] = encode(disc, x)
    end
    reshape(arr, size(data))
end

function decode{N<:Real,D<:Integer}(disc::HybridDiscretizer{N,D}, d::D, method::AbstractSampleMethod=SAMPLE_UNIFORM)
    if d ≤ disc.lin.nbins
        retval = decode(disc.lin, d, method)
    else
        retval = decode(disc.cat,  d - disc.lin.nbins)
    end
    convert(N, retval)
end
decode{N<:Real,D<:Integer,E<:Integer}(disc::HybridDiscretizer{N,D}, d::E, method::AbstractSampleMethod=SAMPLE_UNIFORM) =
    decode(disc, convert(D, d), method)

function decode{N,D<:Integer}(disc::HybridDiscretizer{N,D}, data::AbstractArray{D}, ::AbstractSampleMethod=SAMPLE_UNIFORM)
    arr = Array(N, length(data))
    for (i,d) in enumerate(data)
        arr[i] = decode(disc, d)
    end
    reshape(arr, size(data))
end

Base.max(disc::HybridDiscretizer) = Base.max(disc.lin)
Base.min(disc::HybridDiscretizer) = Base.min(disc.lin)
extrema{N,D}(disc::HybridDiscretizer{N,D}) = extrema(disc.lin)
extrema{N<:AbstractFloat,D}(disc::HybridDiscretizer{N,D}, d::D) = extrema(disc.lin, d)
extrema{N<:Integer,D}(disc::HybridDiscretizer{N,D}, d::D) = extrema(disc.lin, d)
totalwidth(disc::HybridDiscretizer) = totalwidth(disc.lin)

nlabels(disc::HybridDiscretizer) = disc.lin.nbins + nlabels(disc.cat)
bincenters{N<:Real,D}(disc::HybridDiscretizer{N,D}) = bincenters(disc.lin)
binwidth{N<:AbstractFloat,D}(disc::HybridDiscretizer{N,D}, d::D) = binwidth(disc.lin, d)
binwidths{N<:AbstractFloat,D}(disc::HybridDiscretizer{N,D}) = binwidths(disc.lin)


text:
# Query Date Format = yyyy-mm-dd
# Freq = D
# Date Column Format = yyyy-mm-dd
# Header Row = 1
# Rows to Exclude = 0

#using Base.Dates, Requests, JuliaDB, CSV, FileIO, DataFrames, TextParse, HTTP, SQLite, DataStreams

const FXALL = [:AUD	:BGN	:BRL	:CAD	:CHF	:CNY	:CYP	:CZK	:DKK	:EEK	:GBP	:GRD	:HKD	:HRK	:HUF	:IDR	:ILS	:INR	:ISK	:JPY	:KRW	:LTL	:LVL	:MTL	:MXN	:MYR	:NOK	:NZD	:PHP	:PLN	:RON	:RUB	:SEK	:SGD	:SIT	:SKK	:THB	:TRY	:USD	:ZAR    :FX]
const FXsimbolo = [:USD :JPY :GBP :CHF :HKD]

const SVENSSON = [:BETA0, :BETA1, :BETA2, :BETA3, :TAU1, :TAU2]

const EURIBOR = [:EURIBOR1M, :EURIBOR3M, :EURIBOR6M, :EURIBOR9M, :EURIBOR1Y]

const MMIsimbolo = [:FD, :FME, :OPR, :EONIA]
const MMIcodigo = ["143.FM.D.U2.EUR.4F.KR.DFR.LEV", "143.FM.D.U2.EUR.4F.KR.MLFR.LEV", "143.FM.D.U2.EUR.4F.KR.MRR_FR.LEV", "198.EON.D.EONIA_TO.RATE"]

const RFRsimbolo = [:BE, :DE, :FR, :IT, :NL, :SP]
const RFRurl = ["RFR_Belgium.csv", "RFR_Germany.csv", "RFR_France.csv", "RFR_Italy.csv", "RFR_Netherlands.csv", "RFR_Spain.csv"]

const quandlheaders = [:ticker, :date, :open, :high, :low, :close, :volume, :ex_dividend, :split_ratio, :adj_open, :adj_high, :adj_low, :adj_close, :adj_volume]

function quandl(simbolos, inicio = "0001-01-01", fim = "9999-01-01")
    quandltURL = "https://www.quandl.com/api/v3/datatables/WIKI/PRICES.csv?ticker=$(simbolos)&date.gte=$(inicio)&date.lte=$(fim)&api_key=DQw1bXYU8NieGQKTYnJy"
    fonte = conformar(quandltURL)
    resp = download(fonte)
    tabela = JuliaDB.table(resp[1]..., names = map(Symbol,resp[2]), pkey = (:ticker,:date))
end

function conformar(a::String)
    if a[1:4] == "http"
        return  IOBuffer(HTTP.get(a).body)
    else
        return a
    end
end

# Download/Ler de URL Remoto/Ficheiro dados em formato CSV usando TextParse
#function download(fonte, separador=','; saltar=0, datatipo="y-m-d", header=true, na = [""])
#    if na == [""]
#        return TextParse.csvread(fonte, separador, skiplines_begin = saltar,header_exists=header)
#    else
#        return TextParse.csvread(
#            fonte,
#            skiplines_begin = saltar,
#            header_exists = header,
#            colparsers = [TextParse.DateTimeToken(Date, DateFormat(datatipo)), TextParse.NAToken(TextParse.Numeric(Float64), nastrings = na)])
#    end
#end

# Ingerir dados obtidos por TextParse para uma tabela de JuliaDB
#    textp -  TextParse
#    colindex - Numeric (posição da coluna que será indexada)
#    colseries - Numeric (posição da outra coluna a incluir)
# Assume apenas 2 colunas, sendo a coluna de datas (colindex) a que se irá indexar colindex.

function juliadb(textp, colindexes, nomes)
   return  table(textp[1]..., names = nomes, pkey = colindexes)
end

# colindexes e.g. (:col1, :col2, :col3, :col4)
function juliadb(textp, colindexes)
    return  table(textp[1]..., names = map(Symbol,textp[2]), pkey = colindexes)
 end

 function juliadb(textp)
    return  table(textp[1]..., names = map(Symbol,textp[2]))
 end

# CSV: Remote URL -> in-memory JuliaDB, IndexedTables
# using JuliaDB
function bceJDB(myURL::String, nomes::Array{Symbol,1})
    newtable = TextParse.csvread(IOBuffer(HTTP.get(myURL).body), skiplines_begin=5, header_exists=false)
    tabela =  table(newtable[1][1], newtable[1][2], names = nomes, pkey = (nomes[1]))
    return tabela
end

# CSV: Remote URL -> in-memory JuliaDB(|>table), DataFrames(|>DataFrame), StatsModels, StatsPlots, IndexedTables,...
# using CSVFiles, FileIO
function bceCSVFiles(myURL::String, nomes::Array{Symbol,1})
    tabela = FileIO.load(Stream(format"CSV", IOBuffer(HTTP.get(myURL).body)), skiplines_begin=5,header_exists=false, colnames = nomes) |> table
    return tabela
end

# CSV: Remote URL -> in-memory SQLite, ODBC, DataFrames
# using CSV
# CSV.read(fullpath::Union{AbstractString,IO}, sink::Type{T}=DataFrame, args...; kwargs...) => typeof(sink)
function bceCSV(myURL::String, nomes::Array{Symbol,1})
    tabela = CSV.read(IOBuffer(HTTP.get(myURL).body), datarow = 6, header = nomes, missingstring=" ")
    return tabela
end

function obter(series; inicio="", fim="")

    tabela = JuliaDB.table([1])

    if series == :NSS
        nssURL = "http://sdw.ecb.europa.eu/quickviewexport.do?trans=N&updated=&start=$(inicio)&end=$(fim)&SERIES_KEY=165.YC.B.U2.EUR.4F.G_N_A.SV_C_YM."
        print("Modelo Svensson >")
        for simbolo in SVENSSON
            fonte = conformar(nssURL*"$(simbolo)&format=csvdata")
            resp = download(fonte, saltar = 5, header = false)
            tabelaN = juliadb(resp, (:Data), [:Data, simbolo])
            if simbolo == :BETA0
                tabela = tabelaN
            else
                tabela = join(tabela, tabelaN)
            end
            print("\t",simbolo)
        end
        println("\n",tabela,"\n")
        return tabela

    elseif series == :MM
        mmiURL = "http://sdw.ecb.europa.eu/quickviewexport.do?trans=N&start=$(inicio)&end=$(fim)&SERIES_KEY="
        println("\n")
        for i = 1:4
            print(MMIsimbolo[i], "\t")
            fonte = conformar(mmiURL*"$(MMIcodigo[i])&type=csv")
            resp = download(fonte, saltar = 5, header = false)
            tabelaN = juliadb(resp, (:Data), [:Data, MMIsimbolo[i]])
            if i == 1
                tabela = tabelaN
            else
                tabela = join(tabela, tabelaN)
            end
        end
        for simbolo in EURIBOR
            print(simbolo,"\t")
            euriborURL = "http://webstat.banque-france.fr/en/quickviewexport.do?SERIES_KEY=213.FM.D.U2.EUR.FR2.MM.$(simbolo)D_.HSTA&type=csv"
            fonte = conformar(euriborURL)
            resp = download(fonte, saltar=6, datatipo = "d-m-y", header = false, na=["-"])
            tabelaN = juliadb(resp, (:Data), [:Data, simbolo])
            tabela = join(tabela, tabelaN)
        end
        println("\n",tabela,"\n")
        return tabela

    elseif series == :RFR
        rfrURL = "http://www.repofundsrate.com/csv/"
        print("Taxas Fundos Repo EUR >")
        for i = 1:6
            fonte =  conformar(rfrURL*RFRurl[i])
            resp = download(fonte, saltar =0, header=true)
            tabelaN = juliadb(resp, (:Data), [:Data, :Description, RFRsimbolo[i], :IndexVol, :InicialVol])
            tabelaN = select(tabelaN, All(:Data, RFRsimbolo[i]))
            if i == 1
                tabela = tabelaN
            else
                tabela = join(tabela, tabelaN)
            end
            print("\t",RFRsimbolo[i])
        end
        println("\n",tabela, "\n")
        return tabela

    else series in FXALL
        if series == :FX
            fxURL = "http://sdw.ecb.europa.eu/quickviewexport.do?start=$(inicio)&end=$(fim)&SERIES_KEY=120.EXR.D."
            for simbolo in FXsimbolo
                print(simbolo,"\t")
                fonte = conformar(fxURL*"$(simbolo).EUR.SP00.A&type=csv")
                resp = download(fonte, saltar = 5, header = false,  na = ["-"])
                tabelaN = juliadb(resp, (:Data), [:Data, simbolo])
                if simbolo == :USD
                    tabela = tabelaN
                else
                    tabela = join(tabela, tabelaN)
                end
            end
        else
            fxURL = "http://sdw.ecb.europa.eu/quickviewexport.do?start=$(inicio)&end=$(fim)&SERIES_KEY=120.EXR.D.$(series).EUR.SP00.A&type=csv"
            fonte = conformar(fxURL)
            resp = download(fonte, saltar = 5, header = false,  na = ["-"])
            tabela = juliadb(resp, (:Data), [:Data, series])
        end
        println("\n\n",tabela,"\n")
        return tabela
    end
end

# top é o header
function obter(url::String; na =[""], saltar = 0, top = true, data = "y-m-d")
    fonte = conformar(url)
    resp = download(fonte, saltar = saltar, datatipo= data, header = top, na = na)
    tabela = juliadb(resp)
    println("\n",tabela)
    return tabela
end

max_stars_repo_name: iscalprog/Iscalc

max_issues_repo_name: iscalprog/Iscalc

max_forks_repo_name: iscalprog/Iscalc


text:
"""
    sform_code(x) -> CoordinateSystem

Code describing the orientation of the image.

Should only be one of following (although others are allowed):

* UnkownSpace
* AnatomicalSpace
* TalairachSpace
* MNI152Space
"""
sform_code(x::Any) = getter(x, "sform_code", CoordinateSystem, UnkownSpace)

"""
    sform_code!(x, val)

Set the `sform` coordinate space of `x` to `val`.
"""
sform_code!(x::Any, val::CoordinateSystem) = setter!(x, "sform_code", val, CoordinateSystem)

"""
    qform_code(x) -> CoordinateSystem

Code describing the orientation of the image in its acquired space.

Should only be one of the following (although others are allowed):

* UnkownSpace
* ScannerSpace
"""
qform_code(x::Any) = getter(x, "sform_code", CoordinateSystem, UnkownSpace)

"""
    qform_code!(x, val)

Set the `qfrom` coordinate space of `x` to `val`.
"""
qform_code!(x::Any, val::CoordinateSystem) = setter!(x, "qform_code", val, CoordinateSystem)

"""
    qform(x) -> MMatrix{4,4,Float64,16}
"""
qform(x::Any) = getter(x, "qform", MMatrix{4,4,Float64,16}, i->default_affinemat(i))
qform!(x::Any, val::AbstractMatrix) = setter!(x, "qform", val, MMatrix{4,4,Float64,16})

"""
    sform(x) -> MMatrix{4,4,Float64,16}

The 4th column of the matrix is the offset of the affine matrix.
This is primarily included for the purpose of compatibility with DICOM formats, where the
"Image Position" stores the coordinates of the center of the first voxel
(see the [DICOM standard](http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html#sect_C.7.6.2.1.1) for more details;
Note, these values should be in interpreted as 'mm').
"""
sform(x::Any) = getter(x, "sform", MMatrix{4,4,Float64,16}, i-> affine_map(i))

sform!(x::Any, val::AbstractMatrix) = setter!(x, "sform", val, i -> affine_map(i))

function default_affinemat(x::Any)
    if sform_code(x) === UnkownSpace
        if qform_code(x) === UnkownSpace
            return _default_affinemat(x)
        else
            return qform(x)
        end
    else
        return sform(x)
    end
end

"""
    affine_map(x) -> MMatrix{4,4,Float64,16}

Returns affine matrix. For an instance that returns `spacedirections` this is
the corresponding tuple converted to an array.
"""
function affine_map(x)
    return _spacedirection_to_rotation(spacedirections(x)) ∘ _pixelspacing_to_linearmap(pixelspacing(x))
end

function _pixelspacing_to_linearmap(ps::NTuple{2,T}) where {T}
    return @inbounds LinearMap(SVector(Float64(ps[1]), Float64(ps[2]), 0.0))
end

function _pixelspacing_to_linearmap(ps::NTuple{3,T}) where {T}
    return @inbounds LinearMap(SVector(Float64(ps[1]), Float64(ps[2]), Float64(ps[3])))
end

function _spacedirection_to_rotation(::Type{R}, sd::NTuple{2,NTuple{2,T}}) where {R,T}
    return @inbounds R(SMatrix{3,3,Float64,9}(
        sd[1][1], sd[2][1], 0,
        sd[1][2], sd[2][2], 0,
               0,        0, 0))
end

function _spacedirection_to_rotation(::Type{R}, sd::NTuple{3,NTuple{3,T}}) where {R,T}
    return @inbounds R(SMatrix{3,3,Float64,9}(
        sd[1][1], sd[2][1], sd[3][1],
        sd[1][2], sd[2][2], sd[3][2],
        sd[1][3], sd[2][3], sd[3][3]))
end


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

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 iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
        el
    end
end

# ╔═╡ da1d65a0-ec42-11ea-0141-334c9eeeb035
begin
	import ImageMagick
	using PlutoUI
	using Images
end

# ╔═╡ 5ef51c3a-70a7-11eb-2023-31113399a57f
html"""

Section 1.2

 Intro to Abstractions

Lecture Video

"""

# ╔═╡ 60ae819a-70a7-11eb-31d4-750c7f5dc6ca
PlutoUI.TableOfContents(aside=true)

# ╔═╡ 792c6a62-ec41-11ea-01f3-73e7eee23cc7
md"""
#### Intializing packages

_When running this notebook for the first time, this could take up to 15 minutes. Hang in there!_
"""

# ╔═╡ ef1bfa16-70ea-11eb-189c-a54db292cd6f
md"
## Introduction

The goal of this section is to introduce you to the notion of abstraction. You can think of abstraction as an opposite to specialization. We will illustrate this by looking at the following example.

### What is _one_?

Before we get lost talking about the foundations of number theory, I will present you with a few examples that represent one to me.
"

# ╔═╡ 6fcac482-70ee-11eb-0b80-ff41c708053b
md"Each of the items in this list is a specific, or **_specialized_** representation of _one_:
1. as an integer
1. as a float
1. as a string
1. as a rational number
1. as a cute picture
1. as an 2x2 identity matrix
1. as a singular dog

Of course, these are just a few examples of _one_. People have been representing _one_ for ages in different langauges, scripts, artistic expression, etc.

The difference between these ones to me is clear. In fact, I just articulated it to you. Now, let's turn to how a computer sees _one_ differently based on what I type.
"

# ╔═╡ 9ebc079a-70f0-11eb-07d9-f9e80f3f4584
md"So to a computer, all of these are different types."

# ╔═╡ 15f7f90a-70f0-11eb-0d41-63677e4023f4
md"### What is a collection of _one_s?

Now, I want to make a collection of ones for some reason. Below is a way for you to experiment building this collection with different _one_s. As you do this experiment, I want you to look at what stays in the same in the Julia output, and what doesn't."

# ╔═╡ f6886d90-70ed-11eb-07c4-471ee267e7c1
md"""
Before we even look at the output, I am amazed that this code even ran. Are you telling me that the computer doesn't care which _one_ I am using in my array?

Yes! That's exactly what abstraction is. By stepping back, we can now think and operate at a level that doesn't care about which _specific_ one I am using. This is what we mean by **abstraction is the opposite of specialization**.

The information that Julia gives back is quite informative. Here is an example of the first line of the output for a few different types:

```
array = 3x4 Array{Int64, 2}
array = 3x4 Array{Float64, 2}
array = 3x4 Array{Rational{Int64}, 2}
```

Notice that for all of these, we have the same `3x4 Array{***, 2}`.
"""

# ╔═╡ 3c1a3cf8-70f8-11eb-3c18-375207f321eb
md"""
## First Taste of Abstraction

Now, I want to do something to a collection of ones, that doesn't care about which one I'm using. So I'm going to write a function that takes in my collection, and add a corgi whereever I desire.
"""

# ╔═╡ 19f4ddb0-ec44-11ea-20b9-5d97fb2b1cf4
function insert(new, A, i, j)
	B = copy(A)
	B[i,j] = new
	return B
end

# ╔═╡ 424f5f10-ec44-11ea-076d-f3cba4435e0c
begin
	A = fill(0, 3, 4)
	md"""
	$(@bind i Slider(1: size(A,1), show_value=true))
	$(@bind j Slider(1: size(A,2), show_value=true))
	"""
end

# ╔═╡ 71ac08ea-7145-11eb-237d-5506adfb9533
begin
	one_number_array = fill(1,3,4)
	insert(5, one_number_array, i, j)
end

# ╔═╡ ee43d808-70fa-11eb-0cc6-337279f41494
md"This is still amazing. I wrote one function that just cares about how to insert an object into an array, without knowing anything about what's inside, and it worked for two completely different arrays, _collections of ones of **any kind**_."

# ╔═╡ 263a8a0a-70ee-11eb-236d-c941ba63dff3
md"
## Conclusion
The key idea here is that a computer language should allow you to do operations that make sense. Often times, an operation can make sense for many different objects. So we can abstract away the specifics of the object in our implementation. It should let you step back from there.
"

# ╔═╡ 52461588-ea1a-4e7d-aec2-3de388d31656
md"""
## Appendix
"""

# ╔═╡ 1a2a9000-ec43-11ea-3f39-8312ea286a92
begin
	oneimage = load(download("https://gallery.yopriceville.com/var/albums/Free-Clipart-Pictures/Decorative-Numbers/Cute_Number_One_PNG_Clipart_Image.png?m=1437447301"))
	corgi = load(download("https://i.barkpost.com/wp-content/uploads/2015/01/corgi2.jpg?q=70&fit=crop&crop=entropy&w=808&h=500"))
	nothing
end

# ╔═╡ 0504ac94-70ee-11eb-1c4e-977d9e7d35c9
one = [
	1,
	1.0,
	"one",
	1//1,
	oneimage,
	[1 0; 0 1],
	corgi,
]

# ╔═╡ 0b1668ba-ec42-11ea-3e50-ed97c5b17ced
computer_ones = typeof.(one)

# ╔═╡ b2239b96-70ef-11eb-0b85-21ecab25dc9f
begin
	one_keys = ["1", "1.0", "one", "1//1", "Cute One", "2x2 Identity", "One Corgi"]
	selections = one_keys .=> one_keys
	lookup_element = Dict(one_keys .=> one)
	md"$(@bind element_key Select(selections))"
end

# ╔═╡ 4251f668-70aa-11eb-3d89-35f8d53b7d9b
# your chosen one
element = lookup_element[element_key]

# ╔═╡ f1568d10-ec41-11ea-3dd2-a9cb273ce5b8
# its type
typeof(element)

# ╔═╡ ab02d850-ec41-11ea-10b2-a1b600b12658
# a 3x4 array of this one.
array = fill(element,3,4)

# ╔═╡ 5363a400-ec44-11ea-284e-d13a8872551c
begin
	one_image_array = fill(oneimage,3,4)
	insert(corgi, one_image_array, i, j)
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
ImageMagick = "~1.2.1"
Images = "~0.24.1"
PlutoUI = "~0.7.9"
"""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "9f46deb4d4ee4494ffb5a40a27a2aced67bdd838"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.4"

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

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

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

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

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

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

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

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

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

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "2c1cf4df419938ece72de17f368a021ee162762e"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.0"

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

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

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

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

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

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

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

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

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

[[ImageMagick_jll]]
deps = ["JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "1c0a2295cca535fabaf2029062912591e9b61987"
uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
version = "6.9.10-12+3"

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

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

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

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

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

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

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

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

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

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

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

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

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

[[IrrationalConstants]]
git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.0"

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

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

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

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

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

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

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

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

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

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

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

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

[[LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.3"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.2"

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

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

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

[[PlutoUI]]
deps = ["Base64", "Dates", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "Suppressor"]
git-tree-sha1 = "44e225d5837e2a2345e69a1d1e01ac2443ff9fcb"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.9"

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

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

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

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

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

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

[[Ratios]]
deps = ["Requires"]
git-tree-sha1 = "7dff99fbc740e2f8228c6878e2aad6d7c2678098"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.1"

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

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

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

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

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

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

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

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

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

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

[[SpecialFunctions]]
deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"]
git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.1"

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

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

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.12"

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

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

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

[[Suppressor]]
git-tree-sha1 = "a819d77f31f83e5792a76081eee1ea6342ab8787"
uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
version = "0.2.0"

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

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

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

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

[[TiffImages]]
deps = ["ColorTypes", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "OffsetArrays", "OrderedCollections", "PkgVersion", "ProgressMeter"]
git-tree-sha1 = "632a8d4dbbad6627a4d2d21b1c6ebcaeebb1e1ed"
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
version = "0.4.2"

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "52c5f816857bfb3291c7d25420b1f4aca0a74d18"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.0"

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

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

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

[[WoodburyMatrices]]
deps = ["LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "59e2ad8fd1591ea019a5259bd012d7aee15f995c"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "0.5.3"

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─5ef51c3a-70a7-11eb-2023-31113399a57f
# ╟─60ae819a-70a7-11eb-31d4-750c7f5dc6ca
# ╟─792c6a62-ec41-11ea-01f3-73e7eee23cc7
# ╠═da1d65a0-ec42-11ea-0141-334c9eeeb035
# ╟─ef1bfa16-70ea-11eb-189c-a54db292cd6f
# ╠═0504ac94-70ee-11eb-1c4e-977d9e7d35c9
# ╟─6fcac482-70ee-11eb-0b80-ff41c708053b
# ╠═0b1668ba-ec42-11ea-3e50-ed97c5b17ced
# ╟─9ebc079a-70f0-11eb-07d9-f9e80f3f4584
# ╟─15f7f90a-70f0-11eb-0d41-63677e4023f4
# ╠═b2239b96-70ef-11eb-0b85-21ecab25dc9f
# ╠═4251f668-70aa-11eb-3d89-35f8d53b7d9b
# ╠═f1568d10-ec41-11ea-3dd2-a9cb273ce5b8
# ╠═ab02d850-ec41-11ea-10b2-a1b600b12658
# ╟─f6886d90-70ed-11eb-07c4-471ee267e7c1
# ╟─3c1a3cf8-70f8-11eb-3c18-375207f321eb
# ╠═19f4ddb0-ec44-11ea-20b9-5d97fb2b1cf4
# ╠═424f5f10-ec44-11ea-076d-f3cba4435e0c
# ╠═5363a400-ec44-11ea-284e-d13a8872551c
# ╠═71ac08ea-7145-11eb-237d-5506adfb9533
# ╟─ee43d808-70fa-11eb-0cc6-337279f41494
# ╟─263a8a0a-70ee-11eb-236d-c941ba63dff3
# ╟─52461588-ea1a-4e7d-aec2-3de388d31656
# ╠═1a2a9000-ec43-11ea-3f39-8312ea286a92
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
module MyPackageTests
using ReTest
include("tests.jl")
end


text:
using HTTP, JSON

# to override with your Etherscan API key
API_token = ""
# first block to start from
first_block = 6290000
log_result_topic = "0x8dd0b145385d04711e29558ceab40b456976a2b9a7d648cc1bcd416161bf97b9"
contract_address = "0xA52e014B3f5Cc48287c2D483A3E026C32cc76E6d"

# gets logs from a given block
function get_logs(from_block; to_block="latest")
    get_logs_url = "https://api.etherscan.io/api?module=logs&action=getLogs"
    url = "$get_logs_url&fromBlock=$from_block&toBlock=$to_block&address=$contract_address&topic0=$log_result_topic&apikey=$API_token"
    response = HTTP.request("GET", url)
    body = String(response.body)
    JSON.parse(body)["result"]
end

# pulls batches of logs and concatenates to a resulting array
function get_logs_batch(from_block, batch_count)
    # for stroing event logs from API call
    log_array = []
    for i in 1:batch_count
        print(i,", ")
        logs = get_logs(from_block)
        # going to the next batch
        from_block = parse(Int64, logs[end]["blockNumber"]) + 1
        println(from_block)
        log_array = vcat(log_array, logs)
    end
    return log_array
end

# extracts Oracle result number given an array of logs
function extract_oracle_results(log_array)
    # for storing Oracle results
    result_array = Int64[]
    for i in 1:length(log_array)
        # results stored in hex at index 129:130
        result = parse(Int64, log_array[i]["data"][129:130], base = 16)
        push!(result_array, result)
    end
    # dropping failed transactions (result == 0)
    filter!(x->x !== 0, result_array)
end

function main()
    log_array = get_logs_batch(first_block, 400)
    result_array = extract_oracle_results(log_array)
    print(result_array)
end

main()

max_stars_repo_name: tuckertranslator/Etheroll

max_issues_repo_name: tuckertranslator/Etheroll

max_forks_repo_name: tuckertranslator/Etheroll