text:
abstract type BOOL end
struct TRUE <: BOOL end
struct FALSE <: BOOL end

(t::TRUE)(x::BOOL, y::BOOL) = x
(f::FALSE)(x::BOOL, y::BOOL) = y

show(io::IO, ::TRUE) = print(io, "true")
show(io::IO, ::FALSE) = print(io, "false")


text:
#=
Implements the Kalman filter for a linear Gaussian state space model.

@author : Spencer Lyon <spencer.lyon@nyu.edu>

@date: 2014-07-29

References
----------

http://quant-econ.net/jl/kalman.html

TODO: Do docstrings here after implementing LinerStateSpace
=#

type Kalman
    A
    G
    Q
    R
    k
    n
    cur_x_hat
    cur_sigma
end

# Initializes current mean and cov to zeros
function Kalman(A, G, Q, R)
    k = size(G, 1)
    n = size(G, 2)
    xhat = n == 1 ? zero(eltype(A)) : zeros(n)
    Sigma = n == 1 ? zero(eltype(A)) : zeros(n, n)
    return Kalman(A, G, Q, R, k, n, xhat, Sigma)
end

function set_state!(k::Kalman, x_hat, Sigma)
    k.cur_x_hat = x_hat
    k.cur_sigma = Sigma
    Void
end

function prior_to_filtered!(k::Kalman, y)
    # simplify notation
    G, R = k.G, k.R
    x_hat, Sigma = k.cur_x_hat, k.cur_sigma

    # and then update
    if k.k > 1
        reshape(y, k.k, 1)
    end
    A = Sigma * G'
    B = G * Sigma' * G' + R
    M = A * inv(B)
    k.cur_x_hat = x_hat + M * (y - G * x_hat)
    k.cur_sigma = Sigma - M * G * Sigma
    Void
end

function filtered_to_forecast!(k::Kalman)
    # simplify notation
    A, Q = k.A, k.Q
    x_hat, Sigma = k.cur_x_hat, k.cur_sigma

    # and then update
    k.cur_x_hat = A * x_hat
    k.cur_sigma = A * Sigma * A' + Q
    Void
end

function update!(k::Kalman, y)
    prior_to_filtered!(k, y)
    filtered_to_forecast!(k)
    Void
end

function stationary_values(k::Kalman)
    # simplify notation
    A, Q, G, R = k.A, k.Q, k.G, k.R

    # solve Riccati equation, obtain Kalman gain
    Sigma_inf = solve_discrete_riccati(A', G', Q, R)
    K_inf = A * Sigma_inf * G' * inv(G * Sigma_inf * G' + R)
    return Sigma_inf, K_inf
end

ext: jl


text:
using Pkg

Pkg.add("LightGraphs")
Pkg.add("SimpleWeightedGraphs")
Pkg.add("DataStructures")
Pkg.add("BenchmarkTools")

# uncomment if you want to draw the thing
#Pkg.add("GraphPlot")
# note: Cairo can cause errors in Compose and Fontconfig, but running the script again resolves this
#Pkg.add("Cairo")
#Pkg.add("Fontconfig")
#Pkg.add("Compose")
#Pkg.add("Luxor")
#Pkg.add("SimpleTraits")

using LightGraphs
using SimpleWeightedGraphs
using DataStructures
using BenchmarkTools

# uncomment if drawing
#using GraphPlot
#using Cairo
#using Fontconfig
#using Compose
#using Luxor
#using SimpleTraits

# include implemented algorithms
include("primsI.jl") # Aorthi's
include("primsII.jl") # Blain's
include("aStar.jl")
include("helpers.jl")
include("correctnessTest.jl")


text:
using ForwardDiff
using Setfield: Lens, set, get

using ...Bifurcations: maybe_subtract!

"""
Codimension-2 fixed point bifurcation problem wrapper for DifferentialEquations.
"""
struct DiffEqCodim2Problem{
        skind <: StateKind,
        tkind <: TimeKind,
        ASType <: AugmentedSystem,
        P, X, V, W, T, L1, L2,
        } <: Codim2Problem{skind, tkind}
    statekind::skind
    timekind::tkind
    augmented_system::ASType
    de_prob::P
    x0::X
    v0::V
    w0::W
    t0::T
    t_domain::Tuple{T, T}
    param_axis1::L1
    param_axis2::L2
end

StateKind(::Type{<: DiffEqCodim2Problem{skind}}) where skind = skind()
TimeKind(::Type{<: DiffEqCodim2Problem{_sk, tkind}}) where {_sk, tkind} = tkind()

function DiffEqCodim2Problem(
        de_prob,
        param_axis1::Lens,
        param_axis2::Lens,
        t_domain::Tuple;
        x0 = copy(de_prob.u0),
        v0 = copy(x0),  # TODO: fix it. this default won't work
        w0 = 0,
        t0 = SVector(get(de_prob.p, param_axis1),
                     get(de_prob.p, param_axis2)),
        augmented_system = BackReferencingAS(),
        )
    if param_axis1 == param_axis2
        error("""
        Same parameter is specified for `param_axis1` and `param_axis2`:
        param_axis1 = param_axis2 = $(param_axis1)
        """)
    end

    t_domain = (SVector{2, eltype(t0)}(t_domain[1]),
                SVector{2, eltype(t0)}(t_domain[2]))
    return DiffEqCodim2Problem(
        statekind(de_prob), timekind(de_prob),
        augmented_system,
        de_prob, x0, v0, w0, t0, t_domain,
        param_axis1, param_axis2)
end

AugmentedSystem(::Type{<: DiffEqCodim2Problem{_sk, _tk, ASType}}
                ) where {_sk, _tk, ASType} = ASType()

_get_augsys_cache(::BackReferencingAS, prob::DiffEqCodim2Problem) =
    BackReferencingASCache(copy(prob.v0))

# ------------------------------------------------ DiffEqCodim2BifurcationCache

struct DiffEqCodim2BifurcationCache{P, C, AC, F} <: AbstractProblemCache{P}
    prob::P
    augsys_cache::AC
    cfg::C
    residual::F
end

function DiffEqCodim2BifurcationCache(prob::DiffEqCodim2Problem)
    augsys_cache = get_augsys_cache(prob)
    return _DiffEqCodim2BifurcationCache(statekind(prob), prob, augsys_cache)
end

function _DiffEqCodim2BifurcationCache(::MutableState, prob, augsys_cache)
    x = copy(get_u0(prob))
    y = similar(x, length(x) - 1)
    residual = (y, x) -> _residual!(y, x, prob, augsys_cache)
    cfg = ForwardDiff.JacobianConfig(residual, y, x)
    return DiffEqCodim2BifurcationCache(
        prob,
        augsys_cache,
        cfg,
        residual)
end

function _DiffEqCodim2BifurcationCache(::ImmutableState, prob, augsys_cache)
    x = copy(get_u0(prob))
    residual = (x) -> _residual!(nothing, x, prob, augsys_cache)
    cfg = ForwardDiff.JacobianConfig(residual, x)
    return DiffEqCodim2BifurcationCache(
        prob,
        augsys_cache,
        cfg,
        residual)
end

# ------------------------------------------------------ continuation interface

get_prob_cache(prob::DiffEqCodim2Problem) =
    DiffEqCodim2BifurcationCache(prob)

get_u0(prob::DiffEqCodim2Problem) = _get_u0(prob, prob.x0)

function _get_u0(prob::DiffEqCodim2Problem, ::AbstractVector)
    return vcat(prob.x0, as_reals(prob.v0), imag_eigval(prob), prob.t0)
end

function _get_u0(prob::DiffEqCodim2Problem, ::SVector) :: SVector
    return vcat(prob.x0, as_reals(prob.v0), imag_eigval(prob), prob.t0)
end

function _get_u0(prob::DiffEqCodim2Problem, ::Real)
    return SVector(prob.x0, as_reals(prob.v0)...,
                   imag_eigval(prob)..., prob.t0...)
end

imag_eigval(prob) = _imag_eigval(eltype(prob.v0), prob.w0)
_imag_eigval(::Type{T}, ::Real) where {T <: Real} = SVector{0, T}()
_imag_eigval(::Type{<: Complex}, w0::Real) = SVector(w0)

isindomain(u, cache::DiffEqCodim2BifurcationCache) =
    _isindomain(u, cache, eltype(cache.prob.v0))

function _isindomain(u, cache, ::Type{<: Real})
    t = SVector(u[end-1], u[end])
    tmin, tmax = cache.prob.t_domain
    return all(tmin .<= t .<= tmax)
end

function _isindomain(u, cache, ::Type{<: Complex})
    # Constrain to positive angular velocity (w = u[end-2])
    p = SVector(u[end-2], u[end-1], u[end])
    tmin, tmax = cache.prob.t_domain
    pmin = SVector(zero(tmin[1]), tmin[1], tmin[2])
    pmax = SVector(typemax(tmax[1]), tmax[1], tmax[2])
    return all(pmin .<= p .<= pmax)
end

residual!(H, u, cache::DiffEqCodim2BifurcationCache) =
    _residual!(H, u, cache.prob, cache.augsys_cache)

_residual!(H, u, prob, augsys_cache) =
    _residual!(H, u, prob, augsys_cache, statekind(prob))

residual_jacobian!(H, J, u, cache::DiffEqCodim2BifurcationCache) =
    _residual_jacobian!(H, J, u, cache, statekind(cache.prob))

# ------------------------------------------------------------------- utilities

function modified_param!(p::DiffEqCodim2Problem, u)
    t1, t2 = u[end-1:end]
    q0 = p.de_prob.p
    q1 = set(q0, p.param_axis1, t1)
    q2 = set(q1, p.param_axis2, t2)
    return q2
end

# ------------------------------------------------------------------- residual!

eigvec_constraint(v::AbstractArray{T}, ::NormalizingASCache) where {T <: Real} =
    SVector{1, T}(v ⋅ v - 1)

eigvec_constraint(v::AbstractArray{<: Complex{T}}, ::NormalizingASCache) where {T} =
    SVector{2, T}(real(v ⋅ v) - 1, real(v) ⋅ imag(v))

eigvec_constraint(v, augsys_cache::BackReferencingASCache) =
    as_reals(augsys_cache.v ⋅ v - 1)

ds_state(prob::DiffEqCodim2Problem, u::AbstractArray) =
    _ds_state(eltype(prob.v0), u)

function _ds_state(E::Type, u)
    d = dims_from_augsys(length(u), E)
    return @view u[1:d.ds_dim]
end

@generated function _ds_state(::Type{E}, u::SVector{S, T}) where {E, S, T}
    d = dims_from_augsys(S, E)
    values = [:(u[$i]) for i in 1:d.ds_dim]
    quote
        SVector{$(length(values)), $T}($(values...))
    end
end

ds_eigvec(prob::DiffEqCodim2Problem, u::AbstractArray) =
    _ds_eigvec(eltype(prob.v0), u::AbstractArray)

ds_eigvec(::HopfCont, u::AbstractArray) = _ds_eigvec(Complex, u)
ds_eigvec(::SaddleNodeCont, u::AbstractArray) = _ds_eigvec(Real, u)

function _ds_eigvec(::Type{E}, u::AbstractArray) where {E <: Real}
    d = dims_from_augsys(length(u), E)
    return @view u[eigvec_range(d)]
end

function _ds_eigvec(::Type{E}, u::Array{T}) where {E <: Complex, T}
    d = dims_from_augsys(length(u), E)
    # return ComplexView(u, eigvec_range(d))  # TODO: implement
    ofs, r = divrem(d.ds_dim, 2)
    if r != 0 || length(u) ÷ 2 != 0
        # Can't be done without copy?  Maybe this memory layout was a
        # bad idea...
        uc = reinterpret(Complex{T}, u[d.ds_dim .+ (1:d.eigvec_dim)])
        return view(uc, :)
    end
    uc = reinterpret(Complex{T}, u)
    return @view uc[ofs .+ 1:ofs .+ d.ds_dim]
end

function _ds_eigvec_expr(C::Type{<: Real}, indices)
    values = [:(u[$i]) for i in indices]
    return Expr(:call, SVector, values...)
end

function _ds_eigvec_expr(::Type{<: Complex}, indices)
    values = [:(u[$i] + u[$i + 1] * im) for i in indices[1:2:end]]
    return Expr(:call, SVector, values...)
end

# Disambiguation:
_ds_eigvec(T::Type{<: Real}, u::SVector) = _ds_eigvec_svec(T, u)
_ds_eigvec(T::Type{<: Complex}, u::SVector) = _ds_eigvec_svec(T, u)

@generated function _ds_eigvec_svec(::Type{eigvec_eltype}, u::SVector{S, T}
                                    ) where {eigvec_eltype, S, T}
    d = dims_from_augsys(S, eigvec_eltype)
    return _ds_eigvec_expr(eigvec_eltype, eigvec_range(d))
end

ds_eigval(prob::DiffEqCodim2Problem, u::AbstractArray) =
    _ds_eigval(eltype(prob.v0), u::AbstractArray)

_ds_eigval(::Type{<: Real}, ::AbstractArray) = 0

function _ds_eigval(T::Type{<: Complex}, u::AbstractArray)
    d = dims_from_augsys(length(u), T)
    return u[eigval_index(d)] * im
end

state_cond_view(d::VarDims, H) = @view H[1:d.ds_dim]       # f(x)
eigvec_cond_view(d::VarDims, H) = @view H[eigvec_range(d)] # Jv - 1
eigvec_cons_view(d::VarDims, H) = @view H[last(eigvec_range(d)) + 1:end]

J_mul_v!(Jv, fx, x, v::AbstractVector{<: Real}, q, f) =
    ForwardDiff.jacobian!(
        reshape(view(Jv, :), :, 1),
        (fx, d) -> f(fx, (@. x + d[1] * v), q, 0),
        fx,
        SVector(zero(eltype(x))),
        # TODO: setup cache
    )

function J_mul_v!(Jv, fx, x, v::AbstractVector{<: Complex}, q, f)
    vr = real(v)
    vi = imag(v)
    JvT = reshape(view(Jv, :), 2, :)'  # not a view in Julia 0.6
    ForwardDiff.jacobian!(
        JvT,
        (fx, d) -> f(fx, (@. x + d[1] * vr + d[2] * vi), q, 0),
        fx,
        SVector(zero(eltype(x)), zero(eltype(x))),
        # TODO: setup cache
    )
    Jv .= view(JvT', :)
end
# It's a bit tricky to handle complex vector case (see also
# `H2[2:2:end]` below).  Maybe `reinterpret` -compatible memory layout
# was not the best choice?

function _residual!(H, u, prob::DiffEqCodim2Problem,
                    augsys_cache,
                    ::MutableState)
    q = modified_param!(prob, u)

    dim = dims_from_augsys(length(u), contkind(prob))
    H1 = state_cond_view(dim, H)
    H2 = eigvec_cond_view(dim, H)
    H3 = eigvec_cons_view(dim, H)
    x = ds_state(prob, u)
    v = ds_eigvec(prob, u)
    iw = ds_eigval(prob, u)

    # TODO: don't allocate x + d * v
    J_mul_v!(H2, H1, x, v, q, prob.de_prob.f)
    if iw != 0
        # "H2" -= iw * v
        w = imag(iw)
        @. H2[1:2:end] += w * imag(v)
        @. H2[2:2:end] -= w * real(v)
    end
    H3[:] = eigvec_constraint(v, augsys_cache)

    maybe_subtract!(H1, x, statekind(prob), timekind(prob))
    maybe_subtract!(H2, v, statekind(prob), timekind(prob))
    return H
end

J_mul_v(x, v::AbstractVector{<: Real}, q, f) =
    ForwardDiff.jacobian(
        (d) -> f((@. x + d[1] * v), q, 0),
        SVector(zero(eltype(x))),
        # TODO: setup cache
    )[:]

function J_mul_v(x, v::AbstractVector{<: Complex}, q, f)
    vr = real(v)
    vi = imag(v)
    Jv = ForwardDiff.jacobian(
        (d) -> f((@. x + d[1] * vr + d[2] * vi), q, 0),
        SVector(zero(eltype(x)), zero(eltype(x))),
        # TODO: setup cache
    )
    return @. Jv[:, 1] + im * Jv[:, 2]
end

function _residual!(::Any, u, prob::DiffEqCodim2Problem,
                    augsys_cache,
                    ::ImmutableState)
    q = modified_param!(prob, u)

    x = ds_state(prob, u)
    v = ds_eigvec(prob, u)
    iw = ds_eigval(prob, u)

    # TODO: Can I compute H and Jv in one go?  Or is it already
    # maximally efficient?
    H1 = prob.de_prob.f(x, q, 0)
    Jv = J_mul_v(x, v, q, prob.de_prob.f)
    H2 = as_reals(@. Jv - iw * v)

    H3 = eigvec_constraint(v, augsys_cache)

    return cat_outputs(
        maybe_subtract!(H1, x, statekind(prob), timekind(prob)),
        maybe_subtract!(H2, v, statekind(prob), timekind(prob)),
        H3)
end

# ---------------------------------------------------------- residual_jacobian!

function _residual_jacobian!(H, J, u, cache::DiffEqCodim2BifurcationCache,
                             ::MutableState)
    ForwardDiff.jacobian!(
        J,
        cache.residual,
        H,  # y
        u,  # x
        cache.cfg,
    )
    return (H, J)
end

function _residual_jacobian!(_H, _J, u, cache::DiffEqCodim2BifurcationCache,
                             ::ImmutableState)
    # TODO: Can I compute H and J in one go?  Or is it already
    # maximally efficient?
    H = cache.residual(u)
    J = ForwardDiff.jacobian(
        cache.residual,
        u,  # x
        cache.cfg,
    )
    return (H, J)
end


text:
module SoilPlantAirContinuum

using ..CanopyLayers
using CLIMAParameters
using CLIMAParameters.Planet
using CLIMAParameters.SubgridScale
using ConstrainedRootSolvers
using DataFrames
using DocStringExtensions
using Parameters
using ..Photosynthesis
using ..PlantHydraulics
using Statistics
using ..StomataModels
using WaterPhysics

# define constants
struct EarthParameterSet <: AbstractEarthParameterSet end
const EARTH       = EarthParameterSet();
GAS_R(FT)         = FT( gas_constant() );
GRAVITY(FT)       = FT( grav(EARTH) );
K_0(FT)           = FT( T_freeze(EARTH) );
K_25(FT)          = K_0(FT) + 25;
K_STEFAN(FT)      = FT( Stefan() );
K_VON_KARMAN(FT)  = FT( von_karman_const(EARTH) );
MOLMASS_WATER(FT) = FT( molmass_water(EARTH) );
P_ATM(FT)         = FT( MSLP(EARTH) );
RK_25(FT)         = GAS_R(FT) * K_25(FT);
YEAR_D(FT)        = FT( 365.2422222 );
ρ_H₂O(FT)         = FT( ρ_cloud_liq(EARTH) );
KG_2_MOL(FT)      = 1 / MOLMASS_WATER(FT);
KG_H_2_MOL_S(FT)  = KG_2_MOL(FT) / 3600;

# export public types
export SPACContainer1L,
       SPACContainer2L,
       SPACMono,
       SPACSimple

# export public functions
export annual_profit,
       annual_simulation!,
       atmospheric_pressure,
       atmospheric_pressure_ratio,
       big_leaf_partition!,
       create_dataframe,
       gain_risk_map,
       initialize_spac_canopy!,
       layer_fluxes!,
       leaf_allocation!,
       leaf_gas_exchange!,
       leaf_gas_exchange_nonopt!,
       leaf_temperature,
       leaf_temperature_shaded,
       leaf_temperature_sunlit,
       optimize_flows!,
       optimize_hs!,
       optimize_leaf!,
       ppm_to_Pa,
       test_soil_from_psoil!,
       test_soil_from_swc!,
       vary_spac!,
       zenith_angle

include("types/container.jl" )
include("types/spacmono.jl"  )
include("types/spacsimple.jl")

include("bigleaf/gainriskmap.jl"   )
include("bigleaf/gasexchange.jl"   )
include("bigleaf/leafallocation.jl")
include("bigleaf/optimizeflow.jl"  )
include("bigleaf/optimizehs.jl"    )
include("bigleaf/optimizeleaf.jl"  )
include("bigleaf/partition.jl"     )
include("bigleaf/temperature.jl"   )
include("bigleaf/varytrait.jl"     )

include("layers/layer_fluxes.jl")
include("layers/initializert.jl")
include("layers/test_diurnal.jl")
include("layers/test_soil.jl"   )
include("layers/windspeed.jl"   )

include("planet/atmpressure.jl")
include("planet/solarangle.jl" )

include("simulation/annualprofit.jl"    )
include("simulation/annualsimulation.jl")
include("simulation/createdataframe.jl" )

include("soil/moisture.jl")

end # module

max_stars_repo_name: bbuman/Land

max_issues_repo_name: bbuman/Land

max_forks_repo_name: bbuman/Land


text:
import AQFED.TermStructure: ConstantBlackModel
import AQFED.Black: blackScholesFormula

export AndersenLakeRepresentation, priceAmerican, americanBoundaryPutQDP

#Andersen-Lake American option pricing under negative rates
struct AndersenLakeNRepresentation
    isCall::Bool
    model::ConstantBlackModel
    tauMax::Float64
    tauMaxOrig::Float64
    nC::Int
    nTS1::Int
    nTS2::Int
    capX::Float64
    capXD::Float64
    avec::Vector{Float64}
    avecD::Vector{Float64}
    qvec::Vector{Float64}
    qvecD::Vector{Float64}
    wvec::Vector{Float64}
    yvec::Vector{Float64}
end

function AndersenLakeNRepresentation(
    model::ConstantBlackModel,
    tauMax::Float64,
    atol::Float64,
    nC::Int,
    nIter::Int,
    nTS1::Int,
    nTS2::Int;
    isCall::Bool = false,
)
    if iseven(nTS1)
        throw(DomainError(string("nTS1 must be odd but was ", nTS1)))
    end
    if iseven(nTS2)
        throw(DomainError(string("nTS2 must be odd but was ", nTS2)))
    end
    K = 1.0
    avec = zeros(nC + 1)
    qvec = zeros(nC + 1)
    avecD = zeros(nC + 1)
    qvecD = zeros(nC + 1)
    wvec = zeros(nTS1)
    yvec = zeros(nTS1)
    ndiv2 = trunc(Int, (nTS1 + 1) / 2)
    hn = tanhsinhStep(nTS1)
    hvec = hn .* (ndiv2-1:-1:1)
    svec = @. pi * sinh(hvec) / 2
    @. @view(yvec[1:ndiv2-1]) = tanh(svec) #may be more precise to store y+1 directly instead
    @. @view(wvec[1:ndiv2-1]) = hn * pi * cosh(hvec) / (2 * cosh(svec)^2)
    for i = 1:ndiv2-1
        yvec[nTS1+1-i] = -yvec[i]
        wvec[nTS1+1-i] = wvec[i]
    end
    yvec[ndiv2] = 0
    wvec[ndiv2] = pi * hn / 2
    r = model.r
    q = model.q
    modelB = model
    if isCall  #use McDonald and Schroder symmetry
        r, q = q, r
        modelB = ConstantBlackModel(vol, r, q)
    end
    vol = model.vol

    capX = K
    capXD = K * r / q
    logCapX = log(capX)
    logCapXD = log(capXD)
    local fprev = capX
    local fprevD = capXD
    qvec[nC+1] = 0
    qvecD[nC+1] = 0
    for i = nC:-1:1
        zi = cos((i - 1) * pi / nC)
        taui = tauMax / 4 * (1 + zi)^2
        fi = americanBoundaryPutQDP(false, modelB, fprev, K, taui, atol)
        fprev = fi
        qvec[i] = (log(fi / capX))^2
        fi = americanBoundaryPutQDP(true, modelB, fprevD, K, taui, atol)
        fprevD = fi
        qvecD[i] = (log(fi / capXD))^2
    end
    B = computeBoundaryFromQVec(-1, qvec, capX)
    Bdown = computeBoundaryFromQVec(1, qvecD, capXD)
    crossingIndex = updateCrossingBoundariesALUp!(nC, B, capX, Bdown, capXD)
    tauMaxOrig = tauMax
    if crossingIndex >= 1
        #refine crossingIndex
        i = crossingIndex
        zi = cos(i * pi / nC)
        taui = tauMax / 4 * (1 + zi)^2
        tauStart = taui
        fStart = B[i]
        fStartD = Bdown[i]
        i += 1
        zi = cos(i * pi / nC)
        taui = tauMax / 4 * (1 + zi)^2
        tauEnd = taui
        fEnd = B[i]
        fEndD = Bdown[i]

        tauCrossing = searchCrossingTimeQDPN(
            tauEnd,
            fEnd,
            fEndD,
            tauStart,
            fStart,
            fStartD,
            K,
            model,
            atol,
        )

        tauMax = tauCrossing
        fprev = capX
        fprevD = capXD
        for i = nC:-1:1
            zi = cos((i - 1) * pi / nC)
            taui = tauMax / 4 * (1 + zi)^2
            fi = americanBoundaryPutQDP(false, modelB, fprev, K, taui, atol)
            fprev = fi
            qvec[i] = (log(fi / capX))^2
            fi = americanBoundaryPutQDP(true, modelB, fprevD, K, taui, atol)
            fprevD = fi
            qvecD[i] = (log(fi / capXD))^2
        end
        B = computeBoundaryFromQVec(-1, qvec, capX)
        Bdown = computeBoundaryFromQVec(1, qvecD, capXD)
    end ##of crossing refinement
    @. qvec = log(B / capX)^2
    @. qvecD = log(Bdown / capXD)^2
    tauVector = zeros(nTS1)
    k1 = zeros(nTS1)
    k2 = zeros(nTS1)
    d1Vector = zeros(nTS1)
    d2Vector = zeros(nTS1)
    #println("AL init ", "Up ", computeBoundaryFromQVec(-1, qvec, capX), " Down Boundary ", computeBoundaryFromQVec(1, qvecD, capXD))
    for j = 1:nIter
        updateAvec!(avec, nC, qvec)
        ##up boundary
        updateAvec!(avecD, nC, qvecD)
        for i = 1:nC
            zi = cos((i - 1) * pi / nC)
            taui = tauMax / 4 * (1 + zi)^2
            Kstari = K * exp(-(r - q) * taui)
            lnBtaui = logCapX - sqrt(qvec[i])
            sum1k = 0.0
            sum2k = 0.0
            @. tauVector = taui / 4 * (1 + yvec)^2
            crossIndex = 0
            @inbounds for sk1 = 1:nTS1
                if yvec[sk1] != -1
                    tauk = tauVector[sk1]
                    zck = 2 * sqrt((taui - tauk) / tauMax) - 1
                    qckD = chebQck(avecD, zck)
                    qck = chebQck(avec, zck)
                    lnBtaukD = logCapXD + sqrt(qckD)
                    lnBtauk = logCapX - sqrt(qck)
                    if (lnBtaukD > lnBtauk) && crossIndex == 0
                        crossIndex = sk1
                    end
                    if crossIndex == 0
                        sqrtv = sqrt(tauk) * vol
                        d1k = ((lnBtaui - lnBtaukD) + (r - q) * tauk) / sqrtv + sqrtv / 2
                        d2k = d1k - sqrtv
                        sum1k -= wvec[sk1] * exp(-q * tauk) * (yvec[sk1] + 1) * normcdf(d1k)
                        sum2k -= wvec[sk1] * exp(-r * tauk) * (yvec[sk1] + 1) * normcdf(d2k)

                        d1k = ((lnBtaui - lnBtauk) + (r - q) * tauk) / sqrtv + sqrtv / 2
                        d2k = d1k - sqrtv
                        sum1k += wvec[sk1] * exp(-q * tauk) * (yvec[sk1] + 1) * normcdf(d1k)
                        sum2k += wvec[sk1] * exp(-r * tauk) * (yvec[sk1] + 1) * normcdf(d2k)
                    end
                end
            end

            sum1k = exp(q * taui) / 2 * taui * sum1k
            sum2k = exp(r * taui) / 2 * taui * sum2k
            sqrtv = sqrt(taui) * vol
            d1i = ((lnBtaui - log(K)) + (r - q) * taui) / sqrtv + sqrtv / 2
            d2i = d1i - sqrtv

            Ni = -1 + exp(r * taui) + normcdf(d2i) + r * sum2k
            Di = -1 + exp(q * taui) + normcdf(d1i) + q * sum1k
            NiOverDi = Ni / Di
            if Di == 0.0 && Ni == 0.0
                #use asymptotic expansion cdf = erfc(-x/sqrt2)/2 and erfc(x) = e^{-x^2}/(x*sqrtpi)*(1-1/(2*x^2))
                NiOverDi = exp(-(d2i^2 - d1i^2) / 2) * (d1i / d2i)
            end
            fi = Kstari * NiOverDi
            if fi < 0
                fi = 1e-4 * Kstari
            end
            lfc = log(fi / capX)
            if isnan(lfc)
                throw(DomainError(
                    fi,
                    string("Nan qvec ", capX, " ", lnBtaui, " ", qvec[i]),
                ))
            end
            qvec[i] = lfc^2
        end
        qvec[nC+1] = 0

        ##Down boundary
        updateAvec!(avec, nC, qvec)
        for i = 1:nC
            zi = cos((i - 1) * pi / nC)
            taui = tauMax / 4 * (1 + zi)^2
            Kstari = K * exp(-(r - q) * taui)
            lnBtauiD = logCapXD + sqrt(qvecD[i])
            sum1kD = 0.0
            sum2kD = 0.0
            @. tauVector = taui / 4 * (1 + yvec)^2
            crossIndex = 0
            @inbounds for sk1 = 1:nTS1
                if yvec[sk1] != -1
                    tauk = tauVector[sk1]
                    zck = 2 * sqrt((taui - tauk) / tauMax) - 1
                    qckD = chebQck(avecD, zck)
                    qck = chebQck(avec, zck)
                    lnBtaukD = logCapXD + sqrt(qckD)
                    lnBtauk = logCapX - sqrt(qck)
                    if (lnBtaukD > lnBtauk) && crossIndex == 0
                        crossIndex = sk1
                    end
                    if crossIndex == 0
                        sqrtv = sqrt(tauk) * vol
                        d1k = ((lnBtauiD - lnBtaukD) + (r - q) * tauk) / sqrtv + sqrtv / 2
                        d2k = d1k - sqrtv
                        sum1kD -=
                            wvec[sk1] * exp(-q * tauk) * (yvec[sk1] + 1) * normcdf(d1k)
                        sum2kD -=
                            wvec[sk1] * exp(-r * tauk) * (yvec[sk1] + 1) * normcdf(d2k)

                        d1k = ((lnBtauiD - lnBtauk) + (r - q) * tauk) / sqrtv + sqrtv / 2
                        d2k = d1k - sqrtv
                        sum1kD +=
                            wvec[sk1] * exp(-q * tauk) * (yvec[sk1] + 1) * normcdf(d1k)
                        sum2kD +=
                            wvec[sk1] * exp(-r * tauk) * (yvec[sk1] + 1) * normcdf(d2k)
                    end
                end
            end

            sum1kD = exp(q * taui) / 2 * taui * sum1kD
            sum2kD = exp(r * taui) / 2 * taui * sum2kD
            sqrtv = sqrt(taui) * vol
            d1i = ((lnBtauiD - log(K)) + (r - q) * taui) / sqrtv + sqrtv / 2
            d2i = d1i - sqrtv
            fiold = capXD * exp(sqrt(qvecD[i]))
            Ni =
                -1 + exp(r * taui) + normcdf(d2i) + r * sum2kD -
                fiold / Kstari * q * (sum1kD)
            Di = -1 + exp(q * taui) + normcdf(d1i)
            NiOverDi = Ni / Di
            if Di == 0.0 && Ni == 0.0
                #use asymptotic expansion cdf = erfc(-x/sqrt2)/2 and erfc(x) = e^{-x^2}/(x*sqrtpi)*(1-1/(2*x^2))
                NiOverDi = exp(-(d2i^2 - d1i^2) / 2) * (d1i / d2i)
            end
            fi = Kstari * NiOverDi
            if fi < 0
                fi = 1e-4 * Kstari
            end
            lfc = log(fi / capXD)
            if isnan(lfc)
                throw(DomainError(
                    fi,
                    string("Nan qvec ", capX, " ", lnBtaui, " ", qvec[i]),
                ))
            end
            qvecD[i] = lfc^2
        end
        qvecD[nC+1] = 0
        ##update boundaries
        B = computeBoundaryFromQVec(-1, qvec, capX)
        Bdown = computeBoundaryFromQVec(1, qvecD, capXD)
        updateCrossingBoundariesALUp!(nC, B, capX, Bdown, capXD)
        @. qvec = (log(B / capX))^2
        @. qvecD = (log(Bdown / capXD))^2
        # updateAvec!(avec, nC, qvec)
        # updateAvec!(avecD, nC, qvecD)
        # println("AL ",j, " Up ", computeBoundaryFromQVec(-1, qvec, capX), " Down Boundary ", computeBoundaryFromQVec(1, qvecD, capXD))
    end

    updateAvec!(avecD, nC, qvecD)
    updateAvec!(avec, nC, qvec)
    if nTS2 != nTS1
        wvec = zeros(nTS2)
        yvec = zeros(nTS2)
        ndiv2 = trunc(Int, (nTS2 + 1) / 2)
        hn = tanhsinhStep(nTS2)
        hi = (ndiv2 - 1) * hn
        for i = 1:ndiv2-1
            p2s = pi * sinh(hi) / 2
            yvec[i] = tanh(p2s)
            p2c = pi * cosh(hi) / 2
            cp2s = cosh(p2s)
            wvec[i] = hn * p2c / (cp2s^2)
            hi = hi - hn
        end
        yvec[ndiv2] = 0
        wvec[ndiv2] = pi * hn / 2
        for i = 1:ndiv2-1
            yvec[nTS2+1-i] = -yvec[i]
            wvec[nTS2+1-i] = wvec[i]
        end
    end
    return AndersenLakeNRepresentation(
        isCall,
        model,
        tauMax,
        tauMaxOrig,
        nC,
        nTS1,
        nTS2,
        capX,
        capXD,
        avec,
        avecD,
        qvec,
        qvecD,
        wvec,
        yvec,
    )

    ##up

end

function priceAmerican(p::AndersenLakeNRepresentation, K::Float64, S::Float64)::Float64
    vol, r, q = p.model.vol, p.model.r, p.model.q
    if p.isCall #use McDonald and Schroder symmetry
        K, S = S, K
        r, q = q, r
    end
    capX, capXD = p.capX * K, p.capXD * K
    f0 = exp(-sqrt(p.qvec[1])) * capX
    f0D = exp(sqrt(p.qvecD[1])) * capXD
    if S <= f0 && S >= f0D && p.tauMax == p.tauMaxOrig
        return max(K - S, 0.0)
    end

    tauMax, tauMaxOrig, nTS2 = p.tauMax, p.tauMaxOrig, p.nTS2
    wvec, yvec, avec, avecD = p.wvec, p.yvec, p.avec, p.avecD
    nC, rK, qS = p.nC, r * K, q * S

    uMax = tauMax
    uMin = 0.0
    uScale = (uMax - uMin) / 2
    uShift = (uMax + uMin) / 2
    sum4k = 0.0
    isCrossed = false
    for sk2 = nTS2:-1:1
        wk = wvec[sk2]
        yk = yvec[sk2]
        uk = uScale * yk + uShift
        if abs(yk) != 1
            zck = 2 * sqrt(uk / tauMax) - 1
            qck = chebQck(avec, zck)
            qckD = chebQck(avecD, zck)
            Bzk = capX * exp(-sqrt(qck))
            BzkD = capXD * exp(sqrt(qckD))
            if Bzk < BzkD
                isCrossed = true
            end
            if !isCrossed
                tauk = uMax - uk + tauMaxOrig - tauMax
                d1k, d2k = vaGBMd1d2(S, Bzk, r, q, tauk, vol)
                sum4k += wk * rK * exp(-r * tauk) * normcdf(-d2k)
                sum4k += -wk * qS * exp(-q * tauk) * normcdf(-d1k)
                d1k, d2k = vaGBMd1d2(S, BzkD, r, q, tauk, vol)
                sum4k -= wk * rK * exp(-r * tauk) * normcdf(-d2k)
                sum4k += wk * qS * exp(-q * tauk) * normcdf(-d1k)
            end
        end
    end

    euro = blackScholesFormula(
        false,
        K,
        S,
        vol * vol * tauMaxOrig,
        exp(-(r - q) * tauMaxOrig),
        exp(-r * tauMaxOrig),
    )
    price = euro + uScale * sum4k
    price = max(K - S, price)
    return price
end

computeBoundaryFromQVec(sign::Int, qvec::Vector{Float64}, capX::Float64) =
    @. exp(sign * sqrt(qvec)) * capX

function updateCrossingBoundariesALUp!(
    m::Int,
    B::Vector{Float64},
    capX::Float64,
    Bdown::Vector{Float64},
    capXD::Float64,
)::Int
    for i = m-1:-1:1
        if B[i+1] < B[i]
            B[i] = B[i+1]
        end
        if Bdown[i] < Bdown[i+1]
            Bdown[i] = Bdown[i+1]
        end
        if B[i] > capX
            B[i] = capX
        end
        if Bdown[i] < capXD
            Bdown[i] = capXD
        end
    end
    icross = m
    while icross > 0 && Bdown[icross] < B[icross]
        icross -= 1
    end
    if icross >= 1
        if Bdown[icross] > capX
            Bdown[icross] = capX
        end
        if B[icross] < capXD
            B[icross] = capXD
        end
        level = B[icross]
        level = max(Bdown[icross+1], level)
        level = min(B[icross+1], level)
        for i = icross:-1:1
            B[i] = level
            Bdown[i] = B[i]
        end
    end
    return icross
end


text:
# User
struct User <: CanvasObject
    """The ID of the user."""
    id::Union{Int,Nothing}
    """The name of the user."""
    name::Union{String,Nothing}
    """The name of the user that is should be used for sorting groups of users,
    such as in the gradebook."""
    sortable_name::Union{String,Nothing}
    """A short name the user has selected, for use in conversations or other
    less formal places through the site."""
    short_name::Union{String,Nothing}
    """The SIS ID associated with the user. This field is only included if the
    user came from a SIS import and has permissions to view SIS information."""
    sis_user_id::Union{String,Nothing}
    """The id of the SIS import. This field is only included if the user came
    from a SIS import and has permissions to manage SIS information."""
    sis_import_id::Union{Int,Nothing}
    """The integration_id associated with the user. This field is only included
    if the user came from a SIS import and has permissions to view SIS information."""
    integration_id::Union{String,Nothing}
    """The unique login id for the user. This is what the user uses to log in
    to Canvas."""
    login_id::Union{String,Nothing}
    """If avatars are enabled, this field will be included and contain a url
    to retrieve the user's avatar."""
    avatar_url::Union{String,Nothing}
    """Optional: This field can be requested with certain API calls, and will
    return a list of the users active enrollments. See the List enrollments
    API for more details about the format of these records."""
    enrollments::Union{Vector,Nothing}
    """Optional: This field can be requested with certain API calls, and will
    return the users primary email address."""
    email::Union{String,Nothing}
    """Optional: This field can be requested with certain API calls, and will
    return the users locale in RFC 5646 format."""
    locale::Union{String,Nothing}
    """Optional: This field is only returned in certain API calls, and will
    return a timestamp representing the last time the user logged in to canvas."""
    last_login::Union{String,Nothing}
    """Optional: This field is only returned in certain API calls, and will
    return the IANA time zone name of the user's preferred timezone."""
    time_zone::Union{String,Nothing}
    """Optional: The user's bio."""
    bio::Union{String,Nothing}
end

# CalendarLink
struct CalendarLink <: CanvasObject
    """The URL of the calendar in ICS format"""
    ics::Union{String,Nothing}
end
# Disambiguating constructor needed for single field structs
CalendarLink(data::Dict) = json2canvas(CalendarLink, data)

# Term
struct Term <: CanvasObject
    id::Union{Int,Nothing}
    name::Union{String,Nothing}
    start_at::Union{Dates.DateTime,Nothing}
    end_at::Union{Dates.DateTime,Nothing}
end

# CourseProgress
struct CourseProgress <: CanvasObject
    """total number of requirements from all modules"""
    requirement_count::Union{Int,Nothing}
    """total number of requirements the user has completed from all modules"""
    requirement_completed_count::Union{Int,Nothing}
    """url to next module item that has an unmet requirement. null if the user
    has completed the course or the current module does not require sequential
    progress"""
    next_requirement_url::Union{String,Nothing}
    """date the course was completed. null if the course has not been completed
    by this user"""
    completed_at::Union{Dates.DateTime,Nothing}
end

# Course
struct Course <: CanvasObject
    """the unique identifier for the course"""
    id::Union{Int,Nothing}
    """the SIS identifier for the course, if defined. This field is only included
    if the user has permission to view SIS information."""
    sis_course_id::Union{String,Nothing}
    """the UUID of the course"""
    uuid::Union{String,Nothing}
    """the integration identifier for the course, if defined. This field is
    only included if the user has permission to view SIS information."""
    integration_id::Union{String,Nothing}
    """the unique identifier for the SIS import. This field is only included
    if the user has permission to manage SIS information."""
    sis_import_id::Union{Int,Nothing}
    """the full name of the course"""
    name::Union{String,Nothing}
    """the course code"""
    course_code::Union{String,Nothing}
    """the current state of the course one of 'unpublished', 'available', 'completed',
    or 'deleted'"""
    workflow_state::Union{String,Nothing}
    """the account associated with the course"""
    account_id::Union{Int,Nothing}
    """the root account associated with the course"""
    root_account_id::Union{Int,Nothing}
    """the enrollment term associated with the course"""
    enrollment_term_id::Union{Int,Nothing}
    """the grading standard associated with the course"""
    grading_standard_id::Union{Int,Nothing}
    """the grade_passback_setting set on the course"""
    grade_passback_setting::Union{String,Nothing}
    """the date the course was created."""
    created_at::Union{Dates.DateTime,Nothing}
    """the start date for the course, if applicable"""
    start_at::Union{Dates.DateTime,Nothing}
    """the end date for the course, if applicable"""
    end_at::Union{Dates.DateTime,Nothing}
    """the course-set locale, if applicable"""
    locale::Union{String,Nothing}
    """A list of enrollments linking the current user to the course. for student
    enrollments, grading information may be included if include[]=total_scores"""
    enrollments::Union{Vector,Nothing}
    """optional: the total number of active and invited students in the course"""
    total_students::Union{Int,Nothing}
    """course calendar"""
    calendar::Union{CalendarLink,Nothing}
    """the type of page that users will see when they first visit the course
    - 'feed': Recent Activity Dashboard - 'wiki': Wiki Front Page - 'modules':
    Course Modules/Sections Page - 'assignments': Course Assignments List -
    'syllabus': Course Syllabus Page other types may be added in the future"""
    default_view::Union{String,Nothing}
    """optional: user-generated HTML for the course syllabus"""
    syllabus_body::Union{String,Nothing}
    """optional: the number of submissions needing grading returned only if
    the current user has grading rights and include[]=needs_grading_count"""
    needs_grading_count::Union{Int,Nothing}
    """optional: the enrollment term object for the course returned only if
    include[]=term"""
    term::Union{Term,Nothing}
    """optional: information on progress through the course returned only if
    include[]=course_progress"""
    course_progress::Union{CourseProgress,Nothing}
    """weight final grade based on assignment group percentages"""
    apply_assignment_group_weights::Union{Bool,Nothing}
    """optional: the permissions the user has for the course. returned only
    for a single course and include[]=permissions"""
    permissions::Union{Dict,Nothing}
    is_public::Union{Bool,Nothing}
    is_public_to_auth_users::Union{Bool,Nothing}
    public_syllabus::Union{Bool,Nothing}
    public_syllabus_to_auth::Union{Bool,Nothing}
    """optional: the public description of the course"""
    public_description::Union{String,Nothing}
    storage_quota_mb::Union{Int,Nothing}
    storage_quota_used_mb::Union{Float64,Nothing}
    hide_final_grades::Union{Bool,Nothing}
    license::Union{String,Nothing}
    allow_student_assignment_edits::Union{Bool,Nothing}
    allow_wiki_comments::Union{Bool,Nothing}
    allow_student_forum_attachments::Union{Bool,Nothing}
    open_enrollment::Union{Bool,Nothing}
    self_enrollment::Union{Bool,Nothing}
    restrict_enrollments_to_course_dates::Union{Bool,Nothing}
    course_format::Union{String,Nothing}
    """optional: this will be true if this user is currently prevented from
    viewing the course because of date restriction settings"""
    access_restricted_by_date::Union{Bool,Nothing}
    """The course's IANA time zone name."""
    time_zone::Union{String,Nothing}
    """optional: whether the course is set as a Blueprint Course (blueprint
    fields require the Blueprint Courses feature)"""
    blueprint::Union{Bool,Nothing}
    """optional: Set of restrictions applied to all locked course objects"""
    blueprint_restrictions::Union{Dict,Nothing}
    """optional: Sets of restrictions differentiated by object type applied
    to locked course objects"""
    blueprint_restrictions_by_object_type::Union{Dict,Nothing}
end

# Group
struct Group <: CanvasObject
    """The ID of the group."""
    id::Union{Int,Nothing}
    """The display name of the group."""
    name::Union{String,Nothing}
    """A description of the group. This is plain text."""
    description::Union{String,Nothing}
    """Whether or not the group is public. Currently only community groups can
    be made public. Also, once a group has been set to public, it cannot be
    changed back to private."""
    is_public::Union{Bool,Nothing}
    """Whether or not the current user is following this group."""
    followed_by_user::Union{Bool,Nothing}
    """How people are allowed to join the group. For all groups except for community
    groups, the user must share the group's parent course or account. For student
    organized or community groups, where a user can be a member of as many or
    few as they want, the applicable levels are 'parent_context_auto_join',
    'parent_context_request', and 'invitation_only'. For class groups, where
    students are divided up and should only be part of one group of the category,
    this value will always be 'invitation_only', and is not relevant. * If 'parent_context_auto_join',
    anyone can join and will be automatically accepted. * If 'parent_context_request',
    anyone can request to join, which must be approved by a group moderator.
    * If 'invitation_only', only those how have received an invitation my join
    the group, by accepting that invitation."""
    join_level::Union{String,Nothing}
    """The number of members currently in the group"""
    members_count::Union{Int,Nothing}
    """The url of the group's avatar"""
    avatar_url::Union{String,Nothing}
    """The course or account that the group belongs to. The pattern here is
    that whatever the context_type is, there will be an _id field named after
    that type. So if instead context_type was 'account', the course_id field
    would be replaced by an account_id field."""
    context_type::Union{String,Nothing}
    course_id::Union{Int,Nothing}
    """Certain types of groups have special role designations. Currently, these
    include: 'communities', 'student_organized', and 'imported'. Regular course/account
    groups have a role of null."""
    role::Union{String,Nothing}
    """The ID of the group's category."""
    group_category_id::Union{Int,Nothing}
    """The SIS ID of the group. Only included if the user has permission to
    view SIS information."""
    sis_group_id::Union{String,Nothing}
    """The id of the SIS import if created through SIS. Only included if the
    user has permission to manage SIS information."""
    sis_import_id::Union{Int,Nothing}
    """the storage quota for the group, in megabytes"""
    storage_quota_mb::Union{Int,Nothing}
    """optional: the permissions the user has for the group. returned only for
    a single group and include[]=permissions"""
    permissions::Union{Dict,Nothing}
end

# LockInfo
struct LockInfo <: CanvasObject
    """Asset string for the object causing the lock"""
    asset_string::Union{String,Nothing}
    """(Optional) Time at which this was/will be unlocked. Must be before the
    due date."""
    unlock_at::Union{Dates.DateTime,Nothing}
    """(Optional) Time at which this was/will be locked. Must be after the due
    date."""
    lock_at::Union{Dates.DateTime,Nothing}
    """(Optional) Context module causing the lock."""
    context_module::Union{String,Nothing}
    manually_locked::Union{Bool,Nothing}
end

# File
struct File <: CanvasObject
    id::Union{Int,Nothing}
    uuid::Union{String,Nothing}
    folder_id::Union{Int,Nothing}
    display_name::Union{String,Nothing}
    filename::Union{String,Nothing}
    content_type::Union{String,Nothing}
    url::Union{String,Nothing}
    """file size in bytes"""
    size::Union{Int,Nothing}
    created_at::Union{Dates.DateTime,Nothing}
    updated_at::Union{Dates.DateTime,Nothing}
    unlock_at::Union{Dates.DateTime,Nothing}
    locked::Union{Bool,Nothing}
    hidden::Union{Bool,Nothing}
    lock_at::Union{Dates.DateTime,Nothing}
    hidden_for_user::Union{Bool,Nothing}
    thumbnail_url::Union{String,Nothing}
    modified_at::Union{Dates.DateTime,Nothing}
    """simplified content-type mapping"""
    mime_class::Union{String,Nothing}
    """identifier for file in third-party transcoding service"""
    media_entry_id::Union{String,Nothing}
    locked_for_user::Union{Bool,Nothing}
    lock_info::Union{LockInfo,Nothing}
    lock_explanation::Union{String,Nothing}
    """optional: url to the document preview. This url is specific to the user
    making the api call. Only included in submission endpoints."""
    preview_url::Union{String,Nothing}
end

# Folder
struct Folder <: CanvasObject
    context_type::Union{String,Nothing}
    context_id::Union{Int,Nothing}
    files_count::Union{Int,Nothing}
    position::Union{Int,Nothing}
    updated_at::Union{Dates.DateTime,Nothing}
    folders_url::Union{String,Nothing}
    files_url::Union{String,Nothing}
    full_name::Union{String,Nothing}
    lock_at::Union{Dates.DateTime,Nothing}
    id::Union{Int,Nothing}
    folders_count::Union{Int,Nothing}
    name::Union{String,Nothing}
    parent_folder_id::Union{Int,Nothing}
    created_at::Union{Dates.DateTime,Nothing}
    unlock_at::Union{Dates.DateTime,Nothing}
    hidden::Union{Bool,Nothing}
    hidden_for_user::Union{Bool,Nothing}
    locked::Union{Bool,Nothing}
    locked_for_user::Union{Bool,Nothing}
    """If true, indicates this is a read-only folder containing files submitted
    to assignments"""
    for_submissions::Union{Bool,Nothing}
end

# TurnitinSettings
struct TurnitinSettings <: CanvasObject
    originality_report_visibility::Union{String,Nothing}
    s_paper_check::Union{Bool,Nothing}
    internet_check::Union{Bool,Nothing}
    journal_check::Union{Bool,Nothing}
    exclude_biblio::Union{Bool,Nothing}
    exclude_quoted::Union{Bool,Nothing}
    exclude_small_matches_type::Union{String,Nothing}
    exclude_small_matches_value::Union{Int,Nothing}
end

# ExternalToolTagAttributes
struct ExternalToolTagAttributes <: CanvasObject
    """URL to the external tool"""
    url::Union{String,Nothing}
    """Whether or not there is a new tab for the external tool"""
    new_tab::Union{Bool,Nothing}
    """the identifier for this tool_tag"""
    resource_link_id::Union{String,Nothing}
end

# DiscussionTopic
struct DiscussionTopic <: CanvasObject
    """The ID of this topic."""
    id::Union{Int,Nothing}
    """The topic title."""
    title::Union{String,Nothing}
    """The HTML content of the message body."""
    message::Union{String,Nothing}
    """The URL to the discussion topic in canvas."""
    html_url::Union{String,Nothing}
    """The datetime the topic was posted. If it is null it hasn't been posted
    yet. (see delayed_post_at)"""
    posted_at::Union{Dates.DateTime,Nothing}
    """The datetime for when the last reply was in the topic."""
    last_reply_at::Union{Dates.DateTime,Nothing}
    """If true then a user may not respond to other replies until that user
    has made an initial reply. Defaults to false."""
    require_initial_post::Union{Bool,Nothing}
    """Whether or not posts in this topic are visible to the user."""
    user_can_see_posts::Union{Bool,Nothing}
    """The count of entries in the topic."""
    discussion_subentry_count::Union{Int,Nothing}
    """The read_state of the topic for the current user, 'read' or 'unread'."""
    read_state::Union{String,Nothing}
    """The count of unread entries of this topic for the current user."""
    unread_count::Union{Int,Nothing}
    """Whether or not the current user is subscribed to this topic."""
    subscribed::Union{Bool,Nothing}
    """(Optional) Why the user cannot subscribe to this topic. Only one reason
    will be returned even if multiple apply. Can be one of: 'initial_post_required':
    The user must post a reply first; 'not_in_group_set': The user is not in
    the group set for this graded group discussion; 'not_in_group': The user
    is not in this topic's group; 'topic_is_announcement': This topic is an
    announcement"""
    subscription_hold::Union{String,Nothing}
    """The unique identifier of the assignment if the topic is for grading,
    otherwise null."""
    assignment_id::Union{Int,Nothing}
    """The datetime to publish the topic (if not right away)."""
    delayed_post_at::Union{Dates.DateTime,Nothing}
    """Whether this discussion topic is published (true) or draft state (false)"""
    published::Union{Bool,Nothing}
    """The datetime to lock the topic (if ever)."""
    lock_at::Union{Dates.DateTime,Nothing}
    """Whether or not the discussion is 'closed for comments'."""
    locked::Union{Bool,Nothing}
    """Whether or not the discussion has been 'pinned' by an instructor"""
    pinned::Union{Bool,Nothing}
    """Whether or not this is locked for the user."""
    locked_for_user::Union{Bool,Nothing}
    """(Optional) Information for the user about the lock. Present when locked_for_user
    is true."""
    lock_info::Union{LockInfo,Nothing}
    """(Optional) An explanation of why this is locked for the user. Present
    when locked_for_user is true."""
    lock_explanation::Union{String,Nothing}
    """The username of the topic creator."""
    user_name::Union{String,Nothing}
    """DEPRECATED An array of topic_ids for the group discussions the user is
    a part of."""
    topic_children::Union{Vector,Nothing}
    """An array of group discussions the user is a part of. Fields include:
    id, group_id"""
    group_topic_children::Union{Vector,Nothing}
    """If the topic is for grading and a group assignment this will point to
    the original topic in the course."""
    root_topic_id::Union{Int,Nothing}
    """If the topic is a podcast topic this is the feed url for the current
    user."""
    podcast_url::Union{String,Nothing}
    """The type of discussion. Values are 'side_comment', for discussions that
    only allow one level of nested comments, and 'threaded' for fully threaded
    discussions."""
    discussion_type::Union{String,Nothing}
    """The unique identifier of the group category if the topic is a group discussion,
    otherwise null."""
    group_category_id::Union{Int,Nothing}
    """Array of file attachments."""
    attachments::Union{Vector,Nothing}
    """The current user's permissions on this topic."""
    permissions::Union{Dict,Nothing}
    """Whether or not users can rate entries in this topic."""
    allow_rating::Union{Bool,Nothing}
    """Whether or not grade permissions are required to rate entries."""
    only_graders_can_rate::Union{Bool,Nothing}
    """Whether or not entries should be sorted by rating."""
    sort_by_rating::Union{Bool,Nothing}
end

# Submission
struct Submission <: CanvasObject
    """The submission's assignment id"""
    assignment_id::Union{Int,Nothing}
    """The submission's assignment (see the assignments API) (optional)"""
    assignment::Union{Assignment,Nothing}
    """The submission's course (see the course API) (optional)"""
    course::Union{Course,Nothing}
    """This is the submission attempt number."""
    attempt::Union{Int,Nothing}
    """The content of the submission, if it was submitted directly in a text
    field."""
    body::Union{String,Nothing}
    """The grade for the submission, translated into the assignment grading
    scheme (so a letter grade, for example)."""
    grade::Union{String,Nothing}
    """A boolean flag which is false if the student has re-submitted since the
    submission was last graded."""
    grade_matches_current_submission::Union{Bool,Nothing}
    """URL to the submission. This will require the user to log in."""
    html_url::Union{String,Nothing}
    """URL to the submission preview. This will require the user to log in."""
    preview_url::Union{String,Nothing}
    """The raw score"""
    score::Union{Float64,Nothing}
    """Associated comments for a submission (optional)"""
    submission_comments::Union{Vector,Nothing}
    """The types of submission ex: ('online_text_entry'|'online_url'|'online_upload'|'media_recording')"""
    submission_type::Union{String,Nothing}
    """The timestamp when the assignment was submitted"""
    submitted_at::Union{Dates.DateTime,Nothing}
    """The URL of the submission (for 'online_url' submissions)."""
    url::Union{String,Nothing}
    """The id of the user who created the submission"""
    user_id::Union{Int,Nothing}
    """The id of the user who graded the submission. This will be null for submissions
    that haven't been graded yet. It will be a positive number if a real user
    has graded the submission and a negative number if the submission was graded
    by a process (e.g. Quiz autograder and autograding LTI tools). Specifically
    autograded quizzes set grader_id to the negative of the quiz id. Submissions
    autograded by LTI tools set grader_id to the negative of the tool id."""
    grader_id::Union{Int,Nothing}
    graded_at::Union{Dates.DateTime,Nothing}
    """The submissions user (see user API) (optional)"""
    user::Union{User,Nothing}
    """Whether the submission was made after the applicable due date"""
    late::Union{Bool,Nothing}
    """Whether the assignment is visible to the user who submitted the assignment.
    Submissions where `assignment_visible` is false no longer count towards
    the student's grade and the assignment can no longer be accessed by the
    student. `assignment_visible` becomes false for submissions that do not
    have a grade and whose assignment is no longer assigned to the student's
    section."""
    assignment_visible::Union{Bool,Nothing}
    """Whether the assignment is excused. Excused assignments have no impact
    on a user's grade."""
    excused::Union{Bool,Nothing}
    """Whether the assignment is missing."""
    missing::Union{Bool,Nothing}
    """The status of the submission in relation to the late policy. Can be late,
    missing, none, or null."""
    late_policy_status::Union{String,Nothing}
    """The amount of points automatically deducted from the score by the missing/late
    policy for a late or missing assignment."""
    points_deducted::Union{Float64,Nothing}
    """The amount of time, in seconds, that an submission is late by."""
    seconds_late::Union{Float64,Nothing}
    """The current state of the submission"""
    workflow_state::Union{String,Nothing}
    """Extra submission attempts allowed for the given user and assignment."""
    extra_attempts::Union{Float64,Nothing}
    """A unique short ID identifying this submission without reference to the
    owning user. Only included if the caller has administrator access for the
    current account."""
    anonymous_id::Union{String,Nothing}
    """The date this submission was posted to the student, or nil if it has
    not been posted."""
    posted_at::Union{Dates.DateTime,Nothing}
end

# Assignment
struct Assignment <: CanvasObject
    """the ID of the assignment"""
    id::Union{Int,Nothing}
    """the name of the assignment"""
    name::Union{String,Nothing}
    """the assignment description, in an HTML fragment"""
    description::Union{String,Nothing}
    """The time at which this assignment was originally created"""
    created_at::Union{Dates.DateTime,Nothing}
    """The time at which this assignment was last modified in any way"""
    updated_at::Union{Dates.DateTime,Nothing}
    """the due date for the assignment. returns null if not present. NOTE: If
    this assignment has assignment overrides, this field will be the due date
    as it applies to the user requesting information from the API."""
    due_at::Union{Dates.DateTime,Nothing}
    """the lock date (assignment is locked after this date). returns null if
    not present. NOTE: If this assignment has assignment overrides, this field
    will be the lock date as it applies to the user requesting information from
    the API."""
    lock_at::Union{Dates.DateTime,Nothing}
    """the unlock date (assignment is unlocked after this date) returns null
    if not present NOTE: If this assignment has assignment overrides, this field
    will be the unlock date as it applies to the user requesting information
    from the API."""
    unlock_at::Union{Dates.DateTime,Nothing}
    """whether this assignment has overrides"""
    has_overrides::Union{Bool,Nothing}
    """(Optional) all dates associated with the assignment, if applicable"""
    all_dates::Union{Vector,Nothing}
    """the ID of the course the assignment belongs to"""
    course_id::Union{Int,Nothing}
    """the URL to the assignment's web page"""
    html_url::Union{String,Nothing}
    """the URL to download all submissions as a zip"""
    submissions_download_url::Union{String,Nothing}
    """the ID of the assignment's group"""
    assignment_group_id::Union{Int,Nothing}
    """Boolean flag indicating whether the assignment requires a due date based
    on the account level setting"""
    due_date_required::Union{Bool,Nothing}
    """Allowed file extensions, which take effect if submission_types includes
    'online_upload'."""
    allowed_extensions::Union{Vector,Nothing}
    """An integer indicating the maximum length an assignment's name may be"""
    max_name_length::Union{Int,Nothing}
    """Boolean flag indicating whether or not Turnitin has been enabled for
    the assignment. NOTE: This flag will not appear unless your account has
    the Turnitin plugin available"""
    turnitin_enabled::Union{Bool,Nothing}
    """Boolean flag indicating whether or not VeriCite has been enabled for
    the assignment. NOTE: This flag will not appear unless your account has
    the VeriCite plugin available"""
    vericite_enabled::Union{Bool,Nothing}
    """Settings to pass along to turnitin to control what kinds of matches should
    be considered. originality_report_visibility can be 'immediate', 'after_grading',
    'after_due_date', or 'never' exclude_small_matches_type can be null, 'percent',
    'words' exclude_small_matches_value: - if type is null, this will be null
    also - if type is 'percent', this will be a number between 0 and 100 representing
    match size to exclude as a percentage of the document size. - if type is
    'words', this will be number > 0 representing how many words a match must
    contain for it to be considered NOTE: This flag will not appear unless your
    account has the Turnitin plugin available"""
    turnitin_settings::Union{TurnitinSettings,Nothing}
    """If this is a group assignment, boolean flag indicating whether or not
    students will be graded individually."""
    grade_group_students_individually::Union{Bool,Nothing}
    """(Optional) assignment's settings for external tools if submission_types
    include 'external_tool'. Only url and new_tab are included (new_tab defaults
    to false). Use the 'External Tools' API if you need more information about
    an external tool."""
    external_tool_tag_attributes::Union{ExternalToolTagAttributes,Nothing}
    """Boolean indicating if peer reviews are required for this assignment"""
    peer_reviews::Union{Bool,Nothing}
    """Boolean indicating peer reviews are assigned automatically. If false,
    the teacher is expected to manually assign peer reviews."""
    automatic_peer_reviews::Union{Bool,Nothing}
    """Integer representing the amount of reviews each user is assigned. NOTE:
    This key is NOT present unless you have automatic_peer_reviews set to true."""
    peer_review_count::Union{Int,Nothing}
    """String representing a date the reviews are due by. Must be a date that
    occurs after the default due date. If blank, or date is not after the assignment's
    due date, the assignment's due date will be used. NOTE: This key is NOT
    present unless you have automatic_peer_reviews set to true."""
    peer_reviews_assign_at::Union{Dates.DateTime,Nothing}
    """Boolean representing whether or not members from within the same group
    on a group assignment can be assigned to peer review their own group's work"""
    intra_group_peer_reviews::Union{Bool,Nothing}
    """The ID of the assignment’s group set, if this is a group assignment.
    For group discussions, set group_category_id on the discussion topic, not
    the linked assignment."""
    group_category_id::Union{Int,Nothing}
    """if the requesting user has grading rights, the number of submissions
    that need grading."""
    needs_grading_count::Union{Int,Nothing}
    """if the requesting user has grading rights and the 'needs_grading_count_by_section'
    flag is specified, the number of submissions that need grading split out
    by section. NOTE: This key is NOT present unless you pass the 'needs_grading_count_by_section'
    argument as true. ANOTHER NOTE: it's possible to be enrolled in multiple
    sections, and if a student is setup that way they will show an assignment
    that needs grading in multiple sections (effectively the count will be duplicated
    between sections)"""
    needs_grading_count_by_section::Union{Vector,Nothing}
    """the sorting order of the assignment in the group"""
    position::Union{Int,Nothing}
    """(optional, present if Sync Grades to SIS feature is enabled)"""
    post_to_sis::Union{Bool,Nothing}
    """(optional, Third Party unique identifier for Assignment)"""
    integration_id::Union{String,Nothing}
    """(optional, Third Party integration data for assignment)"""
    integration_data::Union{Dict,Nothing}
    """For courses using Old Gradebook, indicates whether the assignment is
    muted. For courses using New Gradebook, true if the assignment has any unposted
    submissions, otherwise false. To see the posted status of submissions, check
    the 'posted_attribute' on Submission."""
    muted::Union{Bool,Nothing}
    """the maximum points possible for the assignment"""
    points_possible::Union{Float64,Nothing}
    """the types of submissions allowed for this assignment list containing
    one or more of the following: 'discussion_topic', 'online_quiz', 'on_paper',
    'none', 'external_tool', 'online_text_entry', 'online_url', 'online_upload'
    'media_recording'"""
    submission_types::Union{Vector,Nothing}
    """If true, the assignment has been submitted to by at least one student"""
    has_submitted_submissions::Union{Bool,Nothing}
    """The type of grading the assignment receives; one of 'pass_fail', 'percent',
    'letter_grade', 'gpa_scale', 'points'"""
    grading_type::Union{String,Nothing}
    """The id of the grading standard being applied to this assignment. Valid
    if grading_type is 'letter_grade' or 'gpa_scale'."""
    grading_standard_id::Union{Int,Nothing}
    """Whether the assignment is published"""
    published::Union{Bool,Nothing}
    """Whether the assignment's 'published' state can be changed to false. Will
    be false if there are student submissions for the assignment."""
    unpublishable::Union{Bool,Nothing}
    """Whether the assignment is only visible to overrides."""
    only_visible_to_overrides::Union{Bool,Nothing}
    """Whether or not this is locked for the user."""
    locked_for_user::Union{Bool,Nothing}
    """(Optional) Information for the user about the lock. Present when locked_for_user
    is true."""
    lock_info::Union{LockInfo,Nothing}
    """(Optional) An explanation of why this is locked for the user. Present
    when locked_for_user is true."""
    lock_explanation::Union{String,Nothing}
    """(Optional) id of the associated quiz (applies only when submission_types
    is ['online_quiz'])"""
    quiz_id::Union{Int,Nothing}
    """(Optional) whether anonymous submissions are accepted (applies only to
    quiz assignments)"""
    anonymous_submissions::Union{Bool,Nothing}
    """(Optional) the DiscussionTopic associated with the assignment, if applicable"""
    discussion_topic::Union{DiscussionTopic,Nothing}
    """(Optional) Boolean indicating if assignment will be frozen when it is
    copied. NOTE: This field will only be present if the AssignmentFreezer plugin
    is available for your account."""
    freeze_on_copy::Union{Bool,Nothing}
    """(Optional) Boolean indicating if assignment is frozen for the calling
    user. NOTE: This field will only be present if the AssignmentFreezer plugin
    is available for your account."""
    frozen::Union{Bool,Nothing}
    """(Optional) Array of frozen attributes for the assignment. Only account
    administrators currently have permission to change an attribute in this
    list. Will be empty if no attributes are frozen for this assignment. Possible
    frozen attributes are: title, description, lock_at, points_possible, grading_type,
    submission_types, assignment_group_id, allowed_extensions, group_category_id,
    notify_of_update, peer_reviews NOTE: This field will only be present if
    the AssignmentFreezer plugin is available for your account."""
    frozen_attributes::Union{Vector,Nothing}
    """(Optional) If 'submission' is included in the 'include' parameter, includes
    a Submission object that represents the current user's (user who is requesting
    information from the api) current submission for the assignment. See the
    Submissions API for an example response. If the user does not have a submission,
    this key will be absent."""
    submission::Union{Submission,Nothing}
    """(Optional) If true, the rubric is directly tied to grading the assignment.
    Otherwise, it is only advisory. Included if there is an associated rubric."""
    use_rubric_for_grading::Union{Bool,Nothing}
    """(Optional) An object describing the basic attributes of the rubric, including
    the point total. Included if there is an associated rubric."""
    rubric_settings::Union{String,Nothing}
    """(Optional) A list of scoring criteria and ratings for each rubric criterion.
    Included if there is an associated rubric."""
    rubric::Union{Vector,Nothing}
    """(Optional) If 'assignment_visibility' is included in the 'include' parameter,
    includes an array of student IDs who can see this assignment."""
    assignment_visibility::Union{Vector,Nothing}
    """(Optional) If 'overrides' is included in the 'include' parameter, includes
    an array of assignment override objects."""
    overrides::Union{Vector,Nothing}
    """(Optional) If true, the assignment will be omitted from the student's
    final grade"""
    omit_from_final_grade::Union{Bool,Nothing}
    """Boolean indicating if the assignment is moderated."""
    moderated_grading::Union{Bool,Nothing}
    """The maximum number of provisional graders who may issue grades for this
    assignment. Only relevant for moderated assignments. Must be a positive
    value, and must be set to 1 if the course has fewer than two active instructors.
    Otherwise, the maximum value is the number of active instructors in the
    course minus one, or 10 if the course has more than 11 active instructors."""
    grader_count::Union{Int,Nothing}
    """The user ID of the grader responsible for choosing final grades for this
    assignment. Only relevant for moderated assignments."""
    final_grader_id::Union{Int,Nothing}
    """Boolean indicating if provisional graders' comments are visible to other
    provisional graders. Only relevant for moderated assignments."""
    grader_comments_visible_to_graders::Union{Bool,Nothing}
    """Boolean indicating if provisional graders' identities are hidden from
    other provisional graders. Only relevant for moderated assignments with
    grader_comments_visible_to_graders set to true."""
    graders_anonymous_to_graders::Union{Bool,Nothing}
    """Boolean indicating if provisional grader identities are visible to the
    final grader. Only relevant for moderated assignments."""
    grader_names_visible_to_final_grader::Union{Bool,Nothing}
    """Boolean indicating if the assignment is graded anonymously. If true,
    graders cannot see student identities."""
    anonymous_grading::Union{Bool,Nothing}
    """The number of submission attempts a student can make for this assignment.
    -1 is considered unlimited."""
    allowed_attempts::Union{Int,Nothing}
    """Whether the assignment has manual posting enabled. Only relevant for
    courses using New Gradebook."""
    post_manually::Union{Bool,Nothing}
end

# GradingRules
struct GradingRules <: CanvasObject
    """Number of lowest scores to be dropped for each user."""
    drop_lowest::Union{Int,Nothing}
    """Number of highest scores to be dropped for each user."""
    drop_highest::Union{Int,Nothing}
    """Assignment IDs that should never be dropped."""
    never_drop::Union{Vector,Nothing}
end

# AssignmentGroup
struct AssignmentGroup <: CanvasObject
    """the id of the Assignment Group"""
    id::Union{Int,Nothing}
    """the name of the Assignment Group"""
    name::Union{String,Nothing}
    """the position of the Assignment Group"""
    position::Union{Int,Nothing}
    """the weight of the Assignment Group"""
    group_weight::Union{Int,Nothing}
    """the sis source id of the Assignment Group"""
    sis_source_id::Union{String,Nothing}
    """the integration data of the Assignment Group"""
    integration_data::Union{Dict,Nothing}
    """the assignments in this Assignment Group (see the Assignment API for
    a detailed list of fields)"""
    assignments::Union{Vector,Nothing}
    """the grading rules that this Assignment Group has"""
    rules::Union{GradingRules,Nothing}
end

# AssignmentOverride
struct AssignmentOverride <: CanvasObject
    """the ID of the assignment override"""
    id::Union{Int,Nothing}
    """the ID of the assignment the override applies to"""
    assignment_id::Union{Int,Nothing}
    """the IDs of the override's target students (present if the override targets
    an ad-hoc set of students)"""
    student_ids::Union{Vector,Nothing}
    """the ID of the override's target group (present if the override targets
    a group and the assignment is a group assignment)"""
    group_id::Union{Int,Nothing}
    """the ID of the overrides's target section (present if the override targets
    a section)"""
    course_section_id::Union{Int,Nothing}
    """the title of the override"""
    title::Union{String,Nothing}
    """the overridden due at (present if due_at is overridden)"""
    due_at::Union{Dates.DateTime,Nothing}
    """the overridden all day flag (present if due_at is overridden)"""
    all_day::Union{Bool,Nothing}
    """the overridden all day date (present if due_at is overridden)"""
    all_day_date::Union{Dates.DateTime,Nothing}
    """the overridden unlock at (present if unlock_at is overridden)"""
    unlock_at::Union{Dates.DateTime,Nothing}
    """the overridden lock at, if any (present if lock_at is overridden)"""
    lock_at::Union{Dates.DateTime,Nothing}
end

# Section
struct Section <: CanvasObject
    """The unique identifier for the section."""
    id::Union{Int,Nothing}
    """The name of the section."""
    name::Union{String,Nothing}
    """The sis id of the section. This field is only included if the user has
    permission to view SIS information."""
    sis_section_id::Union{String,Nothing}
    """Optional: The integration ID of the section. This field is only included
    if the user has permission to view SIS information."""
    integration_id::Union{String,Nothing}
    """The unique identifier for the SIS import if created through SIS. This
    field is only included if the user has permission to manage SIS information."""
    sis_import_id::Union{Int,Nothing}
    """The unique Canvas identifier for the course in which the section belongs"""
    course_id::Union{Int,Nothing}
    """The unique SIS identifier for the course in which the section belongs.
    This field is only included if the user has permission to view SIS information."""
    sis_course_id::Union{String,Nothing}
    """the start date for the section, if applicable"""
    start_at::Union{Dates.DateTime,Nothing}
    """the end date for the section, if applicable"""
    end_at::Union{Dates.DateTime,Nothing}
    """Restrict user enrollments to the start and end dates of the section"""
    restrict_enrollments_to_section_dates::Union{Bool,Nothing}
    """The unique identifier of the original course of a cross-listed section"""
    nonxlist_course_id::Union{Int,Nothing}
    """optional: the total number of active and invited students in the section"""
    total_students::Union{Int,Nothing}
end

# Account
struct Account <: CanvasObject
    """the ID of the Account object"""
    id::Union{Int,Nothing}
    """The display name of the account"""
    name::Union{String,Nothing}
    """The UUID of the account"""
    uuid::Union{String,Nothing}
    """The account's parent ID, or null if this is the root account"""
    parent_account_id::Union{Int,Nothing}
    """The ID of the root account, or null if this is the root account"""
    root_account_id::Union{Int,Nothing}
    """The storage quota for the account in megabytes, if not otherwise specified"""
    default_storage_quota_mb::Union{Int,Nothing}
    """The storage quota for a user in the account in megabytes, if not otherwise
    specified"""
    default_user_storage_quota_mb::Union{Int,Nothing}
    """The storage quota for a group in the account in megabytes, if not otherwise
    specified"""
    default_group_storage_quota_mb::Union{Int,Nothing}
    """The default time zone of the account. Allowed time zones are {http://www.iana.org/time-zones
    IANA time zones} or friendlier {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
    Ruby on Rails time zones}."""
    default_time_zone::Union{String,Nothing}
    """The account's identifier in the Student Information System. Only included
    if the user has permission to view SIS information."""
    sis_account_id::Union{String,Nothing}
    """The account's identifier in the Student Information System. Only included
    if the user has permission to view SIS information."""
    integration_id::Union{String,Nothing}
    """The id of the SIS import if created through SIS. Only included if the
    user has permission to manage SIS information."""
    sis_import_id::Union{Int,Nothing}
    """The account's identifier that is sent as context_id in LTI launches."""
    lti_guid::Union{String,Nothing}
    """The state of the account. Can be 'active' or 'deleted'."""
    workflow_state::Union{String,Nothing}
end


text:
# override Documenter.jl/src/Writers/HTMLWriter.jl

import Documenter: Documents, Documenter, Writers, Utilities
import Documenter.Utilities.DOM: DOM, Tag, @tags
import Documenter.Writers.HTMLWriter:
    getpage,
    render_head,
    render_navmenu,
    render_article,
    open_output,
    mdconvert,
    pagetitle,
    navhref,
    render_topbar,
    domify

#=
  1. 修改 html 的 lang 属性为 zh-cn
=#
function Documenter.Writers.HTMLWriter.render_page(ctx, navnode)
    @tags html body

    page = getpage(ctx, navnode)

    head = render_head(ctx, navnode)
    navmenu = render_navmenu(ctx, navnode)
    article = render_article(ctx, navnode)

    htmldoc = DOM.HTMLDocument(
        # 修改 lang tag
        html[:lang=>"zh-cn"](
            head,
            body(navmenu, article)
        )
    )

    open_output(ctx, navnode) do io
        print(io, htmldoc)
    end
end

#=
  1. 修改编辑翻译提示语
    - 改 icon 为 FontAwesome: fa-globe(f0ac)
    - 改 "Edit on Github" 为 “完善 Transifex 上的翻译"
    - 改为对应的 Transifex URL
  2. 修改前后翻页提示语
    - "Previous" => t_Previous
    - "Next" => t_Next
=#

const t_Edit_on_xx = " 完善 Transifex 上的翻译" # 开头加空格，避免与 icon 靠太近
const t_Icon = "\uf0ac" # fa-globe

const t_Previous = "上一篇"
const t_Next     = "下一篇"

# 生成对应文件在 Transifex 上的翻译地址
function transifex_url(rel_path)
    # 首页源文件在 GitHub 上
    if splitdir(rel_path) == splitdir("src/index.md")
        return "https://github.com/JuliaCN/JuliaZH.jl/blob/master/doc/src/index.md"
    end

    # rel_path => "src\\manual\\getting-started.md"

    paths, file_name = splitdir(rel_path)
    # paths     => "src\\manual"
    # file_name => "getting-started.md"

    _, parent_fold = splitdir(paths)
    # parent_fold => "manual"

    page_name = replace(file_name, '.' => "")
    # page_name => "getting-startedmd"

    # final URL =>
    #   "https://www.transifex.com/juliacn/manual-zh_cn/translate/#zh_CN/getting-startedmd"
    "https://www.transifex.com/juliacn/$parent_fold-zh_cn/translate/#zh_CN/$page_name"
end

# workaround on Documenter/#977
function Documenter.Writers.HTMLWriter.render_article(ctx, navnode)
    @tags article header footer nav ul li hr span a

    header_links = map(Documents.navpath(navnode)) do nn
        title = mdconvert(pagetitle(ctx, nn); droplinks=true)
        nn.page === nothing ? li(title) : li(a[:href => navhref(ctx, nn, navnode)](title))
    end

    topnav = nav(ul(header_links))

    # Set the logo and name for the "Edit on.." button.
    host_type = Utilities.repo_host_from_url(ctx.doc.user.repo)

    if !ctx.settings.disable_git # 1.3: 改 URL
        url = transifex_url(getpage(ctx, navnode).source)
        if url !== nothing
            # 1.1 & 1.2: 修改编辑翻译 icon/提示语
            push!(topnav.nodes, a[".edit-page", :href => url](span[".fa"](t_Icon), t_Edit_on_xx))
        end
    end
    art_header = header(topnav, hr(), render_topbar(ctx, navnode))

    # build the footer with nav links
    art_footer = footer(hr())
    if navnode.prev !== nothing
        direction = span[".direction"](t_Previous) # 2.1: 修改翻页提示语
        title = span[".title"](mdconvert(pagetitle(ctx, navnode.prev); droplinks=true))
        link = a[".previous", :href => navhref(ctx, navnode.prev, navnode)](direction, title)
        push!(art_footer.nodes, link)
    end

    if navnode.next !== nothing
        direction = span[".direction"](t_Next) # 2.1: 修改翻页提示语
        title = span[".title"](mdconvert(pagetitle(ctx, navnode.next); droplinks=true))
        link = a[".next", :href => navhref(ctx, navnode.next, navnode)](direction, title)
        push!(art_footer.nodes, link)
    end

    pagenodes = domify(ctx, navnode)
    article["#docs"](art_header, pagenodes, art_footer)
end


text:
#= src/spfft1.jl
=#

abstract type SpFFTPlan1{T,K} end

const NB = 128  # default block size; actual used can be set on FFT execution

function spfft_blkdiv(n::Integer, k::Integer)
  l = k
  while n % l > 0
    l -= 1
  end
  m = div(n, l)
  l, m
end

function spfft_chkidx(n::Integer, idx::AbstractVector{T}) where {T<:Integer}
  n > 0 || throw(ArgumentError("n"))
  for i in idx
    (1 <= i <= n) || throw(DomainError())
  end
end

spfft_size(P::SpFFTPlan1) = (size(P.X)..., length(P.w))

# complex transforms
# - underlying FFT is c2c but can also take real input/output
# -- if real  input, will convert from real to complex
# -- if real output, will take real part
# - note: (r2c, f2s) and (c2r, s2f) can be further optimized

struct cSpFFTPlan1{T<:SpFFTComplex,K} <: SpFFTPlan1{T,K}
  X::Matrix{T}
  F::FFTPlan{T,K}
  w::Vector{T}
  col::Vector{Int}
  p::Vector{Int}
end

spfft_rc(::Type{T}, x) where {T<:Real   } = real(x)  # no inexact error check
spfft_rc(::Type{T}, x) where {T<:Complex} =      x

for (f, K) in ((:fft, FORWARD), (:bfft, BACKWARD))
  sf  = Symbol("sp", f)
  psf = Symbol("plan_", sf)
  pf  = Symbol("plan_", f, "!")
  f2s = Symbol(sf, "_f2s!")
  s2f = Symbol(sf, "_s2f!")
  @eval begin
    function $psf(::Type{T}, n::Integer, idx::AbstractVector{Ti};
                  args...) where {T<:SpFFTComplex,Ti<:Integer}
      spfft_chkidx(n, idx)
      k = length(idx)
      l, m = spfft_blkdiv(n, k)
      X = Array{T}(undef, l, m)
      F = $pf(X, 1; args...)
      wm = exp(2im*$K*pi/m)
      wn = exp(2im*$K*pi/n)
      w   = Array{T  }(undef, k)
      col = Array{Int}(undef, k)
      @inbounds for i = 1:k
        rowm1, colm1 = divrem(idx[i]-1, l)
        w[i] = wm^rowm1 * wn^colm1
        col[i] = colm1 + 1
      end
      p = sortperm(col)
      permute!(  w, p)
      permute!(col, p)
      cSpFFTPlan1{T,$K}(X, F, w, col, p)
    end

    function $f2s(y::AbstractVector{Ty}, P::cSpFFTPlan1{T,K},
                  x::AbstractVector{Tx}; nb::Integer=$NB) where {T,K,Tx,Ty}
      l, m, k = spfft_size(P)
      length(x) == l*m || throw(DimensionMismatch)
      length(y) == k   || throw(DimensionMismatch)
      transpose!(P.X, reshape(x,(m,l)))
      P.F*P.X
      nb = min(nb, k)
      t = Array{Ty}(undef, nb)
      s = Array{T }(undef, nb)
      idx = 0
      while idx < k
        fill!(t, 0)
        fill!(s, 1)
        nbi = min(nb, k-idx)
        @inbounds for j = 1:m
          for i = 1:nbi
            ii = idx + i
            t[i] += spfft_rc(Ty, s[i]*P.X[P.col[ii],j])
            s[i] *= P.w[ii]
          end
        end
        @inbounds for i = 1:nbi
          y[P.p[idx+i]] = t[i]
        end
        idx += nbi
      end
      y
    end

    function $s2f(y::AbstractVector{Ty}, P::cSpFFTPlan1{T,K},
                  x::AbstractVector{Tx}; nb::Integer=$NB) where {T,K,Tx,Ty}
      l, m, k = spfft_size(P)
      length(x) == k   || throw(DimensionMismatch)
      length(y) == l*m || throw(DimensionMismatch)
      fill!(P.X, 0)
      nb = min(nb, k)
      t = Array{Tx}(undef, nb)
      s = Array{T }(undef, nb)
      idx = 0
      while idx < k
        fill!(s, 1)
        nbi = min(nb, k-idx)
        @inbounds for i = 1:nbi
          t[i] = x[P.p[idx+i]]
        end
        @inbounds for j = 1:m
          for i = 1:nbi
            ii = idx + i
            P.X[P.col[ii],j] += s[i]*t[i]
            s[i] *= P.w[ii]
          end
        end
        idx += nbi
      end
      P.F*P.X
      transpose_f!(z->spfft_rc(Ty,z), reshape(y,(m,l)), P.X)
    end
  end
end

# real transforms

## (r2c, f2s)

struct rSpFFTPlan1{T<:SpFFTReal} <: SpFFTPlan1{T,FORWARD}
  X::Matrix{T}
  Xc::Matrix{Complex{T}}
  F::FFTPlan{T,FORWARD}
  w::Vector{Complex{T}}
  col::Vector{Int}
  p::Vector{Int}
end

function sprfft_fullcomplex(X::Matrix{T}, i::Integer, j::Integer, n::Integer,
                            inyq::Integer) where {T<:SpFFTComplex}
  if i <= inyq  return      X[  i  ,j]
  else          return conj(X[n-i+2,j])  # hermitian conjugate
  end
end

function plan_sprfft(::Type{T}, n::Integer, idx::AbstractVector{Ti};
                     args...) where {T<:SpFFTReal,Ti<:Integer}
  spfft_chkidx(n, idx)
  k = length(idx)
  l, m = spfft_blkdiv(n, k)
  lc = div(l, 2) + 1
  Tc = Complex{T}
  X  = Array{T }(undef, l , m)
  Xc = Array{Tc}(undef, lc, m)
  F = plan_rfft(X, 1; args...)
  wm = exp(-2im*pi/m)
  wn = exp(-2im*pi/n)
  w   = Array{Tc }(undef, k)
  col = Array{Int}(undef, k)
  @inbounds for i = 1:k
    rowm1, colm1 = divrem(idx[i]-1, l)
    w[i] = wm^rowm1 * wn^colm1
    col[i] = colm1 + 1
  end
  p = sortperm(col)
  permute!(  w, p)
  permute!(col, p)
  rSpFFTPlan1{T}(X, Xc, F, w, col, p)
end

function sprfft_f2s!(
    y::AbstractVector{Ty}, P::rSpFFTPlan1{T}, x::AbstractVector{Tx};
    nb::Integer=NB) where {T,Tx<:Real,Ty<:Complex}
  l, m, k = spfft_size(P)
  length(x) == l*m || throw(DimensionMismatch)
  length(y) == k   || throw(DimensionMismatch)
  transpose!(P.X, reshape(x,(m,l)))
  mul!(P.Xc, P.F, P.X)
  nb = min(nb, k)
  Tc = Complex{T}
  t = Array{Ty}(undef, nb)
  s = Array{Tc}(undef, nb)
  nyq = size(P.Xc, 1)
  idx = 0
  while idx < k
    fill!(t, 0)
    fill!(s, 1)
    nbi = min(nb, k-idx)
    @inbounds for j = 1:m
      for i = 1:nbi
        ii = idx + i
        z = sprfft_fullcomplex(P.Xc, P.col[ii], j, l, nyq)
        t[i] += s[i]*z
        s[i] *= P.w[ii]
      end
    end
    @inbounds for i = 1:nbi
      y[P.p[idx+i]] = t[i]
    end
    idx += nbi
  end
  y
end

## (c2r, s2f)
## - input must contain only nonredundant frequencies, i.e., up to index n/2 + 1
##   for a full signal of size n

struct brSpFFTPlan1{T<:SpFFTReal,Tc<:SpFFTComplex} <: SpFFTPlan1{T,BACKWARD}
  X::Matrix{T}
  Xc::Matrix{Tc}
  F::FFTPlan{Tc,BACKWARD}
  k::Int
  w::Vector{Tc}
  col::Vector{Int}
  p::Vector{Int}
end

spbrfft_size(P::brSpFFTPlan1) = (spfft_size(P)..., P.k)

function plan_spbrfft(::Type{T}, n::Integer, idx::AbstractVector{Ti};
                      args...) where {T<:SpFFTReal,Ti<:Integer}
  nyqm1 = div(n, 2)
  spfft_chkidx(nyqm1+1, idx)
  k = length(idx)
  l, m = spfft_blkdiv(n, k)
  lc = div(l, 2) + 1
  Tc = Complex{T}
  X  = Array{T }(undef, l , m)
  Xc = Array{Tc}(undef, lc, m)
  F = plan_brfft(Xc, l, 1; args...)
  wm = exp(2im*pi/m)
  wn = exp(2im*pi/n)
  w   = Array{Tc }(undef, 0)
  col = Array{Int}(undef, 0)
  xp  = Array{Int}(undef, 0)
  sizehint!(  w, k)
  sizehint!(col, k)
  sizehint!( xp, k)
  @inbounds for i = 1:k
    idxm1 = idx[i] - 1
    rowm1, colm1 = divrem(idxm1, l)
    if colm1 < lc
      push!(w, wm^rowm1 * wn^colm1)
      push!(col, colm1 + 1)
      push!(xp, i)
    end
    (idxm1 == 0 || (n % 2 == 0 && idxm1 == nyqm1)) && continue
    idxm1 = n - idxm1
    rowm1, colm1 = divrem(idxm1, l)
    if colm1 < lc
      push!(w, wm^rowm1 * wn^colm1)
      push!(col, colm1 + 1)
      push!(xp, -i)
    end
  end
  p = sortperm(col)
  permute!(  w, p)
  permute!(col, p)
  permute!( xp, p)
  brSpFFTPlan1{T,Tc}(X, Xc, F, k, w, col, xp)
end

function spbrfft_s2f!(
    y::AbstractVector{Ty}, P::brSpFFTPlan1{T}, x::AbstractVector{Tx};
    nb::Integer=NB) where {T,Tx<:Complex,Ty<:Real}
  l, m, kc, k = spbrfft_size(P)
  length(x) == k   || throw(DimensionMismatch)
  length(y) == l*m || throw(DimensionMismatch)
  fill!(P.Xc, 0)
  nb = min(nb, kc)
  Tc = Complex{T}
  t = Array{Tx}(undef, nb)
  s = Array{Tc}(undef, nb)
  idx = 0
  while idx < kc
    fill!(s, 1)
    nbi = min(nb, kc-idx)
    @inbounds for i = 1:nbi
      p = P.p[idx+i]
      t[i] = p > 0 ? x[p] : conj(x[-p])
    end
    @inbounds for j = 1:m
      for i = 1:nbi
        ii = idx + i
        P.Xc[P.col[ii],j] += s[i]*t[i]
        s[i] *= P.w[ii]
      end
    end
    idx += nbi
  end
  mul!(P.X, P.F, P.Xc)
  transpose!(reshape(y,(m,l)), P.X)
  y
end


text:
"""
    planepoint(point::Point, triangle::Polygon)

Take a triangular plane as a Polygon and a Point within that triangle
and returns the z-value at that point.

# Examples
```jldoctest
julia> point = Point([-175, 22])
Point([-175.0, 22.0])

julia> triangle = Polygon([[-174.55, 32.54, 55], [-186.94, 17.64, 24.5], [-167.95, 17.81, 33.6], [-174.55, 32.54, 55]])
Polygon(Array{Array{Float64,1},1}[[[-174.55, 32.54, 55.0], [-186.94, 17.64, 24.5], [-167.95, 17.81, 33.6], [-174.55, 32.54, 55.0]]])

julia> planepoint(point, triangle)
37.28550123965308
```
"""
function planepoint(point::Point, triangle::Polygon)
    pCoords = point.coordinates
    tCoords = triangle.coordinates

    outer = tCoords[1]
    length(outer) < 4 && throw(error("The outer ring of a Polygon must have 4 or more Points."))

    x = pCoords[1]
    y = pCoords[2]

    x1 = outer[1][1]
    y1 = outer[1][2]
    z1 = outer[1][3]

    x2 = outer[2][1]
    y2 = outer[2][2]
    z2 = outer[2][3]

    x3 = outer[3][1]
    y3 = outer[3][2]
    z3 = outer[3][3]

    return (z3 * (x - x1) * (y - y2) + z1 * (x - x2) * (y - y3) + z2 * (x - x3) * (y - y1) -
            z2 * (x - x1) * (y - y3) - z3 * (x - x2) * (y - y1) - z1 * (x - x3) * (y - y2)) /
           ((x - x1) * (y - y2) + (x - x2) * (y - y3) + (x - x3) * (y - y1) -
           (x - x1) * (y - y3) - (x - x2) * (y - y1) - (x - x3) * (y - y2))
end


text:
using NMRCalibrate
using Test

@testset "NMRCalibrate.jl" begin
    # Write your tests here.
end


text:
function X1()
    X1 = Dict{Symbol,Float64}()

    # Mass and Moments of Inertia
    X1[:G]   = 9.80665                                                 # standard (Earth) gravity (m/s^2)
    X1[:mfl] = 484                                                     # weight at front left wheel (kg)
    X1[:mfr] = 455                                                     # weight at front right wheel (kg)
    X1[:mrl] = 521                                                     # weight at rear left wheel (kg)
    X1[:mrr] = 504                                                     # weight at rear right wheel (kg)
    X1[:m]   = X1[:mfl] + X1[:mfr] + X1[:mrl] + X1[:mrr]               # total vehicle mass (kg)
    X1[:Ixx] = 175                                                     # pitch moment of inertia (kg*m^2)
    X1[:Iyy] = 1000                                                    # roll moment of inertia (kg*m^2)
    X1[:Izz] = 2900                                                    # yaw moment of inertia (kg*m^2)

    # Dimensions
    X1[:L]  = 2.87                                                     # wheelbase (m)
    X1[:d]  = 1.63                                                     # track width (m)
    X1[:a]  = (X1[:mrl] + X1[:mrr])/X1[:m]*X1[:L]                      # distance from CG to front axle (m)
    X1[:b]  = (X1[:mfl] + X1[:mfr])/X1[:m]*X1[:L]                      # distance from CG to rear axle (m)
    X1[:hf] = 0.1                                                      # front roll center height (m)
    X1[:hr] = 0.1                                                      # rear roll center height (m)
    X1[:h1] = 0.37                                                     # CG height w.r.t. line connecting front and rear roll centers (m)
    X1[:h]  = X1[:hf]*X1[:b]/X1[:L] + X1[:hr]*X1[:a]/X1[:L] + X1[:h1]  # CG height (m)
    X1[:ab] = X1[:a] + 0.4953                                          # distance from CG to front bumper (m)
    X1[:bb] = X1[:b] + 0.4318                                          # distance from CG to rear bumper (m)
    X1[:w]  = 1.87                                                     # physical width (m)

    # Tire Model Parameters
    X1[:μ] = 0.92                                                      # coefficient of friction
    X1[:Cαf] = 150e3 # 140e3                                           # front tire (pair) cornering stiffness (N/rad)
    X1[:Cαr] = 220e3 # 190e3                                           # rear tire (pair) cornering stiffness (N/rad)

    # Longitudinal Actuation Limits
    X1[:Fx_max] = 5600                                                 # max positive longitudinal force (N)
    X1[:Px_max] = 75e3                                                 # max motor power output (W)

    # Longitudinal Drag Force Parameters (FxDrag = Cd0 + Cd1*Ux + Cd2*Ux^2)
    X1[:Cd0] = 241.0                                                   # rolling resistance (N)
    X1[:Cd1] = 25.1                                                    # linear drag coefficint (N/(m/s))
    X1[:Cd2] = 0.0                                                     # quadratic "aero" drag coefficint (N/(m/s)^2)

    # Drive and Brake Distribution
    X1[:fwd_frac] = 0.0                                                # front wheel drive fraction for implementing desired Fx
    X1[:rwd_frac] = 1 - X1[:fwd_frac]                                  # rear wheel drive fraction for implementing desired Fx
    X1[:fwb_frac] = 0.6                                                # front wheel brake fraction for implementing desired Fx
    X1[:rwb_frac] = 1 - X1[:fwb_frac]                                  # rear wheel brake fraction for implementing desired Fx

    # Computed Longitudinal Limits
    X1[:Fx_min] = max(-X1[:m]*X1[:G]*X1[:a]*X1[:μ]/(X1[:L]*X1[:rwb_frac] + X1[:μ]*X1[:h]),    # brake force corresponding to first of
                      -X1[:m]*X1[:G]*X1[:b]*X1[:μ]/(X1[:L]*X1[:fwb_frac] - X1[:μ]*X1[:h]))    # the front or rear tire saturating (N)
    X1[:Ax_max] = X1[:Fx_max]/X1[:m]                                   # longitudinal acceleration corresponding to Fx_max (m/s^2)
    X1[:Ax_min] = X1[:Fx_min]/X1[:m]                                   # longitudinal acceleration corresponding to Fx_min (m/s^2)

    # Steering Limits
    X1[:δ_max] = 18*pi/180                                             # maximum steering angle, 2 wheel mode (radians)
    X1[:κ_max] = tan(X1[:δ_max])/X1[:L]                                # maximum curvature, relevant at low speeds (1/m)

    X1
end


text:
function find(pairs :: Vector{Vector{Int}}, n :: Int)
    for idx in 1:length(pairs)
        if pairs[idx][1] == n
            return idx
        end
    end
end

function part1()
    fish = [parse(Int, i) for i in split(readline("inputD6.txt"), ",")]

    for day in 1:80
        fish .-= 1
        for n in 1:sum(fish .== -1)
            push!(fish, 8)
        end
        replace!(fish, -1 => 6)
    end

    return length(fish)
end

function part2()
    fish = [parse(Int, i) for i in split(readline("inputD6.txt"), ",")]

    totals = [[i, 0] for i in 0:8]
    for f in fish
        totals[find(totals, f)][2] += 1
    end

    for day in 1:256
        for pair in totals
            pair[1] -= 1
        end

        totals[find(totals, 6)][2] += totals[find(totals, -1)][2]
        totals[find(totals, -1)][1] = 8

    end

    return sum([i[2] for i in totals])

end

println(part1())
println(part2())


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

if !isdefined(@__MODULE__, :DefsBotId)
    const DefsBotId = String
else
    @warn("Skipping redefinition of DefsBotId to String")
end


text:
module MyIOModule

using DelimitedFiles

export readtodict
export niceprint
export nicewrite
export replacelineswithstring

function readtodict(filename::String,allowed)
    println("")
    f = open(filename)
    vars = Dict{String, Any}()
    println("Reading config from $filename")
    for line in eachline(f)

        # skip comments
        if length(line) == 0 || line[1] == '#'
            #println("skip comment line")
            continue
        end
        print("\t");
        println(line)

        var, val = split(line, "=")
        stripped = strip(var)
        if stripped in allowed
            vars[stripped] = parse(Float64, val)
        end
    end
    println("")
    return vars
end

function niceprint(A)
    println("")
    #Base.showarray(STDOUT,A,false)
	println(A)
    println("")
end

function nicewrite(filename::String,A)
    writedlm(filename, A, ", ")
end

function replacelineswithstring(filename::String,newfilename::String,keywords::Array{String},replaceby::Array{String})
# replaces every line,  that contains the "keyword" with a line that consists of "replaceby"
# result is written into a new file called "newfilename"

# file from which to read
f = open(filename)
lines = readlines(f)

# file to which we write
open(newfilename,"w") do tmpf
    for l in lines
        containssomething = false
        matchid = 0
        for k=1:size(keywords,1)
            if occursin(keywords[k],l)
                matchid = k
                containssomething = true
            end
        end
        if !(containssomething)
            write(tmpf,l*"\n")
        else
            write(tmpf,replaceby[matchid]*"\n")
        end
    end
end

end

end

max_stars_repo_name: camminady/SN

max_issues_repo_name: camminady/SN

max_forks_repo_name: camminady/SN


text:
# ---
# title: 797. All Paths From Source to Target
# id: problem797
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Backtracking, Depth-first Search, Graph
# link: <https://leetcode.com/problems/all-paths-from-source-to-target/description/>
# hidden: true
# ---
#
# Given a directed acyclic graph ( **DAG** ) of `n` nodes labeled from 0 to n -
# 1, find all possible paths from node `0` to node `n - 1`, and return them in
# any order.
#
# The graph is given as follows: `graph[i]` is a list of all nodes you can visit
# from node `i` (i.e., there is a directed edge from node `i` to node
# `graph[i][j]`).
#
#
#
# **Example 1:**
#
# ![](https://assets.leetcode.com/uploads/2020/09/28/all_1.jpg)
#
#
#
#     Input: graph = [[1,2],[3],[3],[]]
#     Output: [[0,1,3],[0,2,3]]
#     Explanation: There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3.
#
#
# **Example 2:**
#
# ![](https://assets.leetcode.com/uploads/2020/09/28/all_2.jpg)
#
#
#
#     Input: graph = [[4,3,1],[3,2,4],[3],[4],[]]
#     Output: [[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
#
#
# **Example 3:**
#
#
#
#     Input: graph = [[1],[]]
#     Output: [[0,1]]
#
#
# **Example 4:**
#
#
#
#     Input: graph = [[1,2,3],[2],[3],[]]
#     Output: [[0,1,2,3],[0,2,3],[0,3]]
#
#
# **Example 5:**
#
#
#
#     Input: graph = [[1,3],[2],[3],[]]
#     Output: [[0,1,2,3],[0,3]]
#
#
#
#
# **Constraints:**
#
#   * `n == graph.length`
#   * `2 <= n <= 15`
#   * `0 <= graph[i][j] < n`
#   * `graph[i][j] != i` (i.e., there will be no self-loops).
#   * The input graph is **guaranteed** to be a **DAG**.
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end