text:
#  git	c1ed2e+	abb2cfe	e53b6d4
#  with	awsgpu	cpu	gpu	cpu	af	kn	kn+gc1	kn+gc2	kn+gc3
#1 mul	0.67	0.95	0.56    0.94	0.56	0.56	0.56	0.56	0.56
#2 bias	0.71	1.05	0.59    1.05	0.56	0.59	0.59	0.59	0.59
#3 max	0.75	1.34	0.62    1.34	0.56	0.63	0.62	0.62	0.62
#4 mul	0.81	1.43	0.75    1.44	0.74	0.75	0.75	0.75	0.75
#5 bias	0.85	1.48	0.78    1.48	0.75	0.79	0.78	0.78	0.78
#6 sub	0.89	1.49	0.82    1.49	0.81	0.82	0.81	0.81	0.82
#7 sq	0.92	1.62	0.85    1.62	0.93	0.85	0.84	0.84	0.85
#8 sum	1.21	1.63	1.06±01 1.62	1.22	1.19	1.07	1.08	1.07
#9 forw	1.51	1.96±04	1.18±02 2.47	2.60	2.25	1.67	1.46	1.68
#A grad	2.89	4.40±12	2.10±22	5.52	6.53	5.86	3.52	3.62	3.30
#
# (*) timeall(weights(), weights(64), data(), 10)
# (*) af results with gc_enable=false and sync()
# (*) kn uses `similar`, +gc1 runs tmpfree every epoch, +gc2 runs tmpfree every iteration (minibatch), +gc3 uses KnetArray.
# AF: The forw records arrays preventing their reuse?
# AF: They are merging consecutive ops in one kernel, which breaks down with forw?

using AutoGrad, GZip, Compat
using AutoGrad: forward_pass

fun = []

push!(fun,(w,x,y)->w[1]*x)
push!(fun,(w,x,y)->w[1]*x.+w[2])
push!(fun,(w,x,y)->max(0,w[1]*x.+w[2]))
push!(fun,(w,x,y)->w[3]*max(0,w[1]*x.+w[2]))
push!(fun,(w,x,y)->w[3]*max(0,w[1]*x.+w[2]).+w[4])
push!(fun,(w,x,y)->((w[3]*max(0,w[1]*x.+w[2]).+w[4])-y))
push!(fun,(w,x,y)->(((w[3]*max(0,w[1]*x.+w[2]).+w[4])-y).^2))
fun1 = (w,x,y)->sum(((w[3]*max(0,w[1]*x.+w[2]).+w[4])-y).^2)
push!(fun, fun1)
push!(fun,(w,x,y)->forward_pass(fun1,(w,x,y),(),1))
push!(fun,grad(fun1))

function timeall(w=w2,d=d0,t=10)
    for i=1:length(fun)
        printfun(fun[i])
        for j=1:3
            sleep(2)
            @time loop(fun[i],w,d,t)
        end
    end
end

function loop(f,w,d,t)
    for i in 1:t
        for (x,y) in d
            f(w,x,y)
        end
    end
end

function weights(h...; seed=nothing)
    seed==nothing || srand(seed)
    w = Array{Float32}[]
    x = 28*28
    for y in [h..., 10]
        push!(w, convert(Array{Float32}, 0.1*randn(y,x)))
        push!(w, zeros(Float32,y))
        x = y
    end
    return w
end

function data()
    info("Loading data...")
    xshape(a)=reshape(a./255f0,784,div(length(a),784))
    yshape(a)=(a[a.==0]=10; full(sparse(convert(Vector{Int},a),1:length(a),1f0)))
    xtrn = xshape(gzload("train-images-idx3-ubyte.gz")[17:end])
    ytrn = yshape(gzload("train-labels-idx1-ubyte.gz")[9:end])
    #xtst = xshape(gzload("t10k-images-idx3-ubyte.gz")[17:end])
    #ytst = yshape(gzload("t10k-labels-idx1-ubyte.gz")[9:end])
    batch(xtrn,ytrn,100)
end

function gzload(file; path=joinpath(AutoGrad.datapath,file), url="http://yann.lecun.com/exdb/mnist/$file")
    isfile(path) || download(url, path)
    f = gzopen(path)
    a = @compat read(f)
    close(f)
    return(a)
end

function batch(x, y, batchsize)
    data = Any[]
    nx = size(x,2)
    for i=1:batchsize:nx
        j=min(i+batchsize-1,nx)
        push!(data, (x[:,i:j], y[:,i:j]))
    end
    return data
end

function printfun(x)
    if isdefined(x,:code)
        println(Base.uncompressed_ast(x.code).args[3].args[2].args[1])
    else
        println(x)
    end
end

if !isdefined(:d0)
    d0 = data()
    w1 = weights(seed=1)
    w2 = weights(64;seed=1)
end

:ok

# julia> timeall()
# (Main.getindex)(w,1) * x
#   0.956369 seconds (30.00 k allocations: 147.400 MB, 0.97% gc time)
#   0.947161 seconds (30.00 k allocations: 147.400 MB, 0.78% gc time)
#   0.947129 seconds (30.00 k allocations: 147.400 MB, 0.66% gc time)
# (Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)
#   1.055720 seconds (144.00 k allocations: 297.913 MB, 1.41% gc time)
#   1.054730 seconds (144.00 k allocations: 297.913 MB, 1.41% gc time)
#   1.054276 seconds (144.00 k allocations: 297.913 MB, 1.31% gc time)
# (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2))
#   1.356736 seconds (168.03 k allocations: 445.315 MB, 1.63% gc time)
#   1.353720 seconds (168.00 k allocations: 445.313 MB, 1.55% gc time)
#   1.353312 seconds (168.00 k allocations: 445.313 MB, 1.56% gc time)
# (Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2))
#   1.443865 seconds (180.04 k allocations: 468.844 MB, 1.62% gc time)
#   1.440977 seconds (180.00 k allocations: 468.842 MB, 1.56% gc time)
#   1.441619 seconds (180.00 k allocations: 468.842 MB, 1.63% gc time)
# (Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)
#   1.486593 seconds (288.04 k allocations: 495.852 MB, 1.63% gc time)
#   1.487364 seconds (288.00 k allocations: 495.850 MB, 1.69% gc time)
#   1.485600 seconds (288.00 k allocations: 495.850 MB, 1.69% gc time)
# ((Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)) - y
#   1.498100 seconds (300.04 k allocations: 519.382 MB, 1.70% gc time)
#   1.498842 seconds (300.00 k allocations: 519.379 MB, 1.78% gc time)
#   1.497004 seconds (300.00 k allocations: 519.379 MB, 1.78% gc time)
# (((Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)) - y) .^ 2
#   1.634301 seconds (318.04 k allocations: 543.277 MB, 1.73% gc time)
#   1.631012 seconds (318.00 k allocations: 543.274 MB, 1.66% gc time)
#   1.632553 seconds (318.00 k allocations: 543.274 MB, 1.96% gc time)
# (Main.sum)((((Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)) - y) .^ 2)
#   1.636876 seconds (324.00 k allocations: 543.365 MB, 1.74% gc time)
#   1.635672 seconds (324.00 k allocations: 543.365 MB, 1.74% gc time)
#   1.636505 seconds (324.00 k allocations: 543.365 MB, 1.74% gc time)
# (Main.forward_pass)(Main.fun1,(top(tuple))(w,x,y),(top(tuple))(),1)
#   2.109984 seconds (1.45 M allocations: 601.044 MB, 1.89% gc time)
#   2.110840 seconds (1.45 M allocations: 601.044 MB, 1.91% gc time)
#   2.109753 seconds (1.45 M allocations: 601.044 MB, 1.83% gc time)
# gradfun
#   4.794647 seconds (3.39 M allocations: 2.200 GB, 2.58% gc time)
#   4.788467 seconds (3.39 M allocations: 2.200 GB, 2.54% gc time)
#   4.790467 seconds (3.39 M allocations: 2.200 GB, 2.57% gc time)

# julia> include(Pkg.dir("Knet/test/profile_kn.jl"))
# before d0kn,w2kn (4934356992,:cuda_ptrs,0)
# after d0kn,w2kn (4720431104,(4000,600,0),(200704,1,0),(2560,1,0),(40,1,0),(256,1,0),(313600,600,0),:cuda_ptrs,0)
# 1(Main.getindex)(w,1) * x
#   0.615343 seconds (278.52 k allocations: 10.871 MB)
#   0.559348 seconds (222.00 k allocations: 8.331 MB)
#   0.559312 seconds (222.00 k allocations: 8.331 MB)
# 2(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)
#   1.278285 seconds (374.67 k allocations: 14.424 MB)
#   0.593602 seconds (330.00 k allocations: 12.268 MB)
#   0.592977 seconds (312.00 k allocations: 11.627 MB)
# 3(Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2))
#   0.632009 seconds (371.78 k allocations: 13.691 MB)
#   0.623921 seconds (348.00 k allocations: 12.817 MB)
#   0.624452 seconds (366.00 k allocations: 13.458 MB)
# 4(Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2))
#   0.755348 seconds (528.04 k allocations: 20.327 MB)
#   0.752531 seconds (510.00 k allocations: 19.684 MB)
#   0.752733 seconds (528.00 k allocations: 20.325 MB)
# 5(Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)
#   0.787278 seconds (618.04 k allocations: 23.623 MB)
#   0.784750 seconds (618.00 k allocations: 23.621 MB)
#   0.785054 seconds (618.00 k allocations: 23.621 MB)
# 6((Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)) - y
#   0.852492 seconds (705.88 k allocations: 27.000 MB)
#   0.815621 seconds (654.00 k allocations: 24.811 MB)
#   0.815684 seconds (654.00 k allocations: 24.811 MB)
# 7(((Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)) - y) .^ 2
#   0.853973 seconds (713.80 k allocations: 26.875 MB)
#   0.845969 seconds (690.00 k allocations: 26.001 MB)
#   0.846093 seconds (690.00 k allocations: 26.001 MB)
# 8(Main.sum)((((Main.getindex)(w,3) * (Main.max)(0,(Main.getindex)(w,1) * x .+ (Main.getindex)(w,2)) .+ (Main.getindex)(w,4)) - y) .^ 2)
#   1.065069 seconds (697.55 k allocations: 26.163 MB)
#   1.059029 seconds (696.00 k allocations: 26.093 MB)
#   1.058951 seconds (696.00 k allocations: 26.093 MB)
# 9(Main.forward_pass)(Main.fun1,(top(tuple))(w,x,y),(top(tuple))(),1)
#   1.606405 seconds (2.22 M allocations: 101.994 MB)
#   1.270358 seconds (1.90 M allocations: 87.799 MB)
#   1.270771 seconds (1.90 M allocations: 87.799 MB)
# 10gradfun
#   4.155291 seconds (4.51 M allocations: 188.382 MB)
#   2.494650 seconds (4.09 M allocations: 171.112 MB)
#   2.528842 seconds (4.09 M allocations: 171.112 MB)


text:
#################################################################
#
#    1st order derivation testing
#
#################################################################

###### sum()
@compare sum(x)  v0ref
@compare sum(x)  v1ref
@compare sum(x)  v2ref

###### abs()
@compare abs(x)        v0ref
@compare sum(abs(x))   v1ref .+ 0.1(sign(v1ref).==0.)
@compare sum(abs(x))   v2ref .+ 0.1(sign(v2ref).==0.)

###### sin()
@compare sin(x)      v0ref
@compare sum(sin(x)) v1ref
@compare sum(sin(x)) v2ref

###### cos()
@compare cos(x)      v0ref
@compare sum(cos(x)) v1ref
@compare sum(cos(x)) v2ref

###### exp()
@compare exp(x)      v0ref
@compare sum(exp(x)) v1ref
@compare sum(exp(x)) v2ref

###### log()
@compare log(x)      v0ref
@compare sum(log(x)) abs( v1ref .+ 0.1(sign(v1ref).==0.) )
@compare sum(log(x)) abs( v2ref .+ 0.1(sign(v2ref).==0.) )

###### sqrt()
@compare sqrt(x)      v0ref
@compare sum(sqrt(x)) abs( v1ref .+ 0.1(sign(v1ref).==0.))

###### - (unary)
@compare -x      v0ref
@compare sum(-x) v1ref
@compare sum(-x) v2ref

###### ' transpose
@compare x'                v0ref
@compare sum(x')           v1ref
@compare sum(x')           v2ref
@compare transpose(x)      v0ref
@compare sum(transpose(x)) v1ref
@compare sum(transpose(x)) v2ref

###### +
@compare x + 1.    v0ref
@compare 1. + x    v0ref

@compare sum(x + v1ref) v1ref
@compare sum(v1ref + x) v1ref
@compare sum(x + v2ref) v2ref
@compare sum(v2ref + x) v2ref

###### -
@compare x - 1.    v0ref
@compare 1. - x    v0ref

@compare sum(x - v1ref)    v1ref
@compare sum(v1ref - x)    v1ref
@compare sum(x - 0.5v2ref) v2ref
@compare sum(3v2ref - x)   v2ref

###### .+
@compare x  .+ 1.   v0ref
@compare 1. .+ x    v0ref

@compare sum(x  .+ 2.)   v1ref
@compare sum(3. .+ x)    v1ref
@compare sum(x .+ v1ref) v1ref
@compare sum(v1ref .+ x) v1ref

@compare sum(x  .+ 2.)   v2ref
@compare sum(3. .+ x)    v2ref
@compare sum(x .+ v2ref) v2ref
@compare sum(v2ref .+ x) v2ref

###### .-
@compare x  .- 1.   v0ref
@compare 1. .- x    v0ref

@compare sum(x  .- 2.)   v1ref
@compare sum(3. .- x)    v1ref
@compare sum(x .- v1ref) v1ref
@compare sum(v1ref .- x) v1ref

@compare sum(x  .- 2.)   v2ref
@compare sum(3. .- x)    v2ref
@compare sum(x .- v2ref) v2ref
@compare sum(v2ref .- x) v2ref

###### .*
@compare x  .* 1.   v0ref
@compare 1. .* x    v0ref

@compare sum(x  .* 2.)   v1ref
@compare sum(3. .* x)    v1ref
@compare sum(x .* v1ref) v1ref
@compare sum(v1ref .* x) v1ref

@compare sum(x  .* 2.)   v2ref
@compare sum(3. .* x)    v2ref
@compare sum(x .* v2ref) v2ref
@compare sum(v2ref .* x) v2ref

###### ./
@compare x  ./ 1.   v0ref
@compare 1. ./ x    v0ref

@compare sum(x      ./ 2.)             v1ref
@compare sum(3.     ./  x)             v1ref .+ 0.1(sign(v1ref) .== 0.)
@compare sum(x      ./ (v1ref .+ 0.1)) v1ref
@compare sum(2v1ref ./  x)             v1ref .+ 0.1(sign(v1ref) .== 0.)

@compare sum(x      ./ 2.)             v2ref
@compare sum(3.     ./  x)             v2ref .+ 0.1(sign(v2ref) .== 0.)
@compare sum(x      ./ (v2ref .+ 0.1)) v2ref
@compare sum(2v2ref ./  x)             v2ref .+ 0.1(sign(v2ref) .== 0.)

###### /
@compare   x / 3.   v0ref
@compare -1. / x    v0ref

@compare sum(x /   2.)     v1ref
@compare sum(x / -0.5)     v2ref

###### max
@compare max( x, 1.)   v0ref
@compare max(1.,  x)   v0ref

@compare sum(max(            x,                   2.1))  v1ref
@compare sum(max(          -3.,                     x))  v1ref
@compare sum(max(            x, 1. .- abs(v1ref)./2.1))  v1ref
@compare sum(max(0.5abs(v1ref),                x.+0.1))  v1ref

@compare sum(max(            x      , -1.1                )) v2ref
@compare sum(max(          0.1      , x                   )) v2ref
@compare sum(max(            x      , -abs(v2ref)./2.+0.1 )) v2ref
@compare sum(max(0.5abs(v2ref).+0.1 , x                   )) v2ref

###### min
@compare min( x, 1.)   v0ref
@compare min(1.,  x)   v0ref

@compare sum(min(            x,              2.))  v1ref
@compare sum(min(          -3.,               x))  v1ref
@compare sum(min(            x, -abs(v1ref)./2.))  v1ref
@compare sum(min(0.5abs(v1ref),               x))  v1ref

@compare sum(min(            x,             -1.))  v2ref
@compare sum(min(           0.,               x))  v2ref
@compare sum(min(            x, -abs(v2ref)./2.))  v2ref
@compare sum(min(0.5abs(v2ref),               x))  v2ref

###### maximum
@compare maximum(x)   v0ref
@compare maximum(x)   v1ref
@compare maximum(x)   v2ref

###### minimum
@compare minimum(x)   v0ref
@compare minimum(x)   v1ref
@compare minimum(x)   v2ref

###### dot
@compare dot(  x, 3.)   v0ref
@compare dot(-1.,  x)   v0ref

@compare dot(            x, -abs(v1ref)./2.)  v1ref
@compare dot(0.5abs(v1ref),               x)  v1ref

###### ^
@compare x ^ 3.   v0ref
@compare 3. ^ x   v0ref

###### .^
@compare x .^ 3.    v0ref
@compare 3 .^ x     v0ref
@compare x .^ -3.   v0ref

@compare sum( x .^  2)  v1ref
@compare sum( x .^ -1)  v1ref .+ 0.1(sign(v1ref) .== 0.)
@compare sum( 2 .^  x)  v1ref

@compare sum( x .^  2)  v2ref
@compare sum( x .^ -1)  v2ref .+ 0.1(sign(v2ref) .== 0.)
@compare sum( 2 .^  x)  v2ref

###### *
@compare  x * 3.    v0ref
@compare -2 * x     v0ref

@compare  sum(ones(5)'   * x)            v1ref
@compare  sum(x'         * v1ref)        v1ref
@compare  sum(ones(5)    * x')           v1ref
@compare  sum(x          * (v1ref./2.)') v1ref

@compare  sum(2ones(5,2) * x)            v2ref
@compare  sum(zeros(1,2) * x)            v2ref
@compare  sum(x          * ones(3,2))    v2ref
@compare  sum(x          * -1ones(3,1))  v2ref

###### tan
@compare tan(x)      v0ref
@compare sum(tan(x)) v1ref
@compare sum(tan(x)) v2ref

###### sinh
@compare sinh(x)      v0ref
@compare sum(sinh(x)) v1ref
@compare sum(sinh(x)) v2ref

###### cosh
@compare cosh(x)      v0ref
@compare sum(cosh(x)) v1ref
@compare sum(cosh(x)) v2ref

###### tanh
@compare tanh(x)      v0ref
@compare sum(tanh(x)) v1ref
@compare sum(tanh(x)) v2ref

###### asin
@compare asin(x)      clamp(v0ref, -.99, .99)
@compare sum(asin(x)) clamp(v1ref, -.99, .99)
@compare sum(asin(x)) clamp(v2ref, -.99, .99)

###### acos
@compare acos(x)      clamp(v0ref, -.99, .99)
@compare sum(acos(x)) clamp(v1ref, -.99, .99)
@compare sum(acos(x)) clamp(v2ref, -.99, .99)

###### atan
@compare atan(x)      v0ref
@compare sum(atan(x)) v1ref
@compare sum(atan(x)) v2ref

###### round
@compare round(x)      v0ref
@compare sum(round(x)) v1ref
@compare sum(round(x)) v2ref

###### ceil
@compare ceil(x)      v0ref + 1e-5
@compare sum(ceil(x)) v1ref + 1e-5
@compare sum(ceil(x)) v2ref + 1e-5

###### floor
@compare floor(x)      v0ref + 1e-5
@compare sum(floor(x)) v1ref + 1e-5
@compare sum(floor(x)) v2ref + 1e-5

###### trunc
@compare trunc(x)      v0ref + 1e-5
@compare sum(trunc(x)) v1ref + 1e-5
@compare sum(trunc(x)) v2ref + 1e-5

###### mod2pi
@compare mod2pi(x)      v0ref

###### log1p
@compare log1p(x)      clamp(v0ref, -0.99, Inf)
@compare sum(log1p(x)) clamp(v1ref, -0.99, Inf)
@compare sum(log1p(x)) clamp(v2ref, -0.99, Inf)

###### expm1
@compare expm1(x)      v0ref
@compare sum(expm1(x)) v1ref
@compare sum(expm1(x)) v2ref

###### erf
@compare erf(x)      v0ref
@compare sum(erf(x)) v1ref
@compare sum(erf(x)) v2ref

###### erfc
@compare erfc(x)      v0ref
@compare sum(erfc(x)) v1ref
@compare sum(erfc(x)) v2ref

###### gamma
@compare gamma(x)      v0ref + 0.1 # to stay away from negative integers
@compare sum(gamma(x)) v1ref + 0.1
@compare sum(gamma(x)) v2ref + 0.1

###### lgamma
@compare lgamma(x)      v0ref + 0.1
@compare sum(lgamma(x)) v1ref + 0.1
@compare sum(lgamma(x)) v2ref + 0.1

###### beta
@compare beta(x, 2.)             v0ref + 0.1
@compare sum(beta(x, x .* 0.2))  v1ref + 0.1
@compare sum(beta(x, x))         v2ref + 0.1

###### lbeta
@compare lbeta(x, 2.)             v0ref + 0.1
@compare sum(lbeta(x, x .* 0.2))  v1ref + 0.1
@compare sum(lbeta(x, x))         v2ref + 0.1

ext: jl


text:
using JSON
using Dates
import Base: *
using TimeZones

include("boss.jl")
const management_json_file_name ="management.json"

f = open(management_json_file_name,"r")
management_dict = JSON.parse(f)
close(f)

*(bool::Bool,str::String) = bool ? str : ""
*(str::String,bool::Bool) = bool ? str : ""

function isdecimal(arg)
    try
        parse(Int,arg)
        return true
    catch ArgumentError
        return false
    end
end

function int(arg)
    return parse(Int,arg)
end

function save_management_dict(management_dict)
    open(management_json_file_name,"w") do f
        JSON.Writer.print(f,management_dict)
    end
end

function get_default_hp(lap,boss_number)
    if lap >= 11
        return Boss_dict["very_hard"][boss_number].hp
    else
        return  Boss_dict["normal"][boss_number].hp
    end
end

function arrange_reserve_list(reserve_list)
    text = ""
    if length(reserve_list) == 0
        return text
    end

    for reserve_dict in reserve_list
        if reserve_dict["over"]
            text *= "\t$(reserve_dict["name"]) $(reserve_dict["damage"]) 持ち越し $(reserve_dict["attack"]*"凸中")\n"
        end
    end

    for reserve_dict in reserve_list
        if !reserve_dict["over"]
            text *= "\t$(reserve_dict["name"]) $(reserve_dict["damage"]) $(reserve_dict["attack"]*"凸中")\n"
        end
    end

    return text
end

function create_output_message(channel_dict)
    boss_number =channel_dict["boss_number"]
    message_contetnt = """
    $(channel_dict["lap"])週目 現在のボスの予約状況は以下の通りです
    **$(Boss_dict["normal"][boss_number].name)** 残HP:$(channel_dict["reserve"][string(boss_number)]["remain_hp"])万
    凸終了後予定HP:$(channel_dict["reserve"][string(boss_number)]["plan_remain_hp"])万
    $(arrange_reserve_list(channel_dict["reserve"][string(boss_number)]["reserve_list"]))

    [凸予約状況]
    """

    for i = 1:4
        boss_number += 1
        boss_number = (boss_number>5) ? boss_number-5 : boss_number

        message_contetnt *=  """
        $(Boss_dict["normal"][boss_number].name)
        凸終了後予定残HP:$(channel_dict["reserve"][string(boss_number)]["plan_remain_hp"])万
        $(arrange_reserve_list(channel_dict["reserve"][string(boss_number)]["reserve_list"]))

        """
    end
    return message_contetnt

end

function get_display_name(message::Message)
    display_name = message.member.nick
    if display_name === missing
        display_name = message.author.username
    end
    return display_name
end

function get_display_name(member::Member)
    display_name = member.nick
    if display_name === missing
        display_name = member.user.username
    end
    return display_name
end

function calc_plan_remain_hp(reserve_boss_dict)
    #現在の残りHPから凸終了後予定HPを計算する
    plan_remain_hp = reserve_boss_dict["remain_hp"]

    reserve_list = reserve_boss_dict["reserve_list"]

    for reserve in reserve_list
        plan_remain_hp -= reserve["damage"]
    end

    if plan_remain_hp <0
        plan_remain_hp = 0
    end

    return plan_remain_hp
end

#残凸状況を記録するための辞書を保管
function initiate_remain_attack_dict(c::Client,channel_dict)

    guild  = fetchval(get_guild(c,channel_dict["guild_id"]))
    members = guild.members

    remain_attack_dict = Dict()
    for member in members
        if member.user.bot !== true
            remain_attack_dict[string(member.user.id)] = Dict(
                "remain" => 3,
                "name" => get_display_name(member),
                "over" => false,
                "over_content" => ""
            )
        end
    end

    return remain_attack_dict
end

function make_remain_attack_message_content(remain_attack_dict,date_string)
    content = " $(date_string) の凸状況です\n"

    for key in keys(remain_attack_dict)
        content *= "$(remain_attack_dict[key]["name"]) $(remain_attack_dict[key]["remain"]) $(remain_attack_dict[key]["over"]*"持ち越し") $(remain_attack_dict[key]["over_content"])\n"
    end

    return content
end

max_stars_repo_name: hibibol/clan-battle

max_issues_repo_name: hibibol/clan-battle

max_forks_repo_name: hibibol/clan-battle


text:
using SciMLBase: ODEProblem, DAEProblem, EnsembleProblem, terminate!, solve, VectorContinuousCallback
const ODEType = Union{ODEProblem, DAEProblem}

function getVectorField(prob::Union{ODEProblem, DAEProblem})
	if isinplace(prob)
		return (x, p) -> (out = similar(x); prob.f(out, x, p, prob.tspan[1]); return out)
	else
		return (x, p) -> prob.f(x, p, prob.tspan[1])
	end
end
getVectorField(pb::EnsembleProblem) = getVectorField(pb.prob)
####################################################################################################
### 									STANDARD SHOOTING
####################################################################################################
# this constructor takes into accound a parameter passed to the vector field
# if M = 1, we disable parallel processing
function ShootingProblem(prob::ODEType, alg, ds, section; parallel = false, kwargs...)
	_M = length(ds)
	parallel = _M == 1 ? false : parallel
	_pb = parallel ? EnsembleProblem(prob) : prob
	return ShootingProblem(M = _M, flow = Flow(_pb, alg; kwargs...),
			ds = ds, section = section, parallel = parallel)
end

ShootingProblem(prob::ODEType, alg, M::Int, section; parallel = false, kwargs...) = ShootingProblem(prob, alg, diff(LinRange(0, 1, M + 1)), section; parallel = parallel, kwargs...)

function ShootingProblem(prob::ODEType, alg, centers::AbstractVector; parallel = false, kwargs...)
	p = prob.p # parameters
	F = getVectorField(prob)
	ShootingProblem(prob, alg, diff(LinRange(0, 1, length(centers) + 1)), SectionSS(F(centers[1], p)./ norm(F(centers[1], p)), centers[1]); parallel = parallel, kwargs...)
end

# this is the "simplest" constructor to use in automatic branching from Hopf
ShootingProblem(M::Int, prob::ODEType, alg; parallel = false, kwargs...) = ShootingProblem(prob, alg, M, nothing; parallel = parallel, kwargs...)

ShootingProblem(M::Int, prob1::ODEType, alg1, prob2::ODEType, alg2; parallel = false, kwargs...) = ShootingProblem(prob1, alg1, prob2, alg2, M, nothing; parallel = parallel, kwargs...)

# idem but with an ODEproblem to define the derivative of the flow
function ShootingProblem(prob1::ODEType, alg1, prob2::ODEType, alg2, ds, section; parallel = false, kwargs...)
	_M = length(ds)
	parallel = _M == 1 ? false : parallel
	_pb1 = parallel ? EnsembleProblem(prob1) : prob1
	_pb2 = parallel ? EnsembleProblem(prob2) : prob2
	ShootingProblem(M = _M, flow = Flow(_pb1, alg1, _pb2, alg2; kwargs...), ds = ds, section = section, parallel = parallel)
end

ShootingProblem(prob1::ODEType, alg1, prob2::ODEType, alg2, M::Int, section; parallel = false, kwargs...) = ShootingProblem(prob1, alg1, prob2, alg2, diff(LinRange(0, 1, M + 1)), section; parallel = parallel, kwargs...)

function ShootingProblem(prob1::ODEType, alg1, prob2::ODEType, alg2, centers::AbstractVector; parallel = false, kwargs...)
	F = getVectorField(prob1)
	p = prob1.p # parameters
	ShootingProblem(prob1, alg1, prob2, alg2, diff(LinRange(0, 1, length(centers) + 1)), SectionSS(F(centers[1], p)./ norm(F(centers[1], p)), centers[1]); parallel = parallel, kwargs...)
end
####################################################################################################
### 									POINCARE SHOOTING
####################################################################################################
function PoincareShootingProblem(prob::ODEProblem, alg,
			hyp::SectionPS;
			δ = 1e-8, interp_points = 50, parallel = false, kwargs...)
	p = prob.p # parameters
	pSection(out, u, t, integrator) = (hyp(out, u); out .*= integrator.iter > 1)
	affect!(integrator, idx) = terminate!(integrator)
	# we put nothing option to have an upcrossing
	cb = VectorContinuousCallback(pSection, affect!, hyp.M; interp_points = interp_points, affect_neg! = nothing)
	# change the ODEProblem -> EnsembleProblem for the parallel case
	_M = hyp.M
	parallel = _M == 1 ? false : parallel
	_pb = parallel ? EnsembleProblem(prob) : prob
	return PoincareShootingProblem(flow = Flow(_pb, alg; callback = cb, kwargs...), M = hyp.M, section = hyp, δ = δ, parallel = parallel)
end

# this is the "simplest" constructor to use in automatic branching from Hopf
# this is a Hack to pass the arguments to construct a Flow. Indeed, we need to provide the
# appropriate callback for Poincare Shooting to work
PoincareShootingProblem(M::Int, prob::ODEProblem, alg; parallel = false, section = SectionPS(M), kwargs...) = PoincareShootingProblem(M = M, flow = (par = prob.p, prob = prob, alg = alg, kwargs = kwargs), parallel = (M == 1 ? false : parallel), section = section)

PoincareShootingProblem(M::Int, prob1::ODEProblem, alg1, prob2::ODEProblem, alg2; parallel = false, section = SectionPS(M), kwargs...) = PoincareShootingProblem(M = M, flow = (par = prob1.p, prob1 = prob1, alg1 = alg1, prob2 = prob2, alg2 = alg2, kwargs = kwargs), parallel = parallel, section = section)

function PoincareShootingProblem(prob::ODEProblem, alg,
			normals::AbstractVector, centers::AbstractVector;
			δ = 1e-8, interp_points = 50, parallel = false, kwargs...)
	return PoincareShootingProblem(prob, alg,
					SectionPS(normals, centers);
					δ = δ, interp_points = interp_points, parallel = parallel, kwargs...)
end

function PoincareShootingProblem(prob1::ODEProblem, alg1,
				prob2::ODEProblem, alg2,
				hyp::SectionPS;
				δ = 1e-8, interp_points = 50, parallel = false, kwargs...)
	p = prob1.p # parameters
	pSection(out, u, t, integrator) = (hyp(out, u); out .*= integrator.iter > 1)
	affect!(integrator, idx) = terminate!(integrator)
	# we put nothing option to have an upcrossing
	cb = VectorContinuousCallback(pSection, affect!, hyp.M; interp_points = interp_points, affect_neg! = nothing)
	# change the ODEProblem -> EnsembleProblem for the parallel case
	_M = hyp.M
	parallel = _M == 1 ? false : parallel
	_pb1 = parallel ? EnsembleProblem(prob1) : prob1
	_pb2 = parallel ? EnsembleProblem(prob2) : prob2
	return PoincareShootingProblem(flow = Flow(_pb1, alg1, _pb2, alg2; callback = cb, kwargs...), M = hyp.M, section = hyp, δ = δ, parallel = parallel)
end

function PoincareShootingProblem(prob1::ODEProblem, alg1,
				prob2::ODEProblem, alg2,
				normals::AbstractVector, centers::AbstractVector;
				δ = 1e-8, interp_points = 50, parallel = false, kwargs...)
	return PoincareShootingProblem(prob1, alg2, prob2, alg2,
					SectionPS(normals, centers);
					δ = δ, interp_points = interp_points, parallel = parallel, kwargs...)
end


text:
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md

using JuliaFEM, Test

# 2d poisson problem with known analytical solution
# from FENiCS tutorial, u(x,y) = 1 + x² + 2y² on [0x1]×[0,1]
# and u₀(x,y) = 1 + x² + 2y², f(x,y) = -6

mesh_file = @__DIR__()*"/testdata/primitives.med"
mesh = aster_read_mesh(mesh_file, "UNITSQUARE_6X4")

square = Problem(PlaneHeat, "unit square, 6x4 triangular mesh", 1)
square_elements = create_elements(mesh, "UNITSQUARE")
update!(square_elements, "thermal conductivity", 1.0)
update!(square_elements, "heat source", -6.0)
add_elements!(square, square_elements)

bc = Problem(Dirichlet, "u₀(x,y) = 1 + x² + 2y²", 1, "temperature")
bc_elements = create_elements(mesh, "FACE1", "FACE2", "FACE3", "FACE4")
function u0(element, ip, time)
    x, y = element("geometry", ip, time)
    return 1 + x^2 + 2*y^2
end
update!(bc_elements, "temperature 1", u0)
add_elements!(bc, bc_elements)

analysis = Analysis(Linear)
add_problems!(analysis, square, bc)
run!(analysis)

T_fem = Float64[]
T_acc = Float64[]
for (nid, Xi) in square("geometry", 0.0)
    push!(T_fem, square("temperature", Xi, 0.0)[1])
    push!(T_acc, 1.0 + Xi[1]^2 + 2*Xi[2]^2)
end

@test maximum(abs.(T_fem-T_acc)) < 1.0e-12

# gradient of field is
X = [0.5, 0.5]
gradT1 = [2*X[1] 4*X[2]]
gradT2 = square("temperature", X, 0.0, Val{:Grad})
@debug("gradT1 = $gradT1, gradT2 = $gradT2")
# [1.1666666666666625 1.5000000000000018] quite big difference ..?
@test isapprox(gradT1, gradT2; rtol=25.0e-2)


text:
using FractalTools
using Plots

function example_attractor_of_ifs(N_iter)
    # An example of one dimensional ifs
    A1 = reshape([1/2],1,1)
    b1 = [0]
    A2 = reshape([1/2],1,1)
    b2 = [1/2]

    w1 = Transformation(A1,b1)
    w2 = Transformation(A2,b2)

    w = [w1, w2]
    ifs = IFS(w)
    initset = [[0.1]]
    atr = attractor(ifs, initset; alg=RandAlg(), numiter=N_iter,allocated=true)
    attrset = vcat(atr.set...)
    t = ones(length(attrset))
    return atr, attrset, t
end

atr, attrset, t = example_attractor_of_ifs(10)
p1 = scatter(t, attrset, title="Attractor of the IFS", label="Attractor Points", markersize = 4)

atr, attrset, t = example_attractor_of_ifs(100)
p2 = scatter(t, attrset, title="Attractor of the IFS", label="Attractor Points", markersize = 4)

atr, attrset, t = example_attractor_of_ifs(10000)
p3 = scatter(t, attrset, title="Attractor of the IFS", label="Attractor Points", markersize = 4)

plot(p1, p2, p3, layout=(1,3))
xlabel!("x")
ylabel!("z")
xlims!((0.9,1.1))
# savefig("myplot.png")

# An example of atr with Channel
atr = attractor(ifs1, initset; alg=RandAlg(), numiter=100, allocated=false)
collect(atr.set)
take!(atr.generator)
# An example of atr with randalg_sequential_generator
# generator = randalg_sequential_generator(ifs1.ws, ifs1.probs)
# take!(generator)

# An example of two dimensional ifs
A = [1/2 0;0 1/2]
b1 = [0; 0]
b2 = [0; 1/2]
b3 = [1/2; 1/2]
b4 = [1/2; 0]

w1 = Transformation(A,b1)
w2 = Transformation(A,b2)
w3 = Transformation(A,b3)
w4 = Transformation(A,b4)

w = [w1, w2, w3, w4]
ifs2 = IFS(w)
ifs2.ws
ifs2.probs

# An example of different probabilities

p = [0.2, 0.4, 0.1, 0.3]

ifs3 = IFS(w,p)
ifs3.ws
ifs3.probs


text:
include("../julia/ProgramTunnel_fs.jl")

using .ProgramTunnel_fs
using Formatting

PTI = ProgramTunnelInfo()
reverseRole!(PTI)

t_mean = 2.0
t_std  = 0.2

for i=1:100
    global t_mean

    t = t_mean + rand()*t_std
    msg = format("[{:3d}] This time: {:03f}.", i, t)

    println(msg)
    sleep(t)

    sendText(PTI, msg)

    t_mean += 0.02
    if i == 30
        t_mean *= 0.7
    end

end

max_stars_repo_name: meteorologytoday/IOM

max_issues_repo_name: meteorologytoday/IOM

max_forks_repo_name: meteorologytoday/IOM


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

import Base: show, *, convert, unsafe_convert, size, strides, ndims, pointer, A_mul_B!

export export_wisdom, import_wisdom, import_system_wisdom, forget_wisdom,
       MEASURE, DESTROY_INPUT, UNALIGNED, CONSERVE_MEMORY, EXHAUSTIVE,
       PRESERVE_INPUT, PATIENT, ESTIMATE, WISDOM_ONLY, NO_TIMELIMIT,
       R2HC, HC2R, DHT, REDFT00, REDFT01, REDFT10, REDFT11,
       RODFT00, RODFT01, RODFT10, RODFT11,
       fftwNumber, fftwReal, fftwComplex, flops

## FFT: Implement fft by calling fftw.

const libfftw = Base.libfftw_name
const libfftwf = Base.libfftwf_name

const version = convert(VersionNumber, split(unsafe_string(cglobal(
    (:fftw_version,Base.DFT.FFTW.libfftw), UInt8)), ['-', ' '])[2])

## Direction of FFT

const FORWARD = -1
const BACKWARD = 1

## FFTW Flags from fftw3.h

const MEASURE         = UInt32(0)
const DESTROY_INPUT   = UInt32(1 << 0)
const UNALIGNED       = UInt32(1 << 1)
const CONSERVE_MEMORY = UInt32(1 << 2)
const EXHAUSTIVE      = UInt32(1 << 3)   # NO_EXHAUSTIVE is default
const PRESERVE_INPUT  = UInt32(1 << 4)   # cancels DESTROY_INPUT
const PATIENT         = UInt32(1 << 5)   # IMPATIENT is default
const ESTIMATE        = UInt32(1 << 6)
const WISDOM_ONLY     = UInt32(1 << 21)
const NO_SIMD = UInt32(1 << 17) # disable SIMD, useful for benchmarking

## R2R transform kinds

const R2HC    = 0
const HC2R    = 1
const DHT     = 2
const REDFT00 = 3
const REDFT01 = 4
const REDFT10 = 5
const REDFT11 = 6
const RODFT00 = 7
const RODFT01 = 8
const RODFT10 = 9
const RODFT11 = 10

let k2s = Dict(R2HC => "R2HC", HC2R => "HC2R", DHT => "DHT", REDFT00 => "REDFT00", REDFT01 => "REDFT01", REDFT10 => "REDFT10", REDFT11 => "REDFT11", RODFT00 => "RODFT00", RODFT01 => "RODFT01", RODFT10 => "RODFT10", RODFT11 => "RODFT11")
    global kind2string
    kind2string(k::Integer) = k2s[Int(k)]
end

# FFTW floating-point types:

const fftwNumber = Union{Float64,Float32,Complex128,Complex64}
const fftwReal = Union{Float64,Float32}
const fftwComplex = Union{Complex128,Complex64}
const fftwDouble = Union{Float64,Complex128}
const fftwSingle = Union{Float32,Complex64}
const fftwTypeDouble = Union{Type{Float64},Type{Complex128}}
const fftwTypeSingle = Union{Type{Float32},Type{Complex64}}

# For ESTIMATE plans, FFTW allows one to pass NULL for the array pointer,
# since it is not written to.  Hence, it is convenient to create an
# array-like type that carries a size and a stride like a "real" array
# but which is converted to C_NULL as a pointer.
struct FakeArray{T, N} <: DenseArray{T, N}
    sz::NTuple{N, Int}
    st::NTuple{N, Int}
end
size(a::FakeArray) = a.sz
strides(a::FakeArray) = a.st
unsafe_convert{T}(::Type{Ptr{T}}, a::FakeArray{T}) = convert(Ptr{T}, C_NULL)
pointer{T}(a::FakeArray{T}) = convert(Ptr{T}, C_NULL)
FakeArray{T, N}(::Type{T}, sz::NTuple{N, Int}) =
    FakeArray{T, N}(sz, colmajorstrides(sz))
FakeArray{T}(::Type{T}, sz::Int...) = FakeArray(T, sz)
fakesimilar(flags, X, T) = flags & ESTIMATE != 0 ? FakeArray(T, size(X)) : Array{T}(size(X))
alignment_of(A::FakeArray) = Int32(0)

## Julia wrappers around FFTW functions

# Wisdom

# Import and export wisdom to/from a single file for all precisions,
# which is more user-friendly than requiring the user to call a
# separate routine depending on the fp precision of the plans.  This
# requires a bit of trickness since we have to (a) use the libc file
# I/O routines with fftw_export_wisdom_to_file/import_wisdom_from_file
# (b) we need 256 bytes of space padding between the wisdoms to work
# around FFTW's internal file i/o buffering [see the BUFSZ constant in
# FFTW's api/import-wisdom-from-file.c file].

function export_wisdom(fname::AbstractString)
    f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :w)
    systemerror("could not open wisdom file $fname for writing", f == C_NULL)
    ccall((:fftw_export_wisdom_to_file,libfftw), Void, (Ptr{Void},), f)
    ccall(:fputs, Int32, (Ptr{UInt8},Ptr{Void}), " "^256, f) # no NUL, hence no Cstring
    ccall((:fftwf_export_wisdom_to_file,libfftwf), Void, (Ptr{Void},), f)
    ccall(:fclose, Void, (Ptr{Void},), f)
end

function import_wisdom(fname::AbstractString)
    f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :r)
    systemerror("could not open wisdom file $fname for reading", f == C_NULL)
    if ccall((:fftw_import_wisdom_from_file,libfftw),Int32,(Ptr{Void},),f)==0||
       ccall((:fftwf_import_wisdom_from_file,libfftwf),Int32,(Ptr{Void},),f)==0
        error("failed to import wisdom from $fname")
    end
    ccall(:fclose, Void, (Ptr{Void},), f)
end

function import_system_wisdom()
    if ccall((:fftw_import_system_wisdom,libfftw), Int32, ()) == 0 ||
       ccall((:fftwf_import_system_wisdom,libfftwf), Int32, ()) == 0
        error("failed to import system wisdom")
    end
end

function forget_wisdom()
    ccall((:fftw_forget_wisdom,libfftw), Void, ())
    ccall((:fftwf_forget_wisdom,libfftwf), Void, ())
end

# Threads

let initialized = false
    global set_num_threads
    function set_num_threads(nthreads::Integer)
        if !initialized
            # must re-initialize FFTW if any FFTW routines have been called
            ccall((:fftw_cleanup,libfftw), Void, ())
            ccall((:fftwf_cleanup,libfftwf), Void, ())
            stat = ccall((:fftw_init_threads,libfftw), Int32, ())
            statf = ccall((:fftwf_init_threads,libfftwf), Int32, ())
            if stat == 0 || statf == 0
                error("could not initialize FFTW threads")
            end
            initialized = true
        end
        ccall((:fftw_plan_with_nthreads,libfftw), Void, (Int32,), nthreads)
        ccall((:fftwf_plan_with_nthreads,libfftwf), Void, (Int32,), nthreads)
    end
end

# pointer type for fftw_plan (opaque pointer)

struct fftw_plan_struct end
const PlanPtr = Ptr{fftw_plan_struct}

# Planner timelimits

const NO_TIMELIMIT = -1.0 # from fftw3.h

set_timelimit(precision::fftwTypeDouble,seconds) =
    ccall((:fftw_set_timelimit,libfftw), Void, (Float64,), seconds)

set_timelimit(precision::fftwTypeSingle,seconds) =
    ccall((:fftwf_set_timelimit,libfftwf), Void, (Float64,), seconds)

# Array alignment mod 16:
#   FFTW plans may depend on the alignment of the array mod 16 bytes,
#   i.e. the address mod 16 of the first element of the array, in order
#   to exploit SIMD operations.  Julia arrays are, by default, aligned
#   to 16-byte boundaries (address mod 16 == 0), but this may not be
#   true for data imported from external C code, or for SubArrays.
#   Use the undocumented routine fftw_alignment_of to determine the
#   alignment of a given pointer modulo whatever FFTW needs; this
#   function will be documented in FFTW 3.3.4.

if Base.libfftw_name == "libmkl_rt"
    alignment_of(A::StridedArray{<:fftwDouble}) =
        convert(Int32, convert(Int64, pointer(A)) % 16)
    alignment_of(A::StridedArray{<:fftwSingle}) =
        convert(Int32, convert(Int64, pointer(A)) % 16)
else
    alignment_of{T<:fftwDouble}(A::StridedArray{T}) =
        ccall((:fftw_alignment_of, libfftw), Int32, (Ptr{T},), A)
    alignment_of{T<:fftwSingle}(A::StridedArray{T}) =
        ccall((:fftwf_alignment_of, libfftwf), Int32, (Ptr{T},), A)
end

# FFTWPlan (low-level)

# low-level storage of the FFTW plan, along with the information
# needed to determine whether it is applicable.   We need to put
# this into a type to support a finalizer on the fftw_plan.
# K is FORWARD/BACKWARD for forward/backward or r2c/c2r plans, respectively.
# For r2r plans, K is a tuple of the transform kinds along each dimension.
abstract type FFTWPlan{T<:fftwNumber,K,inplace} <: Plan{T} end
for P in (:cFFTWPlan, :rFFTWPlan, :r2rFFTWPlan) # complex, r2c/c2r, and r2r
    @eval begin
        mutable struct $P{T<:fftwNumber,K,inplace,N} <: FFTWPlan{T,K,inplace}
            plan::PlanPtr
            sz::NTuple{N, Int} # size of array on which plan operates (Int tuple)
            osz::NTuple{N, Int} # size of output array (Int tuple)
            istride::NTuple{N, Int} # strides of input
            ostride::NTuple{N, Int} # strides of output
            ialign::Int32 # alignment mod 16 of input
            oalign::Int32 # alignment mod 16 of input
            flags::UInt32 # planner flags
            region::Any # region (iterable) of dims that are transormed
            pinv::ScaledPlan
            function $P{T,K,inplace,N}(plan::PlanPtr, flags::Integer, R::Any,
                                       X::StridedArray{T, N}, Y::StridedArray) where {T<:fftwNumber,K,inplace,N}
                p = new(plan, size(X), size(Y), strides(X), strides(Y),
                        alignment_of(X), alignment_of(Y), flags, R)
                finalizer(p, destroy_plan)
                p
            end
        end
    end
end

size(p::FFTWPlan) = p.sz

unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan

destroy_plan(plan::FFTWPlan{<:fftwDouble}) =
    ccall((:fftw_destroy_plan,libfftw), Void, (PlanPtr,), plan)

destroy_plan(plan::FFTWPlan{<:fftwSingle}) =
    ccall((:fftwf_destroy_plan,libfftwf), Void, (PlanPtr,), plan)

cost(plan::FFTWPlan{<:fftwDouble}) =
    ccall((:fftw_cost,libfftw), Float64, (PlanPtr,), plan)
cost(plan::FFTWPlan{<:fftwSingle}) =
    ccall((:fftwf_cost,libfftwf), Float64, (PlanPtr,), plan)

function arithmetic_ops(plan::FFTWPlan{<:fftwDouble})
    # Change to individual Ref after we can allocate them on stack
    ref = Ref{NTuple{3, Float64}}()
    ptr = Ptr{Float64}(Base.unsafe_convert(Ptr{NTuple{3, Float64}}, ref))
    ccall((:fftw_flops,libfftw), Void,
          (PlanPtr,Ptr{Float64},Ptr{Float64},Ptr{Float64}),
          plan, ptr, ptr + 8, ptr + 16)
    (round(Int64, ref[][1]), round(Int64, ref[][2]), round(Int64, ref[][3]))
end
function arithmetic_ops(plan::FFTWPlan{<:fftwSingle})
    # Change to individual Ref after we can allocate them on stack
    ref = Ref{NTuple{3, Float64}}()
    ptr = Ptr{Float64}(Base.unsafe_convert(Ptr{NTuple{3, Float64}}, ref))
    ccall((:fftwf_flops,libfftwf), Void,
          (PlanPtr,Ptr{Float64},Ptr{Float64},Ptr{Float64}),
          plan, ptr, ptr + 8, ptr + 16)
    (round(Int64, ref[][1]), round(Int64, ref[][2]), round(Int64, ref[][3]))
end
flops(plan::FFTWPlan) = let ops = arithmetic_ops(plan)
    ops[1] + ops[2] + 2 * ops[3] # add + mul + 2*fma
end

# Pretty-printing plans

function showfftdims(io, sz::Dims, istride::Dims, T)
    if isempty(sz)
        print(io, "0-dimensional")
    elseif length(sz) == 1
        print(io, sz[1], "-element")
    else
        print(io, join(sz, "×"))
    end
    if istride == colmajorstrides(sz)
        print(io, " array of ", T)
    else
        print(io, " $istride-strided array of ", T)
    end
end

# The sprint_plan function was released in FFTW 3.3.4
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =
    ccall((:fftw_sprint_plan,libfftw), Ptr{UInt8}, (PlanPtr,), plan)
sprint_plan_(plan::FFTWPlan{<:fftwSingle}) =
    ccall((:fftwf_sprint_plan,libfftwf), Ptr{UInt8}, (PlanPtr,), plan)
function sprint_plan(plan::FFTWPlan)
    p = sprint_plan_(plan)
    str = unsafe_string(p)
    Libc.free(p)
    return str
end

function show{T,K,inplace}(io::IO, p::cFFTWPlan{T,K,inplace})
    print(io, inplace ? "FFTW in-place " : "FFTW ",
          K < 0 ? "forward" : "backward", " plan for ")
    showfftdims(io, p.sz, p.istride, T)
    version >= v"3.3.4" && print(io, "\n", sprint_plan(p))
end

function show{T,K,inplace}(io::IO, p::rFFTWPlan{T,K,inplace})
    print(io, inplace ? "FFTW in-place " : "FFTW ",
          K < 0 ? "real-to-complex" : "complex-to-real",
          " plan for ")
    showfftdims(io, p.sz, p.istride, T)
    version >= v"3.3.4" && print(io, "\n", sprint_plan(p))
end

function show{T,K,inplace}(io::IO, p::r2rFFTWPlan{T,K,inplace})
    print(io, inplace ? "FFTW in-place r2r " : "FFTW r2r ")
    if isempty(K)
        print(io, "0-dimensional")
    elseif K == ntuple(i -> K[1], length(K))
        print(io, kind2string(K[1]))
        if length(K) > 1
            print(io, "^", length(K))
        end
    else
        print(io, join(map(kind2string, K), "×"))
    end
    print(io, " plan for ")
    showfftdims(io, p.sz, p.istride, T)
    version >= v"3.3.4" && print(io, "\n", sprint_plan(p))
end

# Check whether a FFTWPlan is applicable to a given input array, and
# throw an informative error if not:
function assert_applicable{T}(p::FFTWPlan{T}, X::StridedArray{T})
    if size(X) != p.sz
        throw(ArgumentError("FFTW plan applied to wrong-size array"))
    elseif strides(X) != p.istride
        throw(ArgumentError("FFTW plan applied to wrong-strides array"))
    elseif alignment_of(X) != p.ialign || p.flags & UNALIGNED != 0
        throw(ArgumentError("FFTW plan applied to array with wrong memory alignment"))
    end
end

function assert_applicable{T,K,inplace}(p::FFTWPlan{T,K,inplace}, X::StridedArray{T}, Y::StridedArray)
    assert_applicable(p, X)
    if size(Y) != p.osz
        throw(ArgumentError("FFTW plan applied to wrong-size output"))
    elseif strides(Y) != p.ostride
        throw(ArgumentError("FFTW plan applied to wrong-strides output"))
    elseif alignment_of(Y) != p.oalign || p.flags & UNALIGNED != 0
        throw(ArgumentError("FFTW plan applied to output with wrong memory alignment"))
    elseif inplace != (pointer(X) == pointer(Y))
        throw(ArgumentError(string("FFTW ",
                                   inplace ? "in-place" : "out-of-place",
                                   " plan applied to ",
                                   inplace ? "out-of-place" : "in-place",
                                   " data")))
    end
end

# strides for a column-major (Julia-style) array of size == sz
colmajorstrides(sz) = isempty(sz) ? () : (1,cumprod(Int[sz[1:end-1]...])...)

# Execute

unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) =
    ccall((:fftw_execute,libfftw), Void, (PlanPtr,), plan)

unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) =
    ccall((:fftwf_execute,libfftwf), Void, (PlanPtr,), plan)

unsafe_execute!{T<:fftwDouble}(plan::cFFTWPlan{T},
                               X::StridedArray{T}, Y::StridedArray{T}) =
    ccall((:fftw_execute_dft,libfftw), Void,
          (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

unsafe_execute!{T<:fftwSingle}(plan::cFFTWPlan{T},
                               X::StridedArray{T}, Y::StridedArray{T}) =
    ccall((:fftwf_execute_dft,libfftwf), Void,
          (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD},
                X::StridedArray{Float64}, Y::StridedArray{Complex128}) =
    ccall((:fftw_execute_dft_r2c,libfftw), Void,
          (PlanPtr,Ptr{Float64},Ptr{Complex128}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD},
                X::StridedArray{Float32}, Y::StridedArray{Complex64}) =
    ccall((:fftwf_execute_dft_r2c,libfftwf), Void,
          (PlanPtr,Ptr{Float32},Ptr{Complex64}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Complex128,BACKWARD},
                X::StridedArray{Complex128}, Y::StridedArray{Float64}) =
    ccall((:fftw_execute_dft_c2r,libfftw), Void,
          (PlanPtr,Ptr{Complex128},Ptr{Float64}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Complex64,BACKWARD},
                X::StridedArray{Complex64}, Y::StridedArray{Float32}) =
    ccall((:fftwf_execute_dft_c2r,libfftwf), Void,
          (PlanPtr,Ptr{Complex64},Ptr{Float32}), plan, X, Y)

unsafe_execute!{T<:fftwDouble}(plan::r2rFFTWPlan{T},
                               X::StridedArray{T}, Y::StridedArray{T}) =
    ccall((:fftw_execute_r2r,libfftw), Void,
          (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

unsafe_execute!{T<:fftwSingle}(plan::r2rFFTWPlan{T},
                               X::StridedArray{T}, Y::StridedArray{T}) =
    ccall((:fftwf_execute_r2r,libfftwf), Void,
          (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

# NOTE ON GC (garbage collection):
#    The FFTWPlan has a finalizer so that gc will destroy the plan,
#    which is necessary for gc to work with plan_fft.  However,
#    even when we are creating a single-use FFTWPlan [e.g. for fftn(x)],
#    we intentionally do NOT call destroy_plan explicitly, and instead
#    wait for garbage collection.  The reason is that, in the common
#    case where the user calls fft(x) a second time soon afterwards,
#    if destroy_plan has not yet been called then FFTW will internally
#    re-use the table of trigonometric constants from the first plan.

# Compute dims and howmany for FFTW guru planner
function dims_howmany(X::StridedArray, Y::StridedArray,
                      sz::Array{Int,1}, region)
    reg = [region...]
    if length(unique(reg)) < length(reg)
        throw(ArgumentError("each dimension can be transformed at most once"))
    end
    ist = [strides(X)...]
    ost = [strides(Y)...]
    dims = [sz[reg] ist[reg] ost[reg]]'
    oreg = [1:ndims(X);]
    oreg[reg] = 0
    oreg = filter(d -> d > 0, oreg)
    howmany = [sz[oreg] ist[oreg] ost[oreg]]'
    return (dims, howmany)
end

# check & convert kinds into int32 array with same length as region
function fix_kinds(region, kinds)
    if length(kinds) != length(region)
        if length(kinds) > length(region)
            throw(ArgumentError("too many transform kinds"))
        else
            if isempty(kinds)
                throw(ArgumentError("must supply a transform kind"))
            end
            k = Array{Int32}(length(region))
            k[1:length(kinds)] = [kinds...]
            k[length(kinds)+1:end] = kinds[end]
            kinds = k
        end
    else
        kinds = Int32[kinds...]
    end
    for i = 1:length(kinds)
        if kinds[i] < 0 || kinds[i] > 10
            throw(ArgumentError("invalid transform kind"))
        end
    end
    return kinds
end

# low-level FFTWPlan creation (for internal use in FFTW module)

for (Tr,Tc,fftw,lib) in ((:Float64,:Complex128,"fftw",libfftw),
                         (:Float32,:Complex64,"fftwf",libfftwf))
    @eval function (::Type{cFFTWPlan{$Tc,K,inplace,N}}){K,inplace,N}(X::StridedArray{$Tc,N},
                                                                     Y::StridedArray{$Tc,N},
                                                                     region, flags::Integer, timelimit::Real)
        direction = K
        set_timelimit($Tr, timelimit)
        R = isa(region, Tuple) ? region : copy(region)
        dims, howmany = dims_howmany(X, Y, [size(X)...], R)
        plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
                     PlanPtr,
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, direction, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return cFFTWPlan{$Tc,K,inplace,N}(plan, flags, R, X, Y)
    end

    @eval function (::Type{rFFTWPlan{$Tr,$FORWARD,inplace,N}}){inplace,N}(X::StridedArray{$Tr,N},
                                                                          Y::StridedArray{$Tc,N},
                                                                          region, flags::Integer, timelimit::Real)
        R = isa(region, Tuple) ? region : copy(region)
        region = circshift([region...],-1) # FFTW halves last dim
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
                     PlanPtr,
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tr}, Ptr{$Tc}, UInt32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return rFFTWPlan{$Tr,$FORWARD,inplace,N}(plan, flags, R, X, Y)
    end

    @eval function (::Type{rFFTWPlan{$Tc,$BACKWARD,inplace,N}}){inplace,N}(X::StridedArray{$Tc,N},
                                                                           Y::StridedArray{$Tr,N},
                                                                           region, flags::Integer, timelimit::Real)
        R = isa(region, Tuple) ? region : copy(region)
        region = circshift([region...],-1) # FFTW halves last dim
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(Y)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
                     PlanPtr,
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tc}, Ptr{$Tr}, UInt32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return rFFTWPlan{$Tc,$BACKWARD,inplace,N}(plan, flags, R, X, Y)
    end

    @eval function (::Type{r2rFFTWPlan{$Tr,ANY,inplace,N}}){inplace,N}(X::StridedArray{$Tr,N},
                                                                       Y::StridedArray{$Tr,N},
                                                                       region, kinds, flags::Integer,
                                                                       timelimit::Real)
        R = isa(region, Tuple) ? region : copy(region)
        knd = fix_kinds(region, kinds)
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
                     PlanPtr,
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, knd, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        r2rFFTWPlan{$Tr,(map(Int,knd)...),inplace,N}(plan, flags, R, X, Y)
    end

    # support r2r transforms of complex = transforms of real & imag parts
    @eval function (::Type{r2rFFTWPlan{$Tc,ANY,inplace,N}}){inplace,N}(X::StridedArray{$Tc,N},
                                                                       Y::StridedArray{$Tc,N},
                                                                       region, kinds, flags::Integer,
                                                                       timelimit::Real)
        R = isa(region, Tuple) ? region : copy(region)
        knd = fix_kinds(region, kinds)
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        dims[2:3, 1:size(dims,2)] *= 2
        howmany[2:3, 1:size(howmany,2)] *= 2
        howmany = [howmany [2,1,1]] # append loop over real/imag parts
        plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
                     PlanPtr,
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, knd, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        r2rFFTWPlan{$Tc,(map(Int,knd)...),inplace,N}(plan, flags, R, X, Y)
    end

end

# Convert arrays of numeric types to FFTW-supported packed complex-float types
# (FIXME: is there a way to use the Julia promotion rules more cleverly here?)
fftwcomplex(X::StridedArray{<:fftwComplex}) = X
fftwcomplex{T<:fftwReal}(X::AbstractArray{T}) =
    copy!(Array{typeof(complex(zero(T)))}(size(X)), X)
fftwcomplex(X::AbstractArray{<:Real}) = copy!(Array{Complex128}(size(X)),X)
fftwcomplex(X::AbstractArray{<:Complex}) = copy!(Array{Complex128}(size(X)), X)
fftwfloat(X::StridedArray{<:fftwReal}) = X
fftwfloat(X::AbstractArray{<:Real}) = copy!(Array{Float64}(size(X)), X)
fftwfloat(X::AbstractArray{<:Complex}) = fftwcomplex(X)

for (f,direction) in ((:fft,FORWARD), (:bfft,BACKWARD))
    plan_f = Symbol("plan_",f)
    plan_f! = Symbol("plan_",f,"!")
    idirection = -direction
    @eval begin
        function $plan_f{T<:fftwComplex,N}(X::StridedArray{T,N}, region;
                                           flags::Integer=ESTIMATE,
                                           timelimit::Real=NO_TIMELIMIT)
            cFFTWPlan{T,$direction,false,N}(X, fakesimilar(flags, X, T),
                                            region, flags, timelimit)
        end

        function $plan_f!{T<:fftwComplex,N}(X::StridedArray{T,N}, region;
                                            flags::Integer=ESTIMATE,
                                            timelimit::Real=NO_TIMELIMIT)
            cFFTWPlan{T,$direction,true,N}(X, X, region, flags, timelimit)
        end
        $plan_f(X::StridedArray{<:fftwComplex}; kws...) =
            $plan_f(X, 1:ndims(X); kws...)
        $plan_f!(X::StridedArray{<:fftwComplex}; kws...) =
            $plan_f!(X, 1:ndims(X); kws...)

        function plan_inv{T<:fftwComplex,N,inplace}(p::cFFTWPlan{T,$direction,inplace,N})
            X = Array{T}(p.sz)
            Y = inplace ? X : fakesimilar(p.flags, X, T)
            ScaledPlan(cFFTWPlan{T,$idirection,inplace,N}(X, Y, p.region,
                                                          p.flags, NO_TIMELIMIT),
                       normalization(X, p.region))
        end
    end
end

function A_mul_B!{T}(y::StridedArray{T}, p::cFFTWPlan{T}, x::StridedArray{T})
    assert_applicable(p, x, y)
    unsafe_execute!(p, x, y)
    return y
end

function *{T,K,N}(p::cFFTWPlan{T,K,false}, x::StridedArray{T,N})
    assert_applicable(p, x)
    y = Array{T}(p.osz)::Array{T,N}
    unsafe_execute!(p, x, y)
    return y
end

function *{T,K}(p::cFFTWPlan{T,K,true}, x::StridedArray{T})
    assert_applicable(p, x)
    unsafe_execute!(p, x, x)
    return x
end

# rfft/brfft and planned variants.  No in-place version for now.

for (Tr,Tc) in ((:Float32,:Complex64),(:Float64,:Complex128))
    # Note: use $FORWARD and $BACKWARD below because of issue #9775
    @eval begin
        function plan_rfft{N}(X::StridedArray{$Tr,N}, region;
                              flags::Integer=ESTIMATE,
                              timelimit::Real=NO_TIMELIMIT)
            osize = rfft_output_size(X, region)
            Y = flags&ESTIMATE != 0 ? FakeArray($Tc,osize...) : Array{$Tc}(osize...)
            rFFTWPlan{$Tr,$FORWARD,false,N}(X, Y, region, flags, timelimit)
        end

        function plan_brfft{N}(X::StridedArray{$Tc,N}, d::Integer, region;
                               flags::Integer=ESTIMATE,
                               timelimit::Real=NO_TIMELIMIT)
            osize = brfft_output_size(X, d, region)
            Y = flags&ESTIMATE != 0 ? FakeArray($Tr,osize...) : Array{$Tr}(osize...)

            # FFTW currently doesn't support PRESERVE_INPUT for
            # multidimensional out-of-place c2r transforms, so
            # we have to handle 1d and >1d cases separately with a copy.  Ugh.
            if length(region) <= 1
                rFFTWPlan{$Tc,$BACKWARD,false,N}(X, Y, region,
                                                 flags | PRESERVE_INPUT,
                                                 timelimit)
            else
                rFFTWPlan{$Tc,$BACKWARD,false,N}(copy(X), Y, region, flags,
                                                 timelimit)
            end
        end

        plan_rfft(X::StridedArray{$Tr};kws...)=plan_rfft(X,1:ndims(X);kws...)
        plan_brfft(X::StridedArray{$Tr};kws...)=plan_brfft(X,1:ndims(X);kws...)

        function plan_inv{N}(p::rFFTWPlan{$Tr,$FORWARD,false,N})
            X = Array{$Tr}(p.sz)
            Y = p.flags&ESTIMATE != 0 ? FakeArray($Tc,p.osz) : Array{$Tc}(p.osz)
            ScaledPlan(rFFTWPlan{$Tc,$BACKWARD,false,N}(Y, X, p.region,
                                                        length(p.region) <= 1 ?
                                                        p.flags | PRESERVE_INPUT :
                                                        p.flags, NO_TIMELIMIT),
                       normalization(X, p.region))
        end

        function plan_inv{N}(p::rFFTWPlan{$Tc,$BACKWARD,false,N})
            X = Arra{$Tc}(p.sz)
            Y = p.flags&ESTIMATE != 0 ? FakeArray($Tr,p.osz) : Array{$Tr}(p.osz)
            ScaledPlan(rFFTWPlan{$Tr,$FORWARD,false,N}(Y, X, p.region,
                                                       p.flags, NO_TIMELIMIT),
                       normalization(Y, p.region))
        end

        function A_mul_B!(y::StridedArray{$Tc}, p::rFFTWPlan{$Tr,$FORWARD}, x::StridedArray{$Tr})
            assert_applicable(p, x, y)
            unsafe_execute!(p, x, y)
            return y
        end
        function A_mul_B!(y::StridedArray{$Tr}, p::rFFTWPlan{$Tc,$BACKWARD}, x::StridedArray{$Tc})
            assert_applicable(p, x, y)
            unsafe_execute!(p, x, y) # note: may overwrite x as well as y!
            return y
        end

        function *{N}(p::rFFTWPlan{$Tr,$FORWARD,false}, x::StridedArray{$Tr,N})
            assert_applicable(p, x)
            y = Array{$Tc}(p.osz)::Array{$Tc,N}
            unsafe_execute!(p, x, y)
            return y
        end

        function *{N}(p::rFFTWPlan{$Tc,$BACKWARD,false}, x::StridedArray{$Tc,N})
            if p.flags & PRESERVE_INPUT != 0
                assert_applicable(p, x)
                y = Array{$Tr}(p.osz)::Array{$Tr,N}
                unsafe_execute!(p, x, y)
            else # need to make a copy to avoid overwriting x
                xc = copy(x)
                assert_applicable(p, xc)
                y = Array{$Tr}(p.osz)::Array{$Tr,N}
                unsafe_execute!(p, xc, y)
            end
            return y
        end
    end
end

# FFTW r2r transforms (low-level interface)

for f in (:r2r, :r2r!)
    pf = Symbol("plan_", f)
    @eval begin
        $f(x::AbstractArray{<:fftwNumber}, kinds) = $pf(x, kinds) * x
        $f(x::AbstractArray{<:fftwNumber}, kinds, region) = $pf(x, kinds, region) * x
        $pf(x::AbstractArray, kinds; kws...) = $pf(x, kinds, 1:ndims(x); kws...)
        $f(x::AbstractArray{<:Real}, kinds, region=1:ndims(x)) = $f(fftwfloat(x), kinds, region)
        $pf(x::AbstractArray{<:Real}, kinds, region; kws...) = $pf(fftwfloat(x), kinds, region; kws...)
        $f(x::AbstractArray{<:Complex}, kinds, region=1:ndims(x)) = $f(fftwcomplex(x), kinds, region)
        $pf(x::AbstractArray{<:Complex}, kinds, region; kws...) = $pf(fftwcomplex(x), kinds, region; kws...)
    end
end

function plan_r2r{T<:fftwNumber,N}(X::StridedArray{T,N}, kinds, region;
                                   flags::Integer=ESTIMATE,
                                   timelimit::Real=NO_TIMELIMIT)
    r2rFFTWPlan{T,ANY,false,N}(X, fakesimilar(flags, X, T), region, kinds,
                               flags, timelimit)
end

function plan_r2r!{T<:fftwNumber,N}(X::StridedArray{T,N}, kinds, region;
                                    flags::Integer=ESTIMATE,
                                    timelimit::Real=NO_TIMELIMIT)
    r2rFFTWPlan{T,ANY,true,N}(X, X, region, kinds, flags, timelimit)
end

# mapping from r2r kind to the corresponding inverse transform
const inv_kind = Dict{Int,Int}(R2HC => HC2R, HC2R => R2HC, DHT => DHT,
                               REDFT00 => REDFT00,
                               REDFT01 => REDFT10, REDFT10 => REDFT01,
                               REDFT11 => REDFT11,
                               RODFT00 => RODFT00,
                               RODFT01 => RODFT10, RODFT10 => RODFT01,
                               RODFT11 => RODFT11)

# r2r inverses are normalized to 1/N, where N is a "logical" size
# the transform with length n and kind k:
function logical_size(n::Integer, k::Integer)
    k <= DHT && return n
    k == REDFT00 && return 2(n-1)
    k == RODFT00 && return 2(n+1)
    return 2n
end

function plan_inv{T<:fftwNumber,K,inplace,N}(p::r2rFFTWPlan{T,K,inplace,N})
    X = Array{T}(p.sz)
    iK = fix_kinds(p.region, [inv_kind[k] for k in K])
    Y = inplace ? X : fakesimilar(p.flags, X, T)
    ScaledPlan(r2rFFTWPlan{T,ANY,inplace,N}(X, Y, p.region, iK,
                                            p.flags, NO_TIMELIMIT),
               normalization(real(T),
                             map(logical_size, [p.sz...][[p.region...]], iK),
                             1:length(iK)))
end

function A_mul_B!{T}(y::StridedArray{T}, p::r2rFFTWPlan{T}, x::StridedArray{T})
    assert_applicable(p, x, y)
    unsafe_execute!(p, x, y)
    return y
end

function *{T,K,N}(p::r2rFFTWPlan{T,K,false}, x::StridedArray{T,N})
    assert_applicable(p, x)
    y = Array{T}(p.osz)::Array{T,N}
    unsafe_execute!(p, x, y)
    return y
end

function *{T,K}(p::r2rFFTWPlan{T,K,true}, x::StridedArray{T})
    assert_applicable(p, x)
    unsafe_execute!(p, x, x)
    return x
end

include("dct.jl")


text:
using LinearAlgebra

function backtrackLineSearch(xInit, dirΔ, f, dfdx, paramA, paramB, verbose = false)
    # Output an updated x

    if verbose
        println("\nBeginning Line Search")
        println("xInit = $xInit")
        println("dirΔ = $dirΔ")
        println("objective function (current x) = $(f(xInit))")
        println("Parameters: $paramA and $paramB")
        println("Verbose = $verbose \n")
    end

    # First, initializae α = 1. This is the "learning rate"

    α = 1
    if verbose
        print("Left side: ")
        println(f(xInit + α * dirΔ))
        print("Right side: ")
        print(f(xInit))
        print(" + ")
        print(paramA * α * dirΔ'dfdx(xInit))
        print(" = ")
        println(f(xInit) + paramA * α * dirΔ'dfdx(xInit))
    end

    # Check the condition:
    # f(x + alpha * dir) > f(x) + a * grad f of (alpha * delta)
    while f(xInit + α * dirΔ) > (f(xInit) + paramA * α * (dirΔ'dfdx(xInit))[1])
        # If the condition passes, set α to b * α
        if verbose
            print("Left side: ")
            println(f(xInit + α * dirΔ))
            print("Right side: ")
            print(f(xInit))
            print(" + ")
            print(paramA * α * dirΔ'dfdx(xInit))
            print(" = ")
            println(f(xInit) + paramA * α * dirΔ'dfdx(xInit))
        end

        α = paramB * α

        if verbose
            println(α)
        end
    end

    # the updated x is xInit - alpha * gradf(xInit)
    xUpdated = xInit + α * dirΔ
    # Return updated x
    return xUpdated, α
end

runTest = false

if runTest
    # Test Script

    # One Dimensional Function
    xInit1 = 600
    fFun(x) = x^2
    dfdx(x) = 2 * x
    dir = -dfdx(xInit1)
    aTest = 0.3
    bTest = 0.707

    xNew = backtrackLineSearch(xInit1, dir, fFun, dfdx, aTest, bTest, true)
    println("New x = $xNew")
    println("Result should by 0.181")

    # Two Dimensional Function
    xInit2 = [600, 400]
    fFun(x) = x'x
    dfdx(x) = 2 * x
    dir2 = -dfdx(xInit2)
    aTest = 0.3
    bTest = 0.707

    xNew = backtrackLineSearch(xInit2, dir2, fFun, dfdx, aTest, bTest, true)
    println("New x = $xNew")
    println("Result should by 0.181")
end

max_stars_repo_name: danineamati/TrajOptSOCPs


text:
using Test
using Random

include("./fixtures.jl")
include("./testVariable.jl")
include("./testFactor.jl")
include("./testSolve.jl")

apiUrl = get(ENV,"API_URL","https://api.navability.io")
userId = get(ENV,"USER_ID","guest@navability.io")
robotId = get(ENV,"ROBOT_ID","IntegrationRobot")
sessionId = get(ENV,"SESSION_ID","TestSession"*randstring(7))
sessionId1d = get(ENV,"SESSION_ID","TestSession1D"*randstring(7))
sessionId2d = get(ENV,"SESSION_ID","TestSession2D"*randstring(7))

@testset "nva-sdk-integration-testset" begin
    # Creating one client and two contexts
    client, navabilityClient1D = createClients(apiUrl, userId, robotId, sessionId1d)
    client, navabilityClient2D = createClients(apiUrl, userId, robotId, sessionId2d)

    @info "Running nva-sdk-integration-testset..."

    # Note - Tests incrementally build on each other because this is an
    # integration test.
    runVariableTests( client, navabilityClient2D )
    runFactorTests( client, navabilityClient2D )
    runSolveTests( client, navabilityClient2D )

    # test fixtures
    exampleGraph1D( client, navabilityClient1D; doSolve=false )

end


text:
using ComponentArrays
using ForwardDiff
using LinearAlgebra
using StaticArrays
using OffsetArrays
using Test
using Unitful

## Test setup
c = (a=(a=1, b=[1.0, 4.4]), b=[0.4, 2, 1, 45])
nt = (a=100, b=[4, 1.3], c=c)
nt2 = (a=5, b=[(a=(a=20,b=1), b=0), (a=(a=33,b=1), b=0)], c=(a=(a=2, b=[1,2]), b=[1. 2.; 5 6]))

ax = Axis(a=1, b=2:3, c=ViewAxis(4:10, (a=ViewAxis(1:3, (a=1, b=2:3)), b=4:7)))
ax_c = (a=ViewAxis(1:3, (a=1, b=2:3)), b=4:7)

a = Float64[100, 4, 1.3, 1, 1, 4.4, 0.4, 2, 1, 45]
sq_mat = collect(reshape(1:9,3,3))

ca = ComponentArray(nt)
ca_Float32 = ComponentArray{Float32}(nt)
ca_MVector = ComponentArray{MVector{10}}(nt)
ca_SVector = ComponentArray{SVector{10}}(nt)
ca_composed = ComponentArray(a=1, b=ca)

ca2 = ComponentArray(nt2)

cmat = ComponentArray(a .* a', ax, ax)
cmat2 = ca2 .* ca2'

caa = ComponentArray(a=ca, b=sq_mat)

_a, _b, _c = fastindices(:a, :b, :c)

## Tests
@testset "Utilities" begin
    @test ComponentArrays.getval.(fastindices(:a, :b, :c)) == (:a, :b, :c)
    @test fastindices(:a, Val(:b)) == (Val(:a), Val(:b))
    @test fastindices(("a", Val(:b))) == (Val(:a), Val(:b))

    @test collect(ComponentArrays.partition(collect(1:12), 3)) == [[1,2,3], [4,5,6], [7,8,9], [10,11,12]]
    @test size(collect(ComponentArrays.partition(zeros(2,2,2), 1, 2, 2))[2,1,1]) == (1, 2, 2)
end

@testset "Construction" begin
    @test ca == ComponentArray(a=100, b=[4, 1.3], c=(a=(a=1, b=[1.0, 4.4]), b=[0.4, 2, 1, 45]))
    @test ca_Float32 == ComponentArray(Float32.(a), ax)
    @test eltype(ComponentArray{ForwardDiff.Dual}(nt)) == ForwardDiff.Dual
    @test ca_composed.b isa ComponentArray
    @test ca_composed.b == ca
    @test getdata(ca_MVector) isa MArray
    @test typeof(ComponentArray(undef, (ax,))) == typeof(ca)
    @test typeof(ComponentArray(undef, (ax, ax))) == typeof(cmat)
    @test typeof(ComponentArray{Float32}(undef, (ax,))) == typeof(ca_Float32)
    @test typeof(ComponentArray{MVector{10,Float64}}(undef, (ax,))) == typeof(ca_MVector)

    # Entry from Dict
    dict1 = Dict(:a=>rand(5), :b=>rand(5,5))
    dict2 = Dict(:a=>3, :b=>dict1)
    @test ComponentArray(dict1) isa ComponentArray
    @test ComponentArray(dict2).b isa ComponentArray

    @test ca == ComponentVector(a=100, b=[4, 1.3], c=(a=(a=1, b=[1.0, 4.4]), b=[0.4, 2, 1, 45]))
    @test cmat == ComponentMatrix(a .* a', ax, ax)
    @test_throws DimensionMismatch ComponentVector(sq_mat, ax)
    @test_throws DimensionMismatch ComponentMatrix(rand(11,11,11), ax, ax)
    @test_throws ErrorException ComponentArray(v=[(a=1, b=2), (a=3, c=4)])

    # Issue #24
    @test ComponentVector(a=1, b=2f0) == ComponentVector{Float32}(a = 1.0, b = 2.0)
    @test ComponentVector(a=1, b=2+im) == ComponentVector{Complex{Int64}}(a = 1 + 0im, b = 2 + 1im)

    # Issue #23
    sz = size(ca)
    temp = ComponentArray(ca; d=100)
    temp2 = ComponentVector(temp; d=4)
    temp3 = ComponentArray(temp2; e=(a=20, b=[2 4; 1 4]))
    @test sz == size(ca)
    @test temp.d == 100
    @test temp2.d == 4
    @test !haskey(ca, :d)
    @test all(temp3.e.b .== [2 4; 1 4])

    # Issue #18
    temp_miss = ComponentArray(a=missing, b=[2, 1, 4, 5], c=[1, 2, 3])
    @test eltype(temp_miss) == Union{Int64, Missing}
    @test temp_miss.a === missing
    temp_noth = ComponentArray(a=nothing, b=[2, 1, 4, 5], c=[1, 2, 3])
    @test eltype(temp_noth) == Union{Int64, Nothing}
    @test temp_noth.a === nothing

    # Issue #61
    @test ComponentArray(x=1) isa ComponentArray{Int}
end

@testset "Attributes" begin
    @test length(ca) == length(a)
    @test size(ca) == size(a)
    @test size(cmat) == (length(a), length(a))

    @test propertynames(ca) == (:a, :b, :c)
    @test propertynames(ca.c) == (:a, :b)

    @test parent(ca) == a

    @test keys(ca) == (:a, :b, :c)
    @test valkeys(ca) == Val.((:a, :b, :c))
end

@testset "Get" begin
    @test getdata(ca) == a
    @test getdata(cmat) == a .* a'

    @test getaxes(ca) == (ax,)
    @test getaxes(cmat) == (ax, ax)

    @test ca[1] == a[1]
    @test ca[1:5] == a[1:5]
    @test cmat[:,:] == cmat
    @test getaxes(cmat[:a,:]) == getaxes(ca)

    @test ca.a == 100.0
    @test ca.b == Float64[4, 1.3]
    @test ca.c.a.a == 1.0
    @test ca.c.a.b[1] == 1.0
    @test ca.c == ComponentArray(c)
    @test ca2.b[1].a.a == 20.0

    @test ca[:a] == ca["a"] == ca.a
    @test ca[:b] == ca["b"] == ca.b
    @test ca[:c] == ca["c"] == ca.c

    @test cmat[:a, :a] == cmat["a", "a"] == 10000.0
    @test cmat[:a, :b] == cmat["a", "b"] == [400, 130]
    @test all(cmat[:c, :c] .== ComponentArray(a[4:10] .* a[4:10]', Axis(ax_c), Axis(ax_c)))
    @test cmat[:c,:][:a,:][:a,:] == ca
    @test cmat[:a, :c] == cmat[:c, :a]
    @test all(cmat2[:b, :b][1,1] .== ca2.b[1] .* ca2.b[1]')

    @test ca[_a] == ca[:a]
    @test cmat[_c,_b] == cmat[:c,:b]
    @test cmat[_c, :a] == cmat[:c, :a]

    @test ca2.b[2].a.a == 33

    @test collect(caa.b) == sq_mat
    @test size(caa.b) == size(sq_mat)
    @test caa.b[1:2, 3] == sq_mat[1:2, 3]

    @test view(ca, :a) == ca.a
    @test cmat[:c, :a] == view(cmat, :c, :a)

    @test ca[CartesianIndex(1)] == ca[1]
    @test cmat[CartesianIndex(1, 2)] == cmat[1, 2]

    @test getproperty(ca, Val(:a)) == ca.a

    #OffsetArray stuff
    part_ax = PartitionedAxis(2, Axis(a=1, b=2))
    oaca = ComponentArray(OffsetArray(collect(1:5), -1), Axis(a=0, b=ViewAxis(1:4, part_ax)))
    temp_ca = ComponentArray(collect(1:5), Axis(a=1, b=ViewAxis(2:5, part_ax)))
    @test oaca.a == temp_ca.a
    @test oaca.b[1].a == temp_ca.b[1].a
    @test oaca[0] == temp_ca[1]
    @test oaca[4] == temp_ca[5]
    @test axes(oaca) == axes(getdata(oaca))

    # Issue #56
    A = ComponentArray(rand(4, 10), Axis(a=1:2, b=3:4), FlatAxis())
    A_vec = A[:,1]
    A_mat = A[:,1:2]
    @test A_vec isa ComponentVector
    @test A_mat isa ComponentMatrix
    @test getdata(A_vec) isa Vector
    @test getdata(A_mat) isa Matrix

    @test Base.to_indices(ca, (:a, :b)) == (:a, :b)
    @test Base.to_indices(ca, (1, 2)) == (1, 2)
    @test Base.to_index(ca, :a) == :a
end

@testset "Set" begin
    temp = deepcopy(ca2)
    tempmat = deepcopy(cmat2)

    temp.c.a .= 1000

    tempmat[:b,:b][1,1][:a,:a][:a,:a] = 100000
    tempmat[:b,:a][2].b = 1000

    @test temp.c.a.a == 1000

    @test tempmat["b","b"][1,1]["a",:a][:a,:a] == 100000
    @test tempmat[:b,:a][2].b == 1000

    temp2 = deepcopy(ca)
    temp3 = deepcopy(ca_MVector)
    @test (temp2 .= ca .* 1) isa ComponentArray
    @test (temp2 .= temp2 .* a .+ 1) isa typeof(temp2)
    @test (temp2 .= ca .* ca_SVector) isa typeof(temp2)
    @test (temp3 .= ca .* ca_SVector) isa typeof(temp3)

    temp2.b = ca.b .+ 1
    @test temp2.b == ca.b .+ 1

    setproperty!(temp2, :a, 20)
    @test temp2.a == 20

    setproperty!(temp2, Val(:b), zeros(2))
    @test temp2.b == zeros(2)

    tempmat .= 0
    @test tempmat[:b,:a][2].b == 0

    temp = deepcopy(cmat)
    @test all((temp[:c,:c][:a,:a] .= 0) .== 0)

    A = ComponentArray(zeros(Int,4,4), Axis(x=1:4), Axis(x=1:4))
    A[1,:] .= 1
    @test A[1,:] == ones(Int,4)
end

@testset "Similar" begin
    @test typeof(similar(ca)) == typeof(ca)
    @test typeof(similar(ca2)) == typeof(ca2)
    @test typeof(similar(ca, Float32)) == typeof(ca_Float32)
    @test eltype(similar(ca, ForwardDiff.Dual)) == ForwardDiff.Dual
end

@testset "Copy" begin
    @test copy(ca) == ca
    @test deepcopy(ca) == ca
end

@testset "Convert" begin
    @test NamedTuple(ca) == nt
    @test NamedTuple(ca.c) == c
    @test convert(typeof(ca), a) == ca
    @test convert(typeof(ca), ca) == ca
    @test convert(typeof(cmat), cmat) == cmat

    @test convert(Array, ca) == getdata(ca)
end

@testset "Broadcasting" begin
    temp = deepcopy(ca)
    @test eltype(Float32.(ca)) == Float32
    @test ca .* ca' == cmat
    @test 1 .* (ca .+ ca) == ComponentArray(a .+ a)
    @test typeof(ca .+ cmat) == typeof(cmat)
    @test getaxes(false .* ca .* ca') == (ax, ax)
    @test getaxes(false .* ca' .* ca) == (ax, ax)
    @test (vec(temp) .= vec(ca_Float32)) isa ComponentArray
    @test getdata(ca_MVector .* ca_MVector) isa MArray

    @test typeof(ca .* ca_MVector) == typeof(ca)
    @test typeof(ca_SVector .* ca) == typeof(ca)
    @test typeof(ca_SVector .* ca_SVector) == typeof(ca_SVector)
    @test typeof(ca_SVector .* ca_MVector) == typeof(ca_SVector)
    @test typeof(ca_SVector' .+ ca) == typeof(cmat)
    @test getdata(ca_SVector' .+ ca_SVector') isa StaticArrays.StaticArray
    @test getdata(ca_SVector .* ca_SVector') isa StaticArrays.StaticArray
    @test ca_SVector .* ca .+ a .- 1 isa ComponentArray

    # Issue #31 (with Complex as a stand-in for Dual)
    @test reshape(Complex.(ca, Float32.(a)), size(ca)) isa ComponentArray{Complex{Float64}}

    # Issue #34 : Different Axis types
    x1 = ComponentArray(a=[1.1,2.1], b=[0.1])
    x2 = ComponentArray(a=[1.1,2.1], b=0.1)
    x3 = ComponentArray(a=[1.1,2.1], c=[0.1])
    xmat = x1 .* x2'
    x1mat = x1 .* x1'
    @test x1 + x2 isa Vector
    @test x1 + x3 isa Vector
    @test x2 + x3 isa Vector
    @test x1 .* x2 isa Vector
    @test xmat + x1mat isa ComponentArray
    @test xmat isa ComponentArray
    @test getaxes(xmat) == (getaxes(x1)[1], getaxes(x2)[1])
    @test getaxes(x1mat + xmat) == (getaxes(x1)[1], FlatAxis())
    @test getaxes(x1mat + xmat') == (FlatAxis(), getaxes(x1)[1])

    @test map(sqrt, ca) isa ComponentArray
    @test map(+, ca, sqrt.(ca)) isa ComponentArray
    @test map(+, sqrt.(ca), Float32.(ca), ca) isa ComponentArray
    @test map(+, ca, getdata(ca)) isa Array
    @test map(+, ca, ComponentArray(v=getdata(ca))) isa Array

    x1 .+= x2
    @test getdata(x1) == 2getdata(x2)

    # Issue #60
    x4 = ComponentArray(rand(3,3), Axis(x=1, y=2, z=3), Axis(x=1, y=2, z=3))
    @test x4 + I(3) isa ComponentMatrix
end

@testset "Math" begin
    a_t = collect(a')

    @test ca * ca' == collect(cmat)
    @test ca * ca' == a * a'
    @test ca' * ca == a' * a
    @test cmat * ca == cmat * a
    @test cmat' * ca isa AbstractArray
    @test a' * ca isa Number
    @test cmat'' == cmat
    @test ca'' == ca
    @test ca.c' * cmat[:c,:c] * ca.c isa Number
    @test ca * 1 isa ComponentVector
    @test size(ca' * 1) == size(ca')
    @test a' * ca isa Number
    @test a_t * ca isa AbstractArray
    @test a' * cmat isa Adjoint
    @test a_t * cmat isa AbstractArray
    @test cmat * ca isa AbstractVector
    @test ca + ca + ca isa typeof(ca)
    @test a + ca + ca isa typeof(ca)
    @test a*ca' isa AbstractMatrix

    @test ca * transpose(ca) == collect(cmat)
    @test ca * transpose(ca) == a * transpose(a)
    @test transpose(ca) * ca == transpose(a) * a
    @test cmat * ca == cmat * a
    @test transpose(transpose(cmat)) == cmat
    @test transpose(transpose(ca)) == ca
    @test transpose(ca.c) * cmat[:c,:c] * ca.c isa Number
    @test size(transpose(ca) * 1) == size(transpose(ca))
    @test transpose(a)*ca isa Number
    @test transpose(a)*cmat isa Transpose
    @test a*transpose(ca) isa AbstractMatrix

    temp = deepcopy(ca)
    temp .= (cmat+I) \ ca
    @test temp isa ComponentArray
    @test (ca' / (cmat'+I))' == (cmat+I) \ ca
    @test cmat * ((cmat+I) \ ca) isa AbstractArray
    @test inv(cmat+I) isa AbstractArray

    tempmat = deepcopy(cmat)

    @test ldiv!(temp, lu(cmat+I), ca) isa ComponentVector
    @test ldiv!(getdata(temp), lu(cmat+I), ca) isa AbstractVector
    @test ldiv!(tempmat, lu(cmat+I), cmat) isa ComponentMatrix
    @test ldiv!(getdata(tempmat), lu(cmat+I), cmat) isa AbstractMatrix

    vca2 = vcat(ca2', ca2')
    hca2 = hcat(ca2, ca2)
    temp = ComponentVector(q=100, r=rand(3,3,3))
    vtempca = [temp; ca]
    @test all(vca2[1,:] .== ca2)
    @test all(hca2[:,1] .== ca2)
    @test all(vca2' .== hca2)
    @test hca2[:a,:] == vca2[:,:a]
    @test vtempca isa ComponentVector
    @test vtempca.r == temp.r
    @test vtempca.c == ca.c
    @test length(vtempca) == length(temp) + length(ca)
    @test [ca; ca; ca] isa Vector
    @test vcat(ca, 100) isa Vector

    # Issue #33
    smat = @SMatrix [1 2; 3 4]
    b = ComponentArray(a = 1, b = 2)
    @test smat*b isa StaticArray
end

@testset "Plot Utilities" begin
    lab = labels(ca2)
    @test lab == [
        "a",
        "b[1].a.a",
        "b[1].a.b",
        "b[1].b",
        "b[2].a.a",
        "b[2].a.b",
        "b[2].b",
        "c.a.a",
        "c.a.b[1]",
        "c.a.b[2]",
        "c.b[1,1]",
        "c.b[2,1]",
        "c.b[1,2]",
        "c.b[2,2]",
    ]
    @test label2index(ca2, "c.b") == collect(11:14)
end

@testset "Uncategorized Issues" begin
    # Issue #25
    @test sum(abs2, cmat) == sum(abs2, getdata(cmat))

    # Issue #40
    r0 = [1131.340, -2282.343, 6672.423]u"km"
    v0 = [-5.64305, 4.30333, 2.42879]u"km/s"
    rv0 = ComponentArray(r=r0, v=v0)
    zrv0 = zero(rv0)
    @test all(zero(cmat) * ca .== zero(ca))
    @test typeof(zrv0) === typeof(rv0)
    @test typeof(zrv0.r[1]) == typeof(rv0[1])
end


text:
module CMSSPDomains

# Stdlib Requirements
using Logging
using Printf
using Random
using LinearAlgebra
using Distributions

# Non-stdlib essentials
using StaticArrays

# POMDP Stuff
using POMDPs
using POMDPModelTools
using POMDPModels
using POMDPPolicies
using POMDPSimulators
using Graphs

# For reading params
using TOML

# For continuum world
using ContinuumWorld

# CMSSPs submodule
using CMSSPs.CMSSPModel
using CMSSPs.HHPC

# Continuum exports
export
    ContinuumDummyBookkeeping,
    ContinuumDummyMetadata,
    GridsContinuumParams,
    GridsContinuumCMSSPType,
    GridsContinuumMDPType,
    GridsContinuumSolverType,
    GridsContinuumContextSet,
    continuum_parse_params,
    create_continuum_cmssp,
    set_start_context_set!,
    generate_start_state,
    sample_continuum_state,
    CONTINUUM_MODES,
    CONTINUUM_GOAL_MODE

include("grids_continuum/grids_continuum.jl")

# DREAMR exports
export
    Point,
    Parameters,
    parse_params,
    UAVDynamicsModel,
    UAVState,
    UAVAction,
    MultiRotorUAVState,
    MultiRotorUAVAction,
    MultiRotorUAVDynamicsModel,
    generate_start_state,
    get_state_at_rest,
    HopOnOffSingleCarSimulator,
    reset_sim,
    step_sim,
    DREAMR_MODETYPE,
    DREAMRModeAction,
    DREAMRContextSet,
    FLIGHT, RIDE,
    HOP_ACTION,
    HOPON, HOPOFF, STAY,
    DREAMRStateType,
    DREAMRActionType,
    DREAMRBookkeeping,
    DREAMRVertexMetadata,
    DREAMRSolverType,
    get_dreamr_actions,
    get_dreamr_mdp,
    set_dreamr_goal!,
    create_dreamr_cmssp,
    get_dreamr_episode_context,
    get_cf_mdp,
    get_uf_mdp,
    get_ride_mdp,
    get_arrival_time_distribution,
    DREAMRDeterministicPolicy,
    DREAMRMCTSType,
    DREAMRMCTSState,
    estimate_value_dreamr,
    init_q_dreamr

include("dreamr/dreamr_types.jl")
include("dreamr/dreamr_params.jl")
include("dreamr/dreamr_dynamics.jl")
include("dreamr/dreamr_cf_simulator.jl")
include("dreamr/dreamr_cmssp.jl")

end # End of submodule


text:
emptymoddict(::Type{T}) where {T <: Modifier} = Dict{Symbol, T}()
emptymoddict() = emptymoddict(Modifier)

ModifiedEdge(x) = ModifiedEdge(x, emptymoddict(EdgeModifier))
ModifiedNode(x) = ModifiedNode(x, emptymoddict(NodeModifier))
ModifyingNode(x) = ModifyingNode(x, emptymoddict(EdgeModifier))
Arrow(x) = Arrow(x, nothing, nothing)

import Base.convert
convert(::Type{T}, x) where {T <: AbstractNode} = T(x)
convert(::Type{T}, x::Symbol) where {T <: AbstractNode} = SimpleNode(x)
convert(::Type{ <: ModifiedEdge}, x::Edge) = ModifiedEdge(x)
convert(::Type{ <: Arrow}, x::Edge) = Arrow([x])
convert(::Type{ <: ModifiedNode}, x::Node) = ModifiedNode(x)
convert(::Type{ <: ModifyingNode}, x::Node) = ModifyingNode(x)

# conversion of Nodes does not change anything
convert(::Type{T}, x::T) where {T <: AbstractNode} = x

import Base.promote_rule
# Symbols get promoted
promote_rule(::Type{T}, ::Type{Symbol}) where {T <: AbstractNode} = AbstractNode
promote_rule(::Type{T}, ::Type{Symbol}) where {T <: Node} = Node
promote_rule(::Type{ <: Edge}, ::Type{T}) where {T <: MetaEdge} = T
promote_rule(::Type{ <: Node}, ::Type{T}) where {T <: MetaNode} = T
promote_rule(::Type{ <: Edge}, ::Type{T}) where {T <: ModifiedEdge} = ModifiedEdge


text:
module FLoopsTests

using Test

for file in
    sort([file for file in readdir(@__DIR__) if match(r"^test_.*\.jl$", file) !== nothing])
    include(file)
end

end  # module