text:
module MappedArrays

using Base: @propagate_inbounds

export AbstractMappedArray, MappedArray, ReadonlyMappedArray, mappedarray, of_eltype

abstract type AbstractMappedArray{T,N} <: AbstractArray{T,N} end
abstract type AbstractMultiMappedArray{T,N} <: AbstractMappedArray{T,N} end

struct ReadonlyMappedArray{T,N,A<:AbstractArray,F} <: AbstractMappedArray{T,N}
    f::F
    data::A
end
struct MappedArray{T,N,A<:AbstractArray,F,Finv} <: AbstractMappedArray{T,N}
    f::F
    finv::Finv
    data::A
end
struct ReadonlyMultiMappedArray{T,N,AAs<:Tuple{Vararg{AbstractArray}},F} <: AbstractMultiMappedArray{T,N}
    f::F
    data::AAs

    function ReadonlyMultiMappedArray{T,N,AAs,F}(f, data) where {T,N,AAs,F}
        inds = axes(first(data))
        checkinds(inds, Base.tail(data)...)
        new(f, data)
    end
end
struct MultiMappedArray{T,N,AAs<:Tuple{Vararg{AbstractArray}},F,Finv} <: AbstractMultiMappedArray{T,N}
    f::F
    finv::Finv
    data::AAs

    function MultiMappedArray{T,N,AAs,F,Finv}(f::F, finv::Finv, data) where {T,N,AAs,F,Finv}
        inds = axes(first(data))
        checkinds(inds, Base.tail(data)...)
        new(f, finv, data)
    end
end

@inline function checkinds(inds, A, As...)
    @noinline throw1(i, j) = throw(DimensionMismatch("arrays do not all have the same axes (got $i and $j)"))
    iA = axes(A)
    iA == inds || throw1(inds, iA)
    checkinds(inds, As...)
end
checkinds(inds) = nothing

"""
    M = mappedarray(f, A)
    M = mappedarray(f, A, B, C...)

Create a view `M` of the array `A` that applies `f` to every element
of `A`; `M == map(f, A)`, with the difference that no storage
is allocated for `M`. The view is read-only (you can get values but
not set them).

When multiple input arrays are supplied, `M[i] = f(A[i], B[i], C[i]...)`.
"""
function mappedarray(f, data::AbstractArray)
    infer_eltype() = Base._return_type(f, eltypes(data))
    T = infer_eltype()
    ReadonlyMappedArray{T,ndims(data),typeof(data),typeof(f)}(f, data)
end

function mappedarray(::Type{T}, data::AbstractArray) where T
    ReadonlyMappedArray{T,ndims(data),typeof(data),Type{T}}(T, data)
end

function mappedarray(f, data::AbstractArray...)
    infer_eltype() = Base._return_type(f, eltypes(data))
    T = infer_eltype()
    ReadonlyMultiMappedArray{T,ndims(first(data)),typeof(data),typeof(f)}(f, data)
end

function mappedarray(::Type{T}, data::AbstractArray...) where T
    ReadonlyMultiMappedArray{T,ndims(first(data)),typeof(data),Type{T}}(T, data)
end

"""
    M = mappedarray(f, finv, A)
    M = mappedarray(f, finv, A, B, C...)

creates a view of the array `A` that applies `f` to every element of
`A`. The inverse function, `finv`, allows one to also set values of
the view and, correspondingly, the values in `A`.

When multiple input arrays are supplied, `M[i] = f(A[i], B[i], C[i]...)`.
"""
function mappedarray(f, finv, data::AbstractArray)
    infer_eltype() = Base._return_type(f, eltypes(data))
    T = infer_eltype()
    MappedArray{T,ndims(data),typeof(data),typeof(f),typeof(finv)}(f, finv, data)
end

function mappedarray(f, finv, data::AbstractArray...)
    infer_eltype() = Base._return_type(f, eltypes(data))
    T = infer_eltype()
    MultiMappedArray{T,ndims(first(data)),typeof(data),typeof(f),typeof(finv)}(f, finv, data)
end

function mappedarray(::Type{T}, finv, data::AbstractArray...) where T
    MultiMappedArray{T,ndims(first(data)),typeof(data),Type{T},typeof(finv)}(T, finv, data)
end
function mappedarray(f, ::Type{Finv}, data::AbstractArray...) where Finv
    infer_eltype() = Base._return_type(f, eltypes(data))
    T = infer_eltype()
    MultiMappedArray{T,ndims(first(data)),typeof(data),typeof(f),Type{Finv}}(f, Finv, data)
end

function mappedarray(::Type{T}, ::Type{Finv}, data::AbstractArray...) where {T,Finv}
    MultiMappedArray{T,ndims(first(data)),typeof(data),Type{T},Type{Finv}}(T, Finv, data)
end

"""
    M = of_eltype(T, A)
    M = of_eltype(val::T, A)

creates a view of `A` that lazily-converts the element type to `T`.
"""
of_eltype(::Type{T}, data::AbstractArray{S}) where {S,T} = mappedarray(x->convert(T,x)::T, y->convert(S,y)::S, data)
of_eltype(::Type{T}, data::AbstractArray{T}) where {T} = data
of_eltype(::T, data::AbstractArray{S}) where {S,T} = of_eltype(T, data)

Base.parent(A::AbstractMappedArray) = A.data
Base.size(A::AbstractMappedArray) = size(A.data)
Base.size(A::AbstractMultiMappedArray) = size(first(A.data))
Base.axes(A::AbstractMappedArray) = axes(A.data)
Base.axes(A::AbstractMultiMappedArray) = axes(first(A.data))
parenttype(::Type{ReadonlyMappedArray{T,N,A,F}}) where {T,N,A,F} = A
parenttype(::Type{MappedArray{T,N,A,F,Finv}}) where {T,N,A,F,Finv} = A
parenttype(::Type{ReadonlyMultiMappedArray{T,N,A,F}}) where {T,N,A,F} = A
parenttype(::Type{MultiMappedArray{T,N,A,F,Finv}}) where {T,N,A,F,Finv} = A
Base.IndexStyle(::Type{MA}) where {MA<:AbstractMappedArray} = IndexStyle(parenttype(MA))
@inline Base.IndexStyle(M::AbstractMultiMappedArray) = IndexStyle(M.data...)
Base.IndexStyle(::Type{MA}) where {MA<:AbstractMultiMappedArray} = _indexstyle(MA)
Base.@pure _indexstyle(::Type{MA}) where {MA<:AbstractMultiMappedArray} =
    _indexstyle(map(IndexStyle, parenttype(MA).parameters)...)
_indexstyle(a, b, c...) = _indexstyle(IndexStyle(a, b), c...)
_indexstyle(a, b) = IndexStyle(a, b)

# IndexLinear implementations
@propagate_inbounds Base.getindex(A::AbstractMappedArray, i::Int) =
    A.f(A.data[i])
@propagate_inbounds Base.getindex(M::AbstractMultiMappedArray, i::Int) =
    M.f(_getindex(i, M.data...)...)

@propagate_inbounds function Base.setindex!(A::MappedArray{T},
                                            val,
                                            i::Int) where {T}
    A.data[i] = A.finv(convert(T, val)::T)
end
@propagate_inbounds function Base.setindex!(A::MultiMappedArray{T},
                                            val,
                                            i::Int) where {T}
    vals = A.finv(convert(T, val)::T)
    _setindex!(A.data, vals, i)
    return vals
end

# IndexCartesian implementations
@propagate_inbounds function Base.getindex(A::AbstractMappedArray{T,N},
                                           i::Vararg{Int,N}) where {T,N}
    A.f(A.data[i...])
end
@propagate_inbounds function Base.getindex(A::AbstractMultiMappedArray{T,N},
                                           i::Vararg{Int,N}) where {T,N}
    A.f(_getindex(CartesianIndex(i), A.data...)...)
end

@propagate_inbounds function Base.setindex!(A::MappedArray{T,N},
                                            val,
                                            i::Vararg{Int,N}) where {T,N}
    A.data[i...] = A.finv(convert(T, val)::T)
end
@propagate_inbounds function Base.setindex!(A::MultiMappedArray{T,N},
                                            val,
                                            i::Vararg{Int,N}) where {T,N}
    vals = A.finv(convert(T, val)::T)
    _setindex!(A.data, vals, i...)
    return vals
end

@propagate_inbounds _getindex(i, A, As...) = (A[i], _getindex(i, As...)...)
_getindex(i) = ()

@propagate_inbounds function _setindex!(as::As, vals::Vs, inds::Vararg{Int,N}) where {As,Vs,N}
    a1, atail = as[1], Base.tail(as)
    v1, vtail = vals[1], Base.tail(vals)
    a1[inds...] = v1
    return _setindex!(atail, vtail, inds...)
end
_setindex!(as::Tuple{}, vals::Tuple{}, inds::Vararg{Int,N}) where N = nothing

function testvalue(data)
    if !isempty(data)
        first(data)
    else
        zero(eltype(data))
    end::eltype(data)
end

## Display

function Base.showarg(io::IO, A::AbstractMappedArray{T,N}, toplevel=false) where {T,N}
    print(io, "mappedarray(")
    func_print(io, A.f, eltypes(A.data))
    if isa(A, Union{MappedArray,MultiMappedArray})
        print(io, ", ")
        func_print(io, A.finv, Tuple{T})
    end
    if isa(A, AbstractMultiMappedArray)
        for a in A.data
            print(io, ", ")
            Base.showarg(io, a, false)
        end
    else
        print(io, ", ")
        Base.showarg(io, A.data, false)
    end
    print(io, ')')
    toplevel && print(io, " with eltype ", T)
end

function func_print(io, f, types)
    ft = typeof(f)
    mt = ft.name.mt
    name = string(mt.name)
    if startswith(name, '#')
        # This is an anonymous function. See if it can be printed nicely
        lwrds = code_lowered(f, types)
        if length(lwrds) != 1
            show(io, f)
            return nothing
        end
        lwrd = lwrds[1]
        c = lwrd.code
        if length(c) == 2 && ((isa(c[2], Expr) && c[2].head === :return) || (isdefined(Core, :ReturnNode) && isa(c[2], Core.ReturnNode)))
            # This is a single-line anonymous function, we should handle it
            s = lwrd.slotnames[2:end]
            if length(s) == 1
                print(io, s[1], "->")
            else
                print(io, tuple(s...), "->")
            end
            c1 = string(c[1])
            for i = 1:length(s)
                c1 = replace(c1, "_"*string(i+1)=>string(s[i]))
            end
            print(io, c1)
        else
            show(io, f)
        end
    else
        show(io, f)
    end
end

eltypes(A::AbstractArray) = Tuple{eltype(A)}
@Base.pure eltypes(A::Tuple{Vararg{<:AbstractArray}}) = Tuple{(eltype.(A))...}

## Deprecations
@deprecate mappedarray(f_finv::Tuple{Any,Any}, args::AbstractArray...) mappedarray(f_finv[1], f_finv[2], args...)

end # module


text:
export sc_all, process_sc!, stream_autocorr

function sc_all(
	mseedfiles::AbstractArray,
	fs::AbstractFloat,
	cc_len::Real,
	cc_step::Real,
	freqmin::AbstractFloat,
	freqmax::AbstractFloat,
	maxlag::AbstractFloat,
	XMLDIR::String;
	responsefreq::Real=0.1,
)

	# read instrument response only once
	RESP = read_resp(mseedfiles[1],XMLDIR)
	ind = findfirst(contains.(RESP.id,".BH"))
	RESP = RESP[ind]

	p = replace(basename(mseedfiles[1]),".ms"=>"")
	pstr = join(split(p,"_",keepempty=false)," ")
	println("Auto-correlation $pstr $(now())")
	S = read_data("mseed",mseedfiles)

	process_sc!(
		S,
		fs,
		RESP,
		responsefreq=responsefreq,
	)
	F1 = seischannel2fft(S[1],cc_len,cc_step,freqmin,freqmax)
	F2 = seischannel2fft(S[2],cc_len,cc_step,freqmin,freqmax)
	F3 = seischannel2fft(S[3],cc_len,cc_step,freqmin,freqmax)

	C1 = correlate(F1,F2,maxlag)
	robuststack!(C1)
	C2 = correlate(F1,F3,maxlag)
	robuststack!(C2)
	C3 = correlate(F2,F3,maxlag)
	robuststack!(C3)
    return C1, C2, C3
end

function stream_autocorr(
	mseedfiles::AbstractArray,
	fs::AbstractFloat,
	cc_len::Real,
	cc_step::Real,
	freqmin::AbstractFloat,
	freqmax::AbstractFloat,
	maxlag::AbstractFloat,
	RESP::SeisData;
	responsefreq::Real=0.1,
	maxgaps::Int=100,
	aws_config::AWSConfig=global_aws_config(),
	bucket::String="scedc-pds",
	filetype::String="mseed",
	resp::Bool=true,
	t_max::Real=20.,
	factor::Real=10,
)
	# print net station channel and date
	p = replace(mseedfiles[1],".ms"=>"")
	println("Auto-correlation $p $(now())")

	# stream from S3
	S = SeisData()
	try
		if filetype == "mseed"
			S += mseed_stream(aws_config, bucket, mseedfiles)
		elseif filetype == "seisio"
			S += seisio_stream(aws_config, bucket, mseedfiles)
		end
	catch e
		julday = replace(basename(dirname(mseedfiles[1])),"_"=>"")
		d = yyyyjjj2date(julday)
		C = nancorr(d,fs,maxlag)
		return C,C,C
	end

	# short circuit if lots of gaps in data
	ngaps = get_gaps(S)
	if maximum(ngaps) > maxgaps
		julday = replace(basename(dirname(mseedfiles[1])),"_"=>"")
		d = yyyyjjj2date(julday)
		C = nancorr(d,fs,maxlag)
		return C,C,C
	end

	starttimes = [SeisIO.starttime(S.t[ii],S.fs[ii]) for ii = 1:S.n]
	endtimes = [SeisIO.endtime(S.t[ii],S.fs[ii]) for ii = 1:S.n]
	s = minimum(starttimes)
	t = maximum(endtimes)

	# sync instrument response time
	R = sync_resp(RESP,s,t)
	if length(R) == 0
		R = RESP[1]
	else
		R = R[1]
	end

	# remove instrument response
	process_sc!(
		S,
		fs,
		R,
		responsefreq=responsefreq,
		resp=resp,
		t_max=t_max,
		factor=factor,
	)

	if all(isnan.(S.x[1])) || length(S) != 3
		julday = replace(basename(dirname(mseedfiles[1])),"_"=>"")
		d = yyyyjjj2date(julday)
		C = nancorr(d,fs,maxlag)
		return C,C,C
	end

	# convert to FFT
	F1 = seischannel2fft(S[1],cc_len,cc_step,freqmin,freqmax)
	F2 = seischannel2fft(S[2],cc_len,cc_step,freqmin,freqmax)
	F3 = seischannel2fft(S[3],cc_len,cc_step,freqmin,freqmax)

	# correlate
	C1 = correlate(F1,F2,maxlag)
	robuststack!(C1)
	C2 = correlate(F1,F3,maxlag)
	robuststack!(C2)
	C3 = correlate(F2,F3,maxlag)
	robuststack!(C3)
    return C1,C2,C3
end

function process_sc!(
	S::SeisData,
	fs::Real,
	RESP::SeisChannel;
	responsefreq::Real=0.1,
	resp::Bool=true,
	t_max::Real=20.,
	factor::Real=10.,
)

	# check sampling rate for corrupted files
	S.fs = round.(S.fs)

	# if can't merge, return NaNs
	try
		merge!(S)
		sort!(S)
		taper!(S,t_max=t_max)
		ungap!(S,m=false)
		demean!(S)
		detrend!(S)
	catch e
		for ii = 1:S.n
			S.x[ii] .= NaN
		end
		return S
	end

	# find and replace nans with zeros
	nans = Dict()
	for ii = 1:S.n
		nans[ii] = findall(isnan.(S.x[ii]))
		S.x[ii][nans[ii]] .= 0
	end

	# highpass filter
	highpass!(S,responsefreq,zerophase=true,corners=2)

	# replace gaps with zeros
	for ii = 1:S.n
		S.x[ii][nans[ii]] .= 0
	end

	# resample
	resample!(S,fs=fs)

	# find and replace nans with zeros
	nans = Dict()
	for ii = 1:S.n
		nans[ii] = findall(isnan.(S.x[ii]))
		S.x[ii][nans[ii]] .= 0
	end

	# phase shift if necessary
	allt = vcat(S.t...)[1:2:end,2]
	if all(t->t==allt[1],allt)
		ϕshift = false
	else
		ϕshift = true
	end
    phase_shift!(S, ϕshift=ϕshift)

	# remove instrument response
	if resp
		for ii = 1:S.n
			S.loc[ii] = RESP.loc
			S.gain[ii] = RESP.gain
			S.resp[ii] = RESP.resp
		end
		remove_resp!(S)
	end

	# setup taper windows
	winsamples = round(Int,t_max * S.fs[1])
	winsamples += iseven(winsamples)
	window = SeisIO.hanning(2 * winsamples - 1)
	leftwin = window[winsamples:end]
	rightwin = window[1:winsamples]

	# find outlier points and taper
	for ii = 1:S.n
		# find points 10x greater than MAD
		fm = fast_mad(S.x[ii], round(Int,length(S.x[ii]) / 100)) * factor
		ind = findall(abs.(S.x[ii]) .> fm)
		if length(ind) <= 1
			continue
		end

		# # get smooth version of envelope
		# eup, edown = envelope(S.x[ii])
		# smooth!(eup,round(Int,S.fs[1] / 2))

		# # get new fm based on envelope
		# fm = fast_mad(eup)
		# ind = findall(eup .> 2 * fm)

		# if length(ind) <= 1
		# 	continue
		# end

		leftind = [ind[1]; ind[findall(diff(ind) .> 1) .+ 1]]
		rightind = [ind[findall(diff(ind) .> 1)]; ind[end]]

		# taper high amplitudes on the left
		for jj = 1:length(rightind)
			if rightind[jj] + winsamples + 1 > length(S.x[ii])
				continue
			end
			S.x[ii][rightind[jj]+1:rightind[jj]+winsamples] .*= rightwin
		end

		# taper high amplitudes on the right
		for jj = 1:length(leftind)
			if leftind[jj] - winsamples < 1
				continue
			end
			S.x[ii][leftind[jj]-winsamples:leftind[jj]-1] .*= leftwin
		end

		# set all points between right and left indices to zero
		for jj = 1:length(leftind)
			S.x[ii][leftind[jj]:rightind[jj]] .= 0
		end
	end

	# replace gaps with zeros
	for ii = 1:S.n
		S.x[ii][nans[ii]] .= 0
	end

	return nothing
end

function seischannel2fft(
	C::SeisChannel,
	cc_len::Real,
	cc_step::Real,
	freqmin::Real,
	freqmax::Real;
)

	# convert to RawData
	R = RawData(C,cc_len,cc_step)
	ind = findall(abs.(R.x) .< fast_mad2d(R.x) ./ 1000)
	detrend!(R)
	taper!(R)
	# highpass!(R,freqmin,zerophase=true)
	# whiten!(R,freqmin,freqmax)
	R.x[ind] .= 0
	onebit!(R)
	return rfft(R)
end

ext: jl


text:
#=
Copyright 2021 BlackRock, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=#

@testset "Identity constrained QP" begin
    P = Diagonal(trues(4))  # === I(4) in 1.2+
    q = ones(4)
    A = spzeros(1, 4)
    b = [1.0]
    g = repeat([zero(PiecewiseQuadratic)], 4)
    params = AdmmParams(P, q, A, b, g)

    vars, _ = optimize(params)

    @test norm(vars.x - (-1) * ones(4)) < 1e-5
end

@testset "Diag constrained QP" begin
    P = spdiagm(0 => 1:4)
    q = Vector{Float64}(1:4)
    A = spzeros(1, 4) .+ 1
    b = [1.0]
    g = repeat([zero(PiecewiseQuadratic)], 4)
    params = AdmmParams(P, q, A, b, g)

    vars, _ = optimize(params)

    @test norm(vars.x - [1.4, 0.2, -0.2, -0.4]) < 1e-5
end

@testset "Test with ρ and σ" begin
    P = [65.0 76.0; 76.0 89.0]
    q = [7.0, 3.0]
    A = [7.0 8.0; 5.0 4.0]
    b = [8.0, 8.0]
    g = repeat([zero(PiecewiseQuadratic)], 2)
    params = AdmmParams(P, q, A, b, g)

    settings = Settings(; ρ=[0.2, 2.0], σ=[2.0, 2.0], ϵ=1e-5)

    vars, _ = optimize(params, settings)

    @test norm(vars.x - [2.66666651, -1.33333319]) < 1e-5
end

@testset "Test with g_i intervals containing feasible point" begin
    P = [5.0 3.0; 3.0 2.0]
    q = [1.0, 2.0]
    A = [1.0 1.0]
    b = [-3.0]
    g = repeat([zero(PiecewiseQuadratic)], 2)
    params = AdmmParams(P, q, A, b, g)

    settings = Settings(; ρ=[0.2], σ=[2.0, 2.0], ϵ=1e-6)

    vars, _ = optimize(params, settings)

    @test norm(vars.x - [4.0, -7.0]) < 1e-5
end

@testset "Test with g_i quadratic" begin
    P = [5.0 3.0; 3.0 2.0]
    q = [1.0, 2.0]
    A = [1.0 1.0; 1.0 2.0]
    b = [1.0, 1.0]
    g = [PiecewiseQuadratic(BoundedQuadratic(1, 0, 0)), zero(PiecewiseQuadratic)]
    params = AdmmParams(P, q, A, b, g)

    settings = Settings(; ρ=[5.0, 5.0], σ=[5.0, 2.0], ϵ=1e-6)

    vars, _ = optimize(params, settings)

    @test norm(vars.x - [1.0, 0.0]) < 1e-5
end


text:
const rocfftComplexes = Union{ComplexF32, ComplexF64}
const rocfftReals = Union{Float32, Float64}

rocfftfloat(::Type{T}) where {T} = error("type $T not supported")
rocfftfloat(::Type{Float16}) = FLoat32
rocfftfloat(::Type{T}) where {T<:rocfftReals} = T
rocfftfloat(::Type{Complex{T}}) where {T} = Complex{rocfftfloat(T)}

complexfloat(x::ROCArray{Complex{T}}) where {T<:rocfftReals} = x
complexfloat(x::ROCArray{T}) where {T<:Complex} = copy1(rocfftfloat(T), x)
complexfloat(x::ROCArray{T}) where {T<:Real} = copy1(complex(rocfftfloat(T)), x)

realfloat(x::ROCArray{T}) where {T<:Real} = copy1(rocfftfloat(T), x)

# TODO: use undef alloco
function copy1(::Type{T}, x) where T
    y = ROCArray(T, map(length, axes(x)))
    y .= broadcast(xi->convert(T,xi), x)
end


text:
"""
    mutable struct AdaptationPathImputation <: eMCMC.Adaptation
        proposed::Int64
        accepted::Int64
        target_accpt_rate::Float64
        adapt_every_k_steps::Int64
        scale::Float64
        min::Float64
        max::Float64
        offset::Float64
    end

A struct containing information about the way in which to adapt the memory
parameter of the preconditioned Crank–Nicolson scheme. `proposed` and `accepted`
are the internal counters that keep track of the number of proposed and accepted
samples. `target_accpt_rate` is the acceptance rate of the Metropolis-Hastings
steps that is supposed to be targetted. `min` is used to enforce the minimal
allowable range that the random walker can sample from, `max` enforces the
maximum. `offset` introduces some delay in the start of decelerting the
adaptation extent and `scale` is a scaling parameter for adaptation speed.
"""
mutable struct AdaptationPathImputation <: eMCMC.Adaptation
    proposed::Int64
    accepted::Int64
    target_accpt_rate::Float64
    adapt_every_k_steps::Int64
    scale::Float64
    min::Float64
    max::Float64
    offset::Float64

    function AdaptationPathImputation(; kwargs...)
        trgt_ar = get(kwargs, :target_accpt_rate, 0.234)
        steps = get(kwargs, :adapt_every_k_steps, 100)
        scale = get(kwargs, :scale, 1.0)
        min = get(kwargs, :min, 1e-12)
        max = get(kwargs, :max, 1.0 - 1e-12)
        offset = get(kwargs, :offset, 1e2)
        new(0, 0, trgt_ar, steps, scale, min, max, offset)
    end
end

"""
    acceptance_rate(adpt::AdaptationPathImputation)

Compute current acceptance rate of the Metropolis-Hastings update step
"""
eMCMC.acceptance_rate(adpt::AdaptationPathImputation) = (
    (adpt.proposed == 0) ? 0.0 : adpt.accepted/adpt.proposed
)

"""
    acceptance_rate!(adpt::AdaptationPathImputation)

Destructive computation of a current acceptance rate that also resets the
number of proposals and accepted samples to zeros.
"""
function eMCMC.acceptance_rate!(adpt::AdaptationPathImputation)
    a_r = eMCMC.acceptance_rate(adpt)
    reset!(adpt)
    a_r
end

"""
    reset!(adpt::AdaptationPathImputation)

Reset the number of proposals and accepted samples to zero.
"""
function DataStructures.reset!(adpt::AdaptationPathImputation)
    adpt.proposed = 0
    adpt.accepted = 0
end

"""
    readjust!(
        imp::PathImputation, adpt::AdaptationPathImputation, mcmc_iter, i=1
    )

Adaptive readjustment for the `i`th recording of the preconditioned
Crank–Nicolson scheme's memory parameter.
"""
function eMCMC.readjust!(
        imp::PathImputation, adpt::AdaptationPathImputation, mcmc_iter, i=1
    )
    δ = eMCMC.compute_δ(adpt, mcmc_iter)
    a_r = eMCMC.acceptance_rate!(adpt)
    ϵ = eMCMC.compute_ϵ(first(imp.ρs[i]), adpt, a_r, δ, -1.0, logit, sigmoid)
    imp.ρs[i] .= ϵ
    ϵ
end

sigmoid(x, a=1.0) = 1.0 / (1.0 + exp(-a*x))
logit(x, a=1.0) = (log(x) - log(1-x))/a

"""
    register!(updt, adpt::AdaptationPathImputation, accepted::Bool, ::Any)

Register the result of the acceptance decision in the Metropolis-Hastings step.
"""
function eMCMC.register!(updt, adpt::AdaptationPathImputation, accepted::Bool, ::Any)
    adpt.accepted += accepted
    adpt.proposed += 1
end

"""
    time_to_update(::Val{true}, adpt::AdaptationPathImputation)

Return true if it's the time to update the memory parameter of the
preconditioned Crank–Nicolson scheme.
"""
function eMCMC.time_to_update(::Val{true}, adpt::AdaptationPathImputation)
    adpt.proposed >= adpt.adapt_every_k_steps
end
#===============================================================================

                        AdaptationMultiPathImputation

        A struct that gathers multiple AdaptationPathImputatation—one
        for each recording
===============================================================================#

struct AdaptationMultiPathImputation <: eMCMC.Adaptation
    v::Vector{AdaptationPathImputation}

    AdaptationMultiPathImputation(adpt::AdaptationPathImputation) = new([adpt])
end

function eMCMC.register!(
        updt,
        adpt::AdaptationMultiPathImputation,
        accepted::Vector{Bool},
        θ
    )
    for i in eachindex(accepted)
        eMCMC.register!(updt, adpt.v[i], accepted[i], θ)
    end
end

function eMCMC.time_to_update(v::Val{true}, adpt::AdaptationMultiPathImputation)
    eMCMC.time_to_update(v, adpt.v[1])
end

function eMCMC.readjust!(
        imp::PathImputation, adpt::AdaptationMultiPathImputation, mcmc_iter
    )
    for i in eachindex(adpt.v)
        eMCMC.readjust!(imp, adpt.v[i], mcmc_iter, i)
    end
end

init_path_imp_adpt(adpt::NoAdaptation) = (adpt, NoAdaptation)

init_path_imp_adpt(adpt::AdaptationMultiPathImputation) = (adpt, typeof(adpt))

function init_path_imp_adpt(adpt::AdaptationPathImputation)
    _adpt = AdaptationMultiPathImputation(adpt)
    _adpt, typeof(_adpt)
end

resize_adpt!(adpt::NoAdaptation, num_recordings) = nothing

function resize_adpt!(adpt::AdaptationMultiPathImputation, num_recordings)
    Δ = num_recordings - length(adpt.v)
    for i in 1:Δ
        push!(adpt.v, deepcopy(adpt.v[1]))
    end
end


text:
module CookieRequest

import ..Dates
using URIs
using ..Cookies
using ..Messages: Request, ascii_lc_isequal, header, setheader
import ..@debug, ..DEBUG_LEVEL, ..access_threaded

const default_cookiejar = Dict{String, Set{Cookie}}[]

function __init__()
    resize!(empty!(default_cookiejar), Threads.nthreads())
    return
end

export cookielayer

"""
    cookielayer(req) -> HTTP.Response

Add locally stored Cookies to the request headers.
Store new Cookies found in the response headers.
"""
function cookielayer(handler)
    return function(req::Request; cookies=true, cookiejar::Dict{String, Set{Cookie}}=access_threaded(Dict{String, Set{Cookie}}, default_cookiejar), kw...)
        if cookies === true || (cookies isa AbstractDict && !isempty(cookies))
            url = req.url
            hostcookies = get!(cookiejar, url.host, Set{Cookie}())
            cookiestosend = getcookies(hostcookies, url)
            if !(cookies isa Bool)
                for (name, value) in cookies
                    push!(cookiestosend, Cookie(name, value))
                end
            end
            if !isempty(cookiestosend)
                existingcookie = header(req.headers, "Cookie")
                if existingcookie != "" && get(req.context, :includedCookies, nothing) !== nothing
                    # this is a redirect where we previously included cookies
                    # we want to filter those out to avoid duplicate cookie sending
                    # and the case where a cookie was set to expire from the 1st request
                    previouscookies = Cookies.readcookies(req.headers, "")
                    previouslyincluded = req.context[:includedCookies]
                    filtered = filter(x -> !(x.name in previouslyincluded), previouscookies)
                    existingcookie = stringify("", filtered)
                end
                setheader(req.headers, "Cookie" => stringify(existingcookie, cookiestosend))
                req.context[:includedCookies] = map(x -> x.name, cookiestosend)
            end
            res = handler(req; kw...)
            setcookies(hostcookies, url.host, res.headers)
            return res
        else
            # skip
            return handler(req; kw...)
        end
    end
end

function getcookies(cookies, url)

    tosend = Vector{Cookie}()
    expired = Vector{Cookie}()

    # Check if cookies should be added to outgoing request based on host...
    for cookie in cookies
        if Cookies.shouldsend(cookie, url.scheme == "https",
                              url.host, url.path)
            t = cookie.expires
            if t != Dates.DateTime(1) && t < Dates.now(Dates.UTC)
                @debug 1 "Deleting expired Cookie: $cookie.name"
                push!(expired, cookie)
            else
                @debug 1 "Sending Cookie: $cookie.name to $url.host"
                push!(tosend, cookie)
            end
        end
    end
    setdiff!(cookies, expired)
    return tosend
end

function setcookies(cookies, host, headers)
    for (k, v) in headers
        ascii_lc_isequal(k, "set-cookie") || continue
        @debug 1 "Set-Cookie: $v (from $host)"
        push!(cookies, Cookies.readsetcookie(host, v))
    end
end

end # module CookieRequest


text:
"""
`annualtoquarter(v)`

Convert from annual to quarter frequency... by dividing by 4.
"""
function annualtoquarter(v)
    v / 4
end

"""
`nominal_to_real(col, df; deflator_mnemonic=:GDPCTPI)`

Converts nominal to real values using the specified deflator.

## Arguments
- `col`: symbol indicating which column of `df` to transform
- `df`: DataFrame containining series for proper population measure and `col`

## Keyword arguments

- `deflator_mnemonic`: indicates which deflator to use to calculate real values. Default
  value is the FRED GDP Deflator mnemonic.
"""
function nominal_to_real(col, df; deflator_mnemonic=:GDPCTPI)
    return df[col] ./ df[deflator_mnemonic]
end

"""
```
percapita(m, col, df)
percapita(col, df, population_mnemonic)
```

Converts data column `col` of DataFrame `df` to a per-capita value.

## Arguments
- `col`: symbol indicating which column of data to transform
- `df`: DataFrame containining series for proper population measure and `col`
- `population_mnemonic`: a mnemonic found in df for some population measure.
"""
function percapita(m::AbstractModel, col::Symbol, df::DataFrame)
    population_mnemonic = get_setting(m, :population_mnemonic)
    percapita(col, df, population_mnemonic)
end
function percapita(col::Symbol, df::DataFrame, population_mnemonic::Symbol)
    df[col] ./ df[population_mnemonic]
end

"""
```
yt, yf = hpfilter(y, λ::Real)
```

Applies the Hodrick-Prescott filter ("H-P filter"). The smoothing parameter `λ` is applied
to the columns of `y`, returning the trend component `yt` and the cyclical component `yf`.
For quarterly data, one can use λ=1600.

Consecutive missing values at the beginning or end of the time series are excluded from the
filtering. If there are missing values within the series, the filtered values are all NaN.

See also:
```
Hodrick, Robert; Prescott, Edward C. (1997). "Postwar U.S. Business Cycles: An Empirical
Investigation". Journal of Money, Credit, and Banking 29 (1): 1–16.
```
"""
function hpfilter(y, λ::Real)
    # Convert y to vector
    if !isa(y, Vector)
        try
            y = vec(y)
        catch
            error("Series must be convertible to Vector")
        end
    end

    # Indices of consecutive NaN elements at beginning
    i = 1
    j = length(y)
    while isnan(y[i])
        i = i+1
    end
    while isnan(y[j])
        j = j-1
    end

    # Filter and adjust for NaNs
    yt_, yf_ = hpfilter_(y[i:j], λ)
    yt = [fill(NaN, i-1); yt_; fill(NaN, length(y)-j)]
    yf = [fill(NaN, i-1); yf_; fill(NaN, length(y)-j)]

    return yt, yf
end

function hpfilter_{T<:Real}(y::Vector{T}, λ::Real)
    n = length(y)
    a = spzeros(n,n)
    for i = 3:n-2
        a[i,i]   = 6λ+1
        a[i,i+1] = -4λ
        a[i,i+2] = λ
        a[i,i-2] = λ
        a[i,i-1] = -4λ
    end

    a[2,2] = 1+5λ
    a[2,3] = -4λ
    a[2,4] = λ
    a[2,1] = -2λ
    a[1,1] = 1+λ
    a[1,2] = -2λ
    a[1,3] = λ

    a[n-1,n-1] = 1+5λ
    a[n-1,n-2] = -4λ
    a[n-1,n-3] = λ
    a[n-1,n]   = -2λ
    a[n,n]     = 1+λ
    a[n,n-1]   = -2λ
    a[n,n-2]   = λ

    yt = a\y
    yf = y-yt

    return yt, yf
end

"""
```
difflog(x::Vector{AbstractFloat})
```
"""
function difflog{T<:AbstractFloat}(x::Vector{T})
    [NaN; log(x[2:end]) - log(x[1:end-1])]
end

"""
```
difflog(x::DataArray{AbstractFloat})
```
"""
function difflog(x::DataArray)
    DSGE.na2nan!(x)
    y = convert(Vector{Float64}, x)
    return difflog(y)
end

"""
```
oneqtrpctchange(y)
```

Calculates the quarter-to-quarter percentage change of a series.
"""
function oneqtrpctchange(y)
    100 * difflog(y)
end

"""
```
hpadjust(y, df)
```

Adjust series to compensate for differences between filtered and unfiltered population.
## Arguments
- `y`: A vector of data
- `df`: DataFrame containing both a filtered and unfiltered population growth series
"""
function hpadjust(y, df; filtered_mnemonic=:filtered_population_growth,
                         unfiltered_mnemonic=:unfiltered_population_growth)
    y + 100 * (df[unfiltered_mnemonic] - df[filtered_mnemonic])
end


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

## printing with color ##

const text_colors = Dict{Union{Symbol,Int},String}(
    :black         => "\033[30m",
    :red           => "\033[31m",
    :green         => "\033[32m",
    :yellow        => "\033[33m",
    :blue          => "\033[34m",
    :magenta       => "\033[35m",
    :cyan          => "\033[36m",
    :white         => "\033[37m",
    :light_black   => "\033[90m", # gray
    :light_red     => "\033[91m",
    :light_green   => "\033[92m",
    :light_yellow  => "\033[93m",
    :light_blue    => "\033[94m",
    :light_magenta => "\033[95m",
    :light_cyan    => "\033[96m",
    :normal        => "\033[0m",
    :default       => "\033[39m",
    :bold          => "\033[1m",
    :underline     => "\033[4m",
    :blink         => "\033[5m",
    :reverse       => "\033[7m",
    :hidden        => "\033[8m",
    :nothing       => "",
)

for i in 0:255
    text_colors[i] = "\033[38;5;$(i)m"
end

const disable_text_style = Dict{Symbol,String}(
    :bold      => "\033[22m",
    :underline => "\033[24m",
    :blink     => "\033[25m",
    :reverse   => "\033[27m",
    :hidden    => "\033[28m",
    :normal    => "",
    :default   => "",
    :nothing   => "",
)

# Create a docstring with an automatically generated list
# of colors.
let color_syms = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors))),
    formatting_syms = [:normal, :bold, :default]
    global const available_text_colors = cat(
        sort!(intersect(color_syms, formatting_syms), rev=true),
        sort!(setdiff(  color_syms, formatting_syms));
        dims=1)
end

const available_text_colors_docstring =
    string(join([string("`:", key,"`")
                 for key in available_text_colors], ",\n", ", or \n"))

"""Dictionary of color codes for the terminal.

Available colors are: $available_text_colors_docstring as well as the integers 0 to 255 inclusive.

The color `:default` will print text in the default color while the color `:normal`
will print text with all text properties (like boldness) reset.
Printing with the color `:nothing` will print the string without modifications.
"""
text_colors

function with_output_color(@nospecialize(f::Function), color::Union{Int, Symbol}, io::IO, args...;
        bold::Bool = false, underline::Bool = false, blink::Bool = false,
        reverse::Bool = false, hidden::Bool = false)
    buf = IOBuffer()
    iscolor = get(io, :color, false)::Bool
    try f(IOContext(buf, io), args...)
    finally
        str = String(take!(buf))
        if !iscolor
            print(io, str)
        else
            bold && color === :bold && (color = :nothing)
            underline && color === :underline && (color = :nothing)
            blink && color === :blink && (color = :nothing)
            reverse && color === :reverse && (color = :nothing)
            hidden && color === :hidden && (color = :nothing)
            enable_ansi  = get(text_colors, color, text_colors[:default]) *
                               (bold ? text_colors[:bold] : "") *
                               (underline ? text_colors[:underline] : "") *
                               (blink ? text_colors[:blink] : "") *
                               (reverse ? text_colors[:reverse] : "") *
                               (hidden ? text_colors[:hidden] : "")

            disable_ansi = (hidden ? disable_text_style[:hidden] : "") *
                           (reverse ? disable_text_style[:reverse] : "") *
                           (blink ? disable_text_style[:blink] : "") *
                           (underline ? disable_text_style[:underline] : "") *
                           (bold ? disable_text_style[:bold] : "") *
                               get(disable_text_style, color, text_colors[:default])
            first = true
            for line in split(str, '\n')
                first || print(buf, '\n')
                first = false
                isempty(line) && continue
                print(buf, enable_ansi, line, disable_ansi)
            end
            print(io, String(take!(buf)))
        end
    end
end

"""
    printstyled([io], xs...; bold::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Symbol,Int}=:normal)

Print `xs` in a color specified as a symbol or integer, optionally in bold.

`color` may take any of the values $(Base.available_text_colors_docstring)
or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors.
If the keyword `bold` is given as `true`, the result will be printed in bold.
If the keyword `underline` is given as `true`, the result will be printed underlined.
If the keyword `blink` is given as `true`, the result will blink.
If the keyword `reverse` is given as `true`, the result will have foreground and background colors inversed.
If the keyword `hidden` is given as `true`, the result will be hidden.
Keywords can be given in any combination.
"""
printstyled(io::IO, msg...; bold::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) =
    with_output_color(print, color, io, msg...; bold=bold, underline=underline, blink=blink, reverse=reverse, hidden=hidden)
printstyled(msg...; bold::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) =
    printstyled(stdout, msg...; bold=bold, underline=underline, blink=blink, reverse=reverse, hidden=hidden, color=color)

"""
    Base.julia_cmd(juliapath=joinpath(Sys.BINDIR::String, julia_exename()))

Return a julia command similar to the one of the running process.
Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`,
`--compiled-modules`, `--inline`, `--check-bounds`, `--optimize`, `-g`,
`--code-coverage`, `--track-allocation`, `--color`, `--startup-file`, and `--depwarn`
command line arguments that are not at their default values.

Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently.

!!! compat "Julia 1.1"
    Only the `--cpu-target`, `--sysimage`, `--depwarn`, `--compile` and `--check-bounds` flags were propagated before Julia 1.1.

!!! compat "Julia 1.5"
    The flags `--color` and `--startup-file` were added in Julia 1.5.
"""
function julia_cmd(julia=joinpath(Sys.BINDIR::String, julia_exename()))
    opts = JLOptions()
    cpu_target = unsafe_string(opts.cpu_target)
    image_file = unsafe_string(opts.image_file)
    addflags = String[]
    let compile = if opts.compile_enabled == 0
                      "no"
                  elseif opts.compile_enabled == 2
                      "all"
                  elseif opts.compile_enabled == 3
                      "min"
                  else
                      "" # default = "yes"
                  end
        isempty(compile) || push!(addflags, "--compile=$compile")
    end
    let depwarn = if opts.depwarn == 1
                      "yes"
                  elseif opts.depwarn == 2
                      "error"
                  else
                      "" # default = "no"
                  end
        isempty(depwarn) || push!(addflags, "--depwarn=$depwarn")
    end
    let check_bounds = if opts.check_bounds == 1
                      "yes" # on
                  elseif opts.check_bounds == 2
                      "no" # off
                  else
                      "" # "default"
                  end
        isempty(check_bounds) || push!(addflags, "--check-bounds=$check_bounds")
    end
    opts.can_inline == 0 && push!(addflags, "--inline=no")
    opts.use_compiled_modules == 0 && push!(addflags, "--compiled-modules=no")
    opts.opt_level == 2 || push!(addflags, "-O$(opts.opt_level)")
    opts.opt_level_min == 0 || push!(addflags, "--min-optlevel=$(opts.opt_level_min)")
    push!(addflags, "-g$(opts.debug_level)")
    if opts.code_coverage != 0
        # Forward the code-coverage flag only if applicable (if the filename is pid-dependent)
        coverage_file = (opts.output_code_coverage != C_NULL) ?  unsafe_string(opts.output_code_coverage) : ""
        if isempty(coverage_file) || occursin("%p", coverage_file)
            if opts.code_coverage == 1
                push!(addflags, "--code-coverage=user")
            elseif opts.code_coverage == 2
                push!(addflags, "--code-coverage=all")
            end
            isempty(coverage_file) || push!(addflags, "--code-coverage=$coverage_file")
        end
    end
    if opts.malloc_log == 1
        push!(addflags, "--track-allocation=user")
    elseif opts.malloc_log == 2
        push!(addflags, "--track-allocation=all")
    end
    if opts.color == 1
        push!(addflags, "--color=yes")
    elseif opts.color == 2
        push!(addflags, "--color=no")
    end
    if opts.startupfile == 2
        push!(addflags, "--startup-file=no")
    end
    return `$julia -C$cpu_target -J$image_file $addflags`
end

function julia_exename()
    if ccall(:jl_is_debugbuild, Cint, ()) == 0
        return @static Sys.iswindows() ? "julia.exe" : "julia"
    else
        return @static Sys.iswindows() ? "julia-debug.exe" : "julia-debug"
    end
end

"""
    securezero!(o)

`securezero!` fills the memory associated with an object `o` with zeros.
Unlike `fill!(o,0)` and similar code, which might be optimized away by
the compiler for objects about to be discarded, the `securezero!` function
will always be called.
"""
function securezero! end
@noinline securezero!(a::AbstractArray{<:Number}) = fill!(a, 0)
@noinline unsafe_securezero!(p::Ptr{T}, len::Integer=1) where {T} =
    ccall(:memset, Ptr{T}, (Ptr{T}, Cint, Csize_t), p, 0, len*sizeof(T))
unsafe_securezero!(p::Ptr{Cvoid}, len::Integer=1) = Ptr{Cvoid}(unsafe_securezero!(Ptr{UInt8}(p), len))

"""
    Base.getpass(message::AbstractString) -> Base.SecretBuffer

Display a message and wait for the user to input a secret, returning an `IO`
object containing the secret.

Note that on Windows, the secret might be displayed as it is typed; see
`Base.winprompt` for securely retrieving username/password pairs from a
graphical interface.
"""
function getpass end

if Sys.iswindows()
function getpass(input::TTY, output::IO, prompt::AbstractString)
    input === stdin || throw(ArgumentError("getpass only works for stdin"))
    print(output, prompt, ": ")
    flush(output)
    s = SecretBuffer()
    plen = 0
    while true
        c = UInt8(ccall(:_getch, Cint, ()))
        if c == 0xff || c == UInt8('\n') || c == UInt8('\r')
            break # EOF or return
        elseif c == 0x00 || c == 0xe0
            ccall(:_getch, Cint, ()) # ignore function/arrow keys
        elseif c == UInt8('\b') && plen > 0
            plen -= 1 # delete last character on backspace
        elseif !iscntrl(Char(c)) && plen < 128
            write(s, c)
        end
    end
    return seekstart(s)
end
else
function getpass(input::TTY, output::IO, prompt::AbstractString)
    (input === stdin && output === stdout) || throw(ArgumentError("getpass only works for stdin"))
    msg = string(prompt, ": ")
    unsafe_SecretBuffer!(ccall(:getpass, Cstring, (Cstring,), msg))
end
end

# allow new getpass methods to be defined if stdin has been
# redirected to some custom stream, e.g. in IJulia.
getpass(prompt::AbstractString) = getpass(stdin, stdout, prompt)

"""
    prompt(message; default="") -> Union{String, Nothing}

Displays the `message` then waits for user input. Input is terminated when a newline (\\n)
is encountered or EOF (^D) character is entered on a blank line. If a `default` is provided
then the user can enter just a newline character to select the `default`.

See also `Base.getpass` and `Base.winprompt` for secure entry of passwords.
"""
function prompt(input::IO, output::IO, message::AbstractString; default::AbstractString="")
    msg = !isempty(default) ? "$message [$default]: " : "$message: "
    print(output, msg)
    uinput = readline(input, keep=true)
    isempty(uinput) && return nothing  # Encountered an EOF
    uinput = chomp(uinput)
    isempty(uinput) ? default : uinput
end

# allow new prompt methods to be defined if stdin has been
# redirected to some custom stream, e.g. in IJulia.
prompt(message::AbstractString; default::AbstractString="") = prompt(stdin, stdout, message, default=default)

# Windows authentication prompt
if Sys.iswindows()
    struct CREDUI_INFO
        cbSize::UInt32
        parent::Ptr{Cvoid}
        pszMessageText::Ptr{UInt16}
        pszCaptionText::Ptr{UInt16}
        banner::Ptr{Cvoid}
    end

    const CREDUIWIN_GENERIC                 = 0x0001
    const CREDUIWIN_IN_CRED_ONLY            = 0x0020
    const CREDUIWIN_ENUMERATE_CURRENT_USER  = 0x0200

    const CRED_PACK_GENERIC_CREDENTIALS     = 0x0004

    const ERROR_SUCCESS                     = 0x0000
    const ERROR_CANCELLED                   = 0x04c7

    function winprompt(message, caption, default_username; prompt_username = true)
        # Step 1: Create an encrypted username/password bundle that will be used to set
        #         the default username (in theory could also provide a default password)
        credbuf = Vector{UInt8}(undef, 1024)
        credbufsize = Ref{UInt32}(sizeof(credbuf))
        succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool,
            (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}),
             CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize)
        if !succeeded
            credbuf = resize!(credbuf, credbufsize[])
            succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool,
                (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}),
                 CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize)
            @assert succeeded
        end

        # Step 2: Create the actual dialog
        #      2.1: Set up the window
        messageArr = Base.cwstring(message)
        captionArr = Base.cwstring(caption)
        pfSave = Ref{Bool}(false)
        cred = Ref{CREDUI_INFO}(CREDUI_INFO(sizeof(CREDUI_INFO), C_NULL, pointer(messageArr), pointer(captionArr), C_NULL))
        dwflags = CREDUIWIN_GENERIC | CREDUIWIN_ENUMERATE_CURRENT_USER
        if !prompt_username
            # Disable setting anything other than default_username
            dwflags |= CREDUIWIN_IN_CRED_ONLY
        end
        authPackage = Ref{Culong}(0)
        outbuf_data = Ref{Ptr{Cvoid}}(C_NULL)
        outbuf_size = Ref{Culong}(0)

        #      2.2: Do the actual request
        code = ccall((:CredUIPromptForWindowsCredentialsW, "credui.dll"), stdcall, UInt32, (Ptr{CREDUI_INFO}, UInt32, Ptr{Culong},
            Ptr{Cvoid}, Culong, Ptr{Ptr{Cvoid}}, Ptr{Culong}, Ptr{Bool}, UInt32), cred, 0, authPackage, credbuf, credbufsize[],
            outbuf_data, outbuf_size, pfSave, dwflags)

        #      2.3: If that failed for any reason other than the user canceling, error out.
        #           If the user canceled, just return nothing
        code == ERROR_CANCELLED && return nothing
        windowserror(:winprompt, code != ERROR_SUCCESS)

        # Step 3: Convert encrypted credentials back to plain text
        passbuf = Vector{UInt16}(undef, 1024)
        passlen = Ref{UInt32}(length(passbuf))
        usernamebuf = Vector{UInt16}(undef, 1024)
        usernamelen = Ref{UInt32}(length(usernamebuf))
        # Need valid buffers for domain, even though we don't care
        dummybuf = Vector{UInt16}(undef, 1024)
        succeeded = ccall((:CredUnPackAuthenticationBufferW, "credui.dll"), Bool,
            (UInt32, Ptr{Cvoid}, UInt32, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}),
            0, outbuf_data[], outbuf_size[], usernamebuf, usernamelen, dummybuf, Ref{UInt32}(1024), passbuf, passlen)
        windowserror(:winprompt, !succeeded)

        # Step 4: Free the encrypted buffer
        # ccall(:SecureZeroMemory, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), outbuf_data[], outbuf_size[]) - not an actual function
        unsafe_securezero!(outbuf_data[], outbuf_size[])
        ccall((:CoTaskMemFree, "ole32.dll"), Cvoid, (Ptr{Cvoid},), outbuf_data[])

        # Done.
        passbuf_ = passbuf[1:passlen[]-1]
        result = (String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])),
                  SecretBuffer!(transcode(UInt8, passbuf_)))
        securezero!(passbuf_)
        securezero!(passbuf)

        return result
    end

end

unsafe_crc32c(a, n, crc) = ccall(:jl_crc32c, UInt32, (UInt32, Ptr{UInt8}, Csize_t), crc, a, n)

_crc32c(a::NTuple{<:Any, UInt8}, crc::UInt32=0x00000000) =
    unsafe_crc32c(Ref(a), length(a) % Csize_t, crc)
_crc32c(a::Union{Array{UInt8},FastContiguousSubArray{UInt8,N,<:Array{UInt8}} where N}, crc::UInt32=0x00000000) =
    unsafe_crc32c(a, length(a) % Csize_t, crc)

_crc32c(s::String, crc::UInt32=0x00000000) = unsafe_crc32c(s, sizeof(s) % Csize_t, crc)

function _crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000)
    nb < 0 && throw(ArgumentError("number of bytes to checksum must be ≥ 0, got $nb"))
    # use block size 24576=8192*3, since that is the threshold for
    # 3-way parallel SIMD code in the underlying jl_crc32c C function.
    buf = Vector{UInt8}(undef, min(nb, 24576))
    while !eof(io) && nb > 24576
        n = readbytes!(io, buf)
        crc = unsafe_crc32c(buf, n, crc)
        nb -= n
    end
    return unsafe_crc32c(buf, readbytes!(io, buf, min(nb, length(buf))), crc)
end
_crc32c(io::IO, crc::UInt32=0x00000000) = _crc32c(io, typemax(Int64), crc)
_crc32c(io::IOStream, crc::UInt32=0x00000000) = _crc32c(io, filesize(io)-position(io), crc)
_crc32c(uuid::UUID, crc::UInt32=0x00000000) =
    ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt128}, Csize_t), crc, uuid.value, 16)
_crc32c(x::UInt64, crc::UInt32=0x00000000) =
    ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt64}, Csize_t), crc, x, 8)

"""
    @kwdef typedef

This is a helper macro that automatically defines a keyword-based constructor for the type
declared in the expression `typedef`, which must be a `struct` or `mutable struct`
expression. The default argument is supplied by declaring fields of the form `field::T =
default` or `field = default`. If no default is provided then the keyword argument becomes
a required keyword argument in the resulting type constructor.

Inner constructors can still be defined, but at least one should accept arguments in the
same form as the default inner constructor (i.e. one positional argument per field) in
order to function correctly with the keyword outer constructor.

!!! compat "Julia 1.1"
    `Base.@kwdef` for parametric structs, and structs with supertypes
    requires at least Julia 1.1.

# Examples
```jldoctest
julia> Base.@kwdef struct Foo
           a::Int = 1         # specified default
           b::String          # required keyword
       end
Foo

julia> Foo(b="hi")
Foo(1, "hi")

julia> Foo()
ERROR: UndefKeywordError: keyword argument b not assigned
Stacktrace:
[...]
```
"""
macro kwdef(expr)
    expr = macroexpand(__module__, expr) # to expand @static
    expr isa Expr && expr.head === :struct || error("Invalid usage of @kwdef")
    expr = expr::Expr
    T = expr.args[2]
    if T isa Expr && T.head === :<:
        T = T.args[1]
    end

    params_ex = Expr(:parameters)
    call_args = Any[]

    _kwdef!(expr.args[3], params_ex.args, call_args)
    # Only define a constructor if the type has fields, otherwise we'll get a stack
    # overflow on construction
    if !isempty(params_ex.args)
        if T isa Symbol
            kwdefs = :(($(esc(T)))($params_ex) = ($(esc(T)))($(call_args...)))
        elseif T isa Expr && T.head === :curly
            T = T::Expr
            # if T == S{A<:AA,B<:BB}, define two methods
            #   S(...) = ...
            #   S{A,B}(...) where {A<:AA,B<:BB} = ...
            S = T.args[1]
            P = T.args[2:end]
            Q = Any[U isa Expr && U.head === :<: ? U.args[1] : U for U in P]
            SQ = :($S{$(Q...)})
            kwdefs = quote
                ($(esc(S)))($params_ex) =($(esc(S)))($(call_args...))
                ($(esc(SQ)))($params_ex) where {$(esc.(P)...)} =
                    ($(esc(SQ)))($(call_args...))
            end
        else
            error("Invalid usage of @kwdef")
        end
    else
        kwdefs = nothing
    end
    quote
        Base.@__doc__($(esc(expr)))
        $kwdefs
    end
end

# @kwdef helper function
# mutates arguments inplace
function _kwdef!(blk, params_args, call_args)
    for i in eachindex(blk.args)
        ei = blk.args[i]
        if ei isa Symbol
            #  var
            push!(params_args, ei)
            push!(call_args, ei)
        elseif ei isa Expr
            if ei.head === :(=)
                lhs = ei.args[1]
                if lhs isa Symbol
                    #  var = defexpr
                    var = lhs
                elseif lhs isa Expr && lhs.head === :(::) && lhs.args[1] isa Symbol
                    #  var::T = defexpr
                    var = lhs.args[1]
                else
                    # something else, e.g. inline inner constructor
                    #   F(...) = ...
                    continue
                end
                defexpr = ei.args[2]  # defexpr
                push!(params_args, Expr(:kw, var, esc(defexpr)))
                push!(call_args, var)
                blk.args[i] = lhs
            elseif ei.head === :(::) && ei.args[1] isa Symbol
                # var::Typ
                var = ei.args[1]
                push!(params_args, var)
                push!(call_args, var)
            elseif ei.head === :block
                # can arise with use of @static inside type decl
                _kwdef!(ei, params_args, call_args)
            end
        end
    end
    blk
end

"""
    @invoke f(arg::T, ...; kwargs...)

Provides a convenient way to call [`invoke`](@ref);
`@invoke f(arg1::T1, arg2::T2; kwargs...)` will be expanded into `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)`.
When an argument's type annotation is omitted, it's specified as `Any` argument, e.g.
`@invoke f(arg1::T, arg2)` will be expanded into `invoke(f, Tuple{T,Any}, arg1, arg2)`.
"""
macro invoke(ex)
    f, args, kwargs = destructure_callex(ex)
    arg2typs = map(args) do x
        is_expr(x, :(::)) ? (x.args...,) : (x, GlobalRef(Core, :Any))
    end
    args, argtypes = first.(arg2typs), last.(arg2typs)
    return esc(:($(GlobalRef(Core, :invoke))($(f), Tuple{$(argtypes...)}, $(args...); $(kwargs...))))
end

"""
    @invokelatest f(args...; kwargs...)

Provides a convenient way to call [`Base.invokelatest`](@ref).
`@invokelatest f(args...; kwargs...)` will simply be expanded into
`Base.invokelatest(f, args...; kwargs...)`.
"""
macro invokelatest(ex)
    f, args, kwargs = destructure_callex(ex)
    return esc(:($(GlobalRef(Base, :invokelatest))($(f), $(args...); $(kwargs...))))
end

function destructure_callex(ex)
    is_expr(ex, :call) || throw(ArgumentError("a call expression f(args...; kwargs...) should be given"))

    f = first(ex.args)
    args = []
    kwargs = []
    for x in ex.args[2:end]
        if is_expr(x, :parameters)
            append!(kwargs, x.args)
        elseif is_expr(x, :kw)
            push!(kwargs, x)
        else
            push!(args, x)
        end
    end

    return f, args, kwargs
end

# testing

"""
    Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_THREADS / 2),
                  exit_on_error=false, revise=false, [seed])

Run the Julia unit tests listed in `tests`, which can be either a string or an array of
strings, using `ncores` processors. If `exit_on_error` is `false`, when one test
fails, all remaining tests in other files will still be run; they are otherwise discarded,
when `exit_on_error == true`.
If `revise` is `true`, the `Revise` package is used to load any modifications to `Base` or
to the standard libraries before running the tests.
If a seed is provided via the keyword argument, it is used to seed the
global RNG in the context where the tests are run; otherwise the seed is chosen randomly.
"""
function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS::Int / 2),
                  exit_on_error::Bool=false,
                  revise::Bool=false,
                  seed::Union{BitInteger,Nothing}=nothing)
    if isa(tests,AbstractString)
        tests = split(tests)
    end
    exit_on_error && push!(tests, "--exit-on-error")
    revise && push!(tests, "--revise")
    seed !== nothing && push!(tests, "--seed=0x$(string(seed % UInt128, base=16))") # cast to UInt128 to avoid a minus sign
    ENV2 = copy(ENV)
    ENV2["JULIA_CPU_THREADS"] = "$ncores"
    try
        run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR::String,
            Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2))
        nothing
    catch
        buf = PipeBuffer()
        Base.require(Base, :InteractiveUtils).versioninfo(buf)
        error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" *
              "including error messages above and the output of versioninfo():\n$(read(buf, String))")
    end
end

max_issues_repo_name: zhuangchaoxi/julia

max_forks_repo_name: zhuangchaoxi/julia


text:
# TODO deal with mapreduce wierdness, JuliaLang/julia#19308
# (current implementation is non-optimal)

# TODO see if we can optimize away the allocating calls to conj()

@inline *(tvec::TransposedVector, vec::AbstractVector) = reduce(+, map(At_mul_B, transpose(tvec), vec))
@inline *(tvec::TransposedVector, mat::AbstractMatrix) = transpose(mat.' * transpose(tvec))
@inline *(vec::AbstractVector, mat::AbstractMatrix) = error("Cannot left-multiply a matrix by a vector") # Should become a deprecation
@inline *(::TransposedVector, ::TransposedVector) = error("Cannot multiply two transposed vectors")
@inline *(vec::AbstractVector, tvec::TransposedVector) = kron(vec, tvec)
@inline *(vec::AbstractVector, tvec::AbstractVector) = error("Cannot multiply two vectors")
@inline *(mat::AbstractMatrix, tvec::TransposedVector) = error("Cannot right-multiply matrix by transposed vector")

# Transposed forms
@inline A_mul_Bt(::TransposedVector, ::AbstractVector) = error("Cannot multiply two transposed vectors")
@inline A_mul_Bt(tvec::TransposedVector, mat::AbstractMatrix) = transpose(mat * transpose(tvec))
@inline A_mul_Bt(vec::AbstractVector, mat::AbstractMatrix) = error("Cannot left-multiply a matrix by a vector")
@inline A_mul_Bt(tvec1::TransposedVector, tvec2::TransposedVector) = reduce(+, map(At_mul_B, transpose(tvec1), transpose(tvec2)))
@inline A_mul_Bt(vec::AbstractVector, tvec::TransposedVector) = error("Cannot multiply two vectors")
@inline A_mul_Bt(vec1::AbstractVector, vec2::AbstractVector) = kron(vec1, transpose(vec2))
@inline A_mul_Bt(mat::AbstractMatrix, tvec::TransposedVector) = mat * transpose(tvec)

@inline At_mul_Bt(tvec::TransposedVector, vec::AbstractVector) = kron(transpose(vec), transpose(tvec))
@inline At_mul_Bt(tvec::TransposedVector, mat::AbstractMatrix) = error("Cannot left-multiply matrix by vector")
@inline At_mul_Bt(vec::AbstractVector, mat::AbstractMatrix) = transpose(mat * vec)
@inline At_mul_Bt(tvec1::TransposedVector, tvec2::TransposedVector) = error("Cannot multiply two vectors")
@inline At_mul_Bt(vec::AbstractVector, tvec::TransposedVector) = reduce(+, map(At_mul_B, vec, transpose(tvec)))
@inline At_mul_Bt(vec::AbstractVector, tvec::AbstractVector) = error("Cannot multiply two transposed vectors")
@inline At_mul_Bt(mat::AbstractMatrix, tvec::TransposedVector) = mat.' * transpose(tvec)

@inline At_mul_B(::TransposedVector, ::AbstractVector) = error("Cannot multiply two vectors")
@inline At_mul_B(tvec::TransposedVector, mat::AbstractMatrix) = error("Cannot left-multiply matrix by vector")
@inline At_mul_B(vec::AbstractVector, mat::AbstractMatrix) = transpose(At_mul_B(mat,vec))
@inline At_mul_B(tvec1::TransposedVector, tvec2::TransposedVector) = kron(transpose(tvec1), tvec2)
@inline At_mul_B(vec::AbstractVector, tvec::TransposedVector) = error("Cannot multiply two transposed vectors")
@inline At_mul_B{T<:Real}(vec1::AbstractVector{T}, vec2::AbstractVector{T}) = reduce(+, map(At_mul_B, vec1, vec2)) # Seems to be overloaded...
@inline At_mul_B(vec1::AbstractVector, vec2::AbstractVector) = reduce(+, map(At_mul_B, vec1, vec2))
@inline At_mul_B(mat::AbstractMatrix, tvec::TransposedVector) = error("Cannot right-multiply matrix by transposed vector")

# Conjugated forms
@inline A_mul_Bc(::TransposedVector, ::AbstractVector) = error("Cannot multiply two transposed vectors")
@inline A_mul_Bc(tvec::TransposedVector, mat::AbstractMatrix) = ctranspose(mat * ctranspose(tvec))
@inline A_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = error("Cannot left-multiply a matrix by a vector")
@inline A_mul_Bc(tvec1::TransposedVector, tvec2::TransposedVector) = reduce(+, map(A_mul_Bc, tvec1, tvec2))
@inline A_mul_Bc(vec::AbstractVector, tvec::TransposedVector) = error("Cannot multiply two vectors")
@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = kron(vec1, ctranspose(vec2))
@inline A_mul_Bc(mat::AbstractMatrix, tvec::TransposedVector) = mat * ctranspose(tvec)

@inline Ac_mul_Bc(tvec::TransposedVector, vec::AbstractVector) = kron(ctranspose(vec), ctranspose(tvec))
@inline Ac_mul_Bc(tvec::TransposedVector, mat::AbstractMatrix) = error("Cannot left-multiply matrix by vector")
@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(mat * vec)
@inline Ac_mul_Bc(tvec1::TransposedVector, tvec2::TransposedVector) = error("Cannot multiply two vectors")
@inline Ac_mul_Bc(vec::AbstractVector, tvec::TransposedVector) = reduce(+, map(Ac_mul_Bc, vec, transpose(tvec)))
@inline Ac_mul_Bc(vec::AbstractVector, tvec::AbstractVector) = error("Cannot multiply two transposed vectors")
@inline Ac_mul_Bc(mat::AbstractMatrix, tvec::TransposedVector) = mat' * ctranspose(tvec)

@inline Ac_mul_B(::TransposedVector, ::AbstractVector) = error("Cannot multiply two vectors")
@inline Ac_mul_B(tvec::TransposedVector, mat::AbstractMatrix) = error("Cannot left-multiply matrix by vector")
@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(Ac_mul_B(mat,vec))
@inline Ac_mul_B(tvec1::TransposedVector, tvec2::TransposedVector) = kron(ctranspose(tvec1), tvec2)
@inline Ac_mul_B(vec::AbstractVector, tvec::TransposedVector) = error("Cannot multiply two transposed vectors")
@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = reduce(+, map(Ac_mul_B, vec1, vec2))
@inline Ac_mul_B(mat::AbstractMatrix, tvec::TransposedVector) = error("Cannot right-multiply matrix by transposed vector")

#=
#A_mul_B!
#Ac_mul_B!
#Ac_mul_B!
#At_mul_B!
#Ac_mul_B!
#Ac_mul_Bc!
#At_mul_Bt!

#Ac_ldiv_B!
#A_ldiv_B!
#At_ldiv_B!
#etc
=#


text:
module UpdatableCholeskyFactorizations

using LinearAlgebra
using LinearAlgebra: checksquare

export UpdatableCholesky, UCholesky,
       updatable_cholesky, ucholesky,
       updatable_cholesky!, ucholesky!,
       add_column!, addcol!,
       remove_column!, remcol!

using LazyInverses

# IDEA: have separate package PermutationMatrices.jl,
# where we take care of all the permutation logic

include("cholesky.jl")
include("inverse.jl")

end # module


text:
## LICENSE
##   Copyright (c) 2018 GEOPHYSTECH LLC
##
##   Licensed under the Apache License, Version 2.0 (the "License");
##   you may not use this file except in compliance with the License.
##   You may obtain a copy of the License at
##
##       http://www.apache.org/licenses/LICENSE-2.0
##
##   Unless required by applicable law or agreed to in writing, software
##   distributed under the License is distributed on an "AS IS" BASIS,
##   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
##   See the License for the specific language governing permissions and
##   limitations under the License.

## initial release by Andrey Stepnov a.stepnov@geophsytech.ru

"""
**ON GRID**

`gmpe_simidorikawa1999(eq::Earthquake,config::Params_simidorikawa1999,grid::Array{Point_vs30};min_val::Number)`

where `min_val=0` by default

Output will be 1-d `Array{Point_pga_out}` with points based on the input grid with `pga > min_val` (pga is Acceleration of gravity in percent (%g) rounded to ggg.gg)

**without grid**

`gmpe_simidorikawa1999(eq::Earthquake,config::Params_simidorikawa1999;VS30::Number=350,distance::Int64=1000)`

where `VS30=30` [m/s^2], `distance=1000` [km] by default.

Output will be 1-d `Array{Float64,1}` with `1:distance` values of `pga` (that is Acceleration of gravity (g) in percent rounded to ggg.gg)

**EXAMPLES:**
```
gmpe_simidorikawa1999(eq,config_simidorikawa1999_interplate,grid) # for PGA on GRID
gmpe_simidorikawa1999(eq,config_simidorikawa1999_intraplate) # for PGA without input grid
```

**Model parameters**

Please, see `examples/si-midorikawa-1999.conf`
"""
## Si-Midorikawa (1999) PGA modeling ON GRID
function gmpe_simidorikawa1999(eq::Earthquake,config::Params_simidorikawa1999,grid::Array{Point_vs30};min_val::Number=0)
  vs30_row_num = length(grid[:,1])
  eq.moment_mag == 0 ? magnitude = eq.local_mag : magnitude = eq.moment_mag
  epicenter = LatLon(eq.lat, eq.lon)
  # define g_global
  g_global = 9.81
  # define \sum{d_i S_i}
  sum_di_si = config.d1*config.S1 + config.d2*config.S2 + config.d3*config.S3
  # define b = aMw + hD + \sum{d_i S_i} +e
  b = config.a*magnitude + config.h + sum_di_si + config.e_
  # define c
  c = 0.006*10^(0.5*magnitude)
  # init output_data
  if config.ground_motion_type == "PGA"
    output_data = Array{Point_pga_out}(undef,0)
    out_type = Point_pga_out
  end
  # main cycle by grid points
  for i=1:vs30_row_num
    # rrup the same as X in Si-Midorikawa (1999) formulae
    # eq.depth the same as D in in Si-Midorikawa (1999) formulae
    current_point = LatLon(grid[i].lat,grid[i].lon)
    r_rup = sqrt((distance(current_point,epicenter)/1000)^2 + eq.depth^2)
    # \logARA for VS30
    log_ARA = 1.35 - 0.47*log10(grid[i].vs30)
    # A in cm/s^2
    if eq.depth <= 30
      A = 10^(b - log10(r_rup + c) - config.k*r_rup + log_ARA)
    else
      A = 10^(b + 0.6*log10(1.7*eq.depth + c) -
              1.6*log10(r_rup + c) - config.k*r_rup + log_ARA)
    end
    if config.ground_motion_type == "PGA"
      motion = round(((A/100)/g_global * 100),digits=2)
    end
    if motion >= min_val
      output_data = push!(output_data, out_type(grid[i].lon,grid[i].lat,motion))
    end
  end
  return output_data
end
## Si-Midorikawa (1999) PGA modeling for PLOTTING
function gmpe_simidorikawa1999(eq::Earthquake,config::Params_simidorikawa1999;VS30::Number=350,distance::Number=1000)
  #vs30_row_num = length(grid[:,1])
  eq.moment_mag == 0 ? magnitude = eq.local_mag : magnitude = eq.moment_mag
  # define g_global
  g_global = 9.81
  # define \sum{d_i S_i}
  sum_di_si = config.d1*config.S1 + config.d2*config.S2 + config.d3*config.S3
  # define b = aMw + hD + \sum{d_i S_i} +e
  b = config.a*magnitude + config.h + sum_di_si + config.e_
  # define c
  c = 0.006*10^(0.5*magnitude)
  # \logARA for VS30
  log_ARA = 1.35 - 0.47*log10(VS30)
  # init output_data
  output_data = Array{Float64}(undef,0)
  # main cycle by grid points
  for i=1:distance
    # rrup the same as X in Si-Midorikawa (1999) formulae
    # eq.depth the same as D in in Si-Midorikawa (1999) formulae
    r_rup = sqrt((i)^2 + eq.depth^2)
    # A in cm/s^2
    if eq.depth <= 30
      A = 10^(b - log10(r_rup + c) - config.k*r_rup + log_ARA)
    else
      A = 10^(b + 0.6*log10(1.7*eq.depth + c) -
              1.6*log10(r_rup + c) - config.k*r_rup + log_ARA)
    end
    if config.ground_motion_type == "PGA"
      motion = round(((A/100)/g_global * 100),digits=2)
    end
    output_data = push!(output_data, motion)
  end
  return output_data
end


text:
"""
    C_iio_channel_attr_get_filename(channel, attr)

Retrieve the filename of an attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute

# Returns
- On success, a NULL-terminated string
- If the attribute name is unknown, NULL is returned. If the assertions are enabled, throws an error instead.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gab6462404bb6667e9e9241a18e09a1638)
"""
function C_iio_channel_attr_get_filename(channel::Ptr{iio_channel}, attr::String)
    @assert_Cstring name = ccall(
        (:iio_channel_attr_get_filename, libIIO),
        Cstring, (Ptr{iio_channel}, Cstring),
        channel, attr
    );
    return name == C_NULL ? "" : Base.unsafe_string(name);
end

"""
    C_iio_channel_attr_read(channel, attr)

Read the content of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute

# Returns
- On success, `(nbytes, value::String)` where nbytes is the length of the value string.
- On error, `(errno, "")` is returned, where errno is a negative error code.
- If all the attributes are begin read, an array of the values above is returned.
The string may be shorter than the number of bytes returned as the conversion tirims excess null characters.

# NOTE
By passing NULL (replaced by an empty string in the Julia wrapper) as the "attr" argument to `iio_channel_attr_read`, it is now possible to read all of the attributes of a channel.

The buffer is filled with one block of data per attribute of the channel, by the order they appear in the iio_channel structure.

The first four bytes of one block correspond to a 32-bit signed value in network order.
If negative, it corresponds to the errno code that were returned when reading the attribute;
if positive, it corresponds to the length of the data read. In that case, the rest of the block contains the data.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga2c2ca5696d1341067051eb390d5014ae)
"""
function C_iio_channel_attr_read(channel::Ptr{iio_channel}, attr::String)
    if attr == ""; attr = C_NULL; end; # allows to read all the attributes
    buf = zeros(UInt8, BUF_SIZE);
    ret = ccall(
        (:iio_channel_attr_read, libIIO),
        Cssize_t, (Ptr{iio_channel}, Cstring, Cstring, Csize_t),
        channel, attr, pointer(buf), BUF_SIZE
    );
    attr == C_NULL ? attrs = iio_decode_blocks(buf, ret) : attrs = toString(buf);
    return ret, attrs;
end

"""
    C_iio_channel_attr_read_all()

THIS IS A PLACEHOLDER. THE DOCUMENTATION BELOW IS ONLY A COPY/PASTE OF THE C DOCUMENTATION.

Read the content of all channel-specific attributes.

# Parameters
- chn : A pointer to an iio_channel structure
- cb : A pointer to a callback function
- data : A pointer that will be passed to the callback function

# Returns
- On success, 0 is returned
- On error, a negative errno code is returned

# NOTE
This function is especially useful when used with the network backend, as all the channel-specific attributes are read in one single command.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gab9c28b0cd94c0607bcc7cac16219eb48)
"""
function C_iio_channel_attr_read_all()
    return "PLACEHOLDER"
end

"""
    C_iio_channel_attr_read_bool(channel, attr)

Read the content of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute

# Returns
- On success, `(0, value::Bool)` is returned
- On error, `(errno, false)` is returned, where errno is a negative error code. The second value should be discarded.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga319f39c313bbd4d331e23df51e4d3ce6)
"""
function C_iio_channel_attr_read_bool(channel::Ptr{iio_channel}, attr::String)
    value = Ref{UInt8}(0);
    ret = ccall(
        (:iio_channel_attr_read_bool, libIIO),
        Cint, (Ptr{iio_channel}, Cstring, Ptr{Cuchar}),
        channel, attr, value
    );
    return ret, Base.convert(Bool, value[]);
end

"""
    C_iio_channel_attr_read_double(channel, attr)

Read the content of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute

# Returns
- On success, `(0, value::Float64)` is returned
- On error, `(errno, 0)` is returned, where errno is a negative error code. The second value should be discarded.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga75ac9b81eb7e7e8a961afb67748e4102)
"""
function C_iio_channel_attr_read_double(channel::Ptr{iio_channel}, attr::String)
    value = Ref{Float64}(0);
    ret = ccall(
        (:iio_channel_attr_read_double, libIIO),
        Cint, (Ptr{iio_channel}, Cstring, Ptr{Cdouble}),
        channel, attr, value
    );
    return ret, value[];
end

"""
    C_iio_channel_attr_read_longlong(channel, attr)

Read the content of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute

# Returns
- On success, `(0, value::Int64)` is returned
- On error, `(errno, 0)` is returned, where errno is a negative error code. The second value should be discarded.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga116c61892bf3d20ff07efd642c5dfbe1)
"""
function C_iio_channel_attr_read_longlong(channel::Ptr{iio_channel}, attr::String)
    value = Ref{Int64}(0);
    ret = ccall(
        (:iio_channel_attr_read_longlong, libIIO),
        Cint, (Ptr{iio_channel}, Cstring, Ptr{Clonglong}),
        channel, attr, value
    );
    return ret, value[];
end

"""
    C_iio_channel_attr_write(channel, attr, value)

Set the value of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute
- `value::String` : A NULL-terminated string to set the attribute to

# Returns
- On success, the number of bytes written
- On error, a negative errno code is returned

# NOTE
By passing NULL as the "attr" argument to `iio_channel_attr_write`, it is now possible to write all of the attributes of a channel.

The buffer must contain one block of data per attribute of the channel, by the order they appear in the iio_channel structure.

The first four bytes of one block correspond to a 32-bit signed value in network order.
If negative, the attribute is not written;
if positive, it corresponds to the length of the data to write.
In that case, the rest of the block must contain the data.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga35c76ce5fcae4c551b7c78d648665a41)
"""
function C_iio_channel_attr_write(channel::Ptr{iio_channel}, attr::String, value::String)
    return ccall(
        (:iio_channel_attr_write, libIIO),
        Cssize_t, (Ptr{iio_channel}, Cstring, Cstring),
        channel, attr, value
    );
end

"""
    C_iio_channel_attr_write_all()

THIS IS A PLACEHOLDER. THE DOCUMENTATION BELOW IS ONLY A COPY/PASTE OF THE C DOCUMENTATION.

Set the values of all channel-specific attributes.

# Parameters
- chn : A pointer to an iio_channel structure
- cb : A pointer to a callback function
- data : A pointer that will be passed to the callback function

# Returns
- On success, 0 is returned
- On error, a negative errno code is returned

# NOTE
This function is especially useful when used with the network backend, as all the channel-specific attributes are written in one single command.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga6df693ee4f0c329d957f7c7ca3588f3f)
"""
function C_iio_channel_attr_write_all()
    return "PLACEHOLDER"
end

"""
    C_iio_channel_attr_write_bool(channel, attr, value)

Set the value of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute
- `value::Bool` : A bool value to set the attribute to

# Returns
- On success, 0 is returned
- On error, a negative errno code is returned

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga9a385b9b05d20f33f8e587feb2ebe81a)
"""
function C_iio_channel_attr_write_bool(channel::Ptr{iio_channel}, attr::String, value::Bool)
    return ccall(
        (:iio_channel_attr_write_bool, libIIO),
        Cint, (Ptr{iio_channel}, Cstring, Cuchar),
        channel, attr, value
    );
end

"""
    C_iio_channel_attr_write_double(channel, attr, value)

Set the value of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute
- `value::Float64` : A double value to set the attribute to

# Returns
- On success, 0 is returned
- On error, a negative errno code is returned

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gad9d6ec4a02948c6416cc99254bdbfa50)
"""
function C_iio_channel_attr_write_double(channel::Ptr{iio_channel}, attr::String, value::Float64)
    return ccall(
        (:iio_channel_attr_write_double, libIIO),
        Cint, (Ptr{iio_channel}, Cstring, Cdouble),
        channel, attr, value
    );
end

"""
    C_iio_channel_attr_write_longlong(channel, attr, value)

Set the value of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute
- `value::Int64` : A long long value to set the attribute to

# Returns
- On success, 0 is returned
- On error, a negative errno code is returned

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gac55cb77a1baf797e54a8a4e31b2f4680)
"""
function C_iio_channel_attr_write_longlong(channel::Ptr{iio_channel}, attr::String, value::Int64)
    return ccall(
        (:iio_channel_attr_write_longlong, libIIO),
        Cint, (Ptr{iio_channel}, Cstring, Clonglong),
        channel, attr, value
    );
end

"""
    C_iio_channel_attr_write_raw(channel, attr, value)

Set the value of the given channel-specific attribute.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `attr::String` : a NULL-terminated string corresponding to the name of the attribute
- `value::Ptr{Cvoid}`       : A pointer to the data to be written
- `size::Csize_t`           : The number of bytes to be written

# Returns
- On success, the number of bytes written
- On error, a negative errno code is returned

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gacd0d3dd36bdc64a9f967e21a891230eb)
"""
function C_iio_channel_attr_write_raw(channel::Ptr{iio_channel}, attr::String, value::Ptr{Cvoid}, size::Csize_t)
    return ccall(
        (:iio_channel_attr_write_raw, libIIO),
        Cssize_t, (Ptr{iio_channel}, Cstring, Ptr{Cuchar}, Csize_t),
        channel, attr, value, size
    );
end

"""
    C_iio_channel_disable(channel)

Disable the given channel.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gad7c7c91c61b8a97187dc73cbcdb60c06)
"""
function C_iio_channel_disable(channel::Ptr{iio_channel})
    ccall(
        (:iio_channel_disable, libIIO),
        Cvoid, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_enable(channel)

Enable the given channel.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# NOTE
Before creating an iio_buffer structure with `iio_device_create_buffer`, it is required to enable at least one channel of the device to read from.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga2b787983683d37966b5e1e5c6c121d6a)
"""
function C_iio_channel_enable(channel::Ptr{iio_channel})
    ccall(
        (:iio_channel_enable, libIIO),
        Cvoid, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_find_attr(channel, name)

Try to find a channel-specific attribute by its name.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `name::String` : A NULL-terminated string corresponding to the name of the attribute

# Returns
- On success, a NULL-terminated string.
- On failure, if the assertions are enabled, throws an error.
- On failure, if the assertions are disabled, returns an empty string.

# NOTE
This function is useful to detect the presence of an attribute. It can also be used to retrieve the name of an attribute as a pointer to a static string from a dynamically allocated string.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga13b2db3252a2380a2b0b1bb15b8034a4)
"""
function C_iio_channel_find_attr(channel::Ptr{iio_channel}, name::String)
    @assert_Cstring attr = ccall(
        (:iio_channel_find_attr, libIIO),
        Cstring, (Ptr{iio_channel}, Cstring),
        channel, name
    );
    return attr == C_NULL ? "" : Base.unsafe_string(attr);
end

"""
    C_iio_channel_get_attr(channel, index)

Get the channel-specific attribute present at the given index.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `index::UInt32` : The index corresponding to the attribute

# Returns
- On success, a NULL-terminated string.
- On failure, if the assertions are enabled, throws an error.
- On failure, if the assertions are disabled, returns an empty string.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gafc3c52f360424c097a24d1925923d772)
"""
function C_iio_channel_get_attr(channel::Ptr{iio_channel}, index::UInt32)
    @assert_Cstring attr = ccall(
        (:iio_channel_get_attr, libIIO),
        Cstring, (Ptr{iio_channel}, Cuint),
        channel, index
    );
    return attr == C_NULL ? "" : Base.unsafe_string(attr);
end

"""
    C_iio_channel_get_attrs_count(channel)

Enumerate the channel-specific attributes of the given channel.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- The number of channel-specific attributes found

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga14869c3fda8b04f413a02f15dfa6ef7c)
"""
function C_iio_channel_get_attrs_count(channel::Ptr{iio_channel})
    return ccall(
        (:iio_channel_get_attrs_count, libIIO),
        Cuint, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_get_data(channel)

Retrieve a previously associated pointer of an iio_channel structure.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- The pointer previously associated if present, or NULL

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gacbce92eaefb8d61c1e4f0dc042b211e6)
"""
function C_iio_channel_get_data(channel::Ptr{iio_channel})
    return ccall(
        (:iio_channel_get_data, libIIO),
        Ptr{Cvoid}, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_get_device(channel)

Retrieve a pointer to the iio_device structure.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- A pointer to an iio_device structure

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gaf2800d7a6953c5dd3271df390c062439)
"""
function C_iio_channel_get_device(channel::Ptr{iio_channel})
    return ccall(
        (:iio_channel_get_device, libIIO),
        Ptr{iio_device}, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_get_id(channel)

Retrieve the channel ID (e.g. voltage0)

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- A NULL-terminated string

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gafda1782de4655905ad08a40492f3dc64)
"""
function C_iio_channel_get_id(channel::Ptr{iio_channel})
    return Base.unsafe_string(ccall(
        (:iio_channel_get_id, libIIO),
        Cstring, (Ptr{iio_channel},),
        channel
    ));
end

"""
    C_iio_channel_get_modifier(channel)

Get the modifier type of the given channel.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- The modifier type of the channel

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga4c3179cee86d8992ee6c6bdbcaa44156)
"""
function C_iio_channel_get_modifier(channel::Ptr{iio_channel})
    return ccall(
        (:iio_channel_get_modifier, libIIO),
        iio_modifier, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_get_name(channel)

Retrieve the channel name (e.g. vccint)

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- A NULL-terminated string.

# NOTE
If the channel has no name, and the assertions are enabled, throws an error. If the assertions are disabled, returns an empty string.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga37346a6f3fcfb1eb40572aec6c3b39ac)
"""
function C_iio_channel_get_name(channel::Ptr{iio_channel})
    @assert_Cstring name = ccall(
        (:iio_channel_get_name, libIIO),
        Cstring, (Ptr{iio_channel},),
        channel
    );
    if name == C_NULL; name = ""; else; name = Base.unsafe_string(name); end;
    return name;
end

"""
    C_iio_channel_get_type(channel)

Get the type of the given channel.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- The type of the channel

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga281660051fb40b5b4055227466a3be36)
"""
function C_iio_channel_get_type(channel::Ptr{iio_channel})
    return ccall(
        (:iio_channel_get_type, libIIO),
        iio_chan_type, (Ptr{iio_channel},),
        channel
    );
end

"""
    C_iio_channel_is_enabled(channel)

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- True if the channel is enabled, False otherwise

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gaf10427dc35adaa0991cd34a9dd45a82f)
"""
function C_iio_channel_is_enabled(channel::Ptr{iio_channel})
    return Base.convert(Bool, ccall(
        (:iio_channel_is_enabled, libIIO),
        Cuchar, (Ptr{iio_channel},),
        channel
    ));
end

"""
    C_iio_channel_is_output(channel)

Return True if the given channel is an output channel.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- True if the channel is an output channel, False otherwise

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga3c24e9c93e2217c9506073d04b878461)
"""
function C_iio_channel_is_output(channel::Ptr{iio_channel})
    return Base.convert(Bool, ccall(
        (:iio_channel_is_output, libIIO),
        Cuchar, (Ptr{iio_channel},),
        channel
    ));
end

"""
    C_iio_channel_is_scan_element(channel)

Return True if the given channel is a scan element.

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure

# Returns
- True if the channel is a scan element, False otherwise

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga07892a3c0c31e7a3eecf76ec72a8669d)
"""
function C_iio_channel_is_scan_element(channel::Ptr{iio_channel})
    return Base.convert(Bool, ccall(
        (:iio_channel_is_scan_element, libIIO),
        Cuchar, (Ptr{iio_channel},),
        channel
    ));
end

"""
    C_iio_channel_read(chn, buf)

Demultiplex and convert the samples of a given channel.

# Parameters
- `chn::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `buf::Ptr{iio_buffer}` : A pointer to an iio_buffer structure

# Returns
- `(nbytes, dst::Array{UInt8})` where nbytes is the number of bytes written to dst.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga5c01edc37b0b57aef503abd5989a6a30)
"""
#  function C_iio_channel_read(chn::Ptr{iio_channel}, buf::Ptr{iio_buffer})
    #  dst = zeros(UInt8, BUF_SIZE);
    #  nbytes = C_iio_channel_read!(chn, buf, dst);
    #  return nbytes, dst;
#  end

"""
    C_iio_channel_read!(chn, buf, dst)

Demultiplex and convert the samples of a given channel.

# Parameters
- `chn::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `buf::Ptr{iio_buffer}` : A pointer to an iio_buffer structure
- `dst::Array{UInt8}` : An array where the converted data will be stored

# Returns
- The size of the converted data written is dst, in bytes

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga5c01edc37b0b57aef503abd5989a6a30)
"""
function C_iio_channel_read!(chn::Ptr{iio_channel}, buf::Ptr{iio_buffer}, dst::Array{UInt8})
    nbytes = ccall(
        (:iio_channel_read, libIIO),
        Csize_t, (Ptr{iio_channel}, Ptr{iio_buffer}, Ptr{Cuchar}, Csize_t),
        chn, buf, pointer(dst), length(dst)
    );
    return nbytes;
end

"""
    C_iio_channel_read_raw()

THIS IS A PLACEHOLDER. THE DOCUMENTATION BELOW IS ONLY A COPY/PASTE OF THE C DOCUMENTATION.

Demultiplex the samples of a given channel.

# Parameters
- chn : A pointer to an iio_channel structure
- buffer : A pointer to an iio_buffer structure
- dst : A pointer to the memory area where the demultiplexed data will be stored
- len : The available length of the memory area, in bytes

# Returns
- The size of the demultiplexed data, in bytes

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#gacd227a6861960ea2fb49d957f62887dd)
"""
function C_iio_channel_read_raw()
    return "PLACEHOLDER"
end

"""
    C_iio_channel_set_data(channel, data)

Associate a pointer to an iio_channel structure. If the pointer is a Julia pointer, you need to protect the data from the GC.

See the [Julia Documentation](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#Garbage-Collection-Safety) and [`GC.@preserve`](https://docs.julialang.org/en/v1/base/base/#Base.GC.@preserve).

# Parameters
- `channel::Ptr{iio_channel}` : A pointer to an iio_channel structure
- `data::Ptr{Cvoid}`          : The pointer to be associated.

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga5150c9b73386d899460ebafbe614f338)
"""
function C_iio_channel_set_data(channel::Ptr{iio_channel}, data::Ptr{Cvoid})
    ccall(
        (:iio_channel_set_data, libIIO),
        Cvoid, (Ptr{iio_channel}, Ptr{Cvoid}),
        channel, data
    );
end

"""
    C_iio_channel_write()

THIS IS A PLACEHOLDER. THE DOCUMENTATION BELOW IS ONLY A COPY/PASTE OF THE C DOCUMENTATION.

Convert and multiplex the samples of a given channel.

# Parameters
- chn : A pointer to an iio_channel structure
- buffer : A pointer to an iio_buffer structure
- src : A pointer to the memory area where the sequential data will be read from
- len : The length of the memory area, in bytes

# Returns
- The number of bytes actually converted and multiplexed

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga52c5a5cc138969b32f78db9669a4ffd2)
"""
function C_iio_channel_write()
    return "PLACEHOLDER"
end

"""
    C_iio_channel_write_raw()

THIS IS A PLACEHOLDER. THE DOCUMENTATION BELOW IS ONLY A COPY/PASTE OF THE C DOCUMENTATION.

Multiplex the samples of a given channel.

# Parameters
- chn : A pointer to an iio_channel structure
- buffer : A pointer to an iio_buffer structure
- src : A pointer to the memory area where the sequential data will be read from
- len : The length of the memory area, in bytes

# Returns
- The number of bytes actually multiplexed

[libIIO documentation](https://analogdevicesinc.github.io/libiio/master/libiio/group__Channel.html#ga350e81855764c159c6aefa12fb78e1c2)
"""
function C_iio_channel_write_raw()
    return "PLACEHOLDER"
end