text:
module JuliaPlot
using Plots, Latexify, LaTeXStrings, Printf, Polynomials, Measures

export juliaPlot,mandelbrotPlot,plotBoth,moreLikelyToBeInterestingPlot,julianimation,juliaMatrix,mandelbrotMatrix,multiMonitorJuliaPlot

function juliaPlot(
        ;
        c::Complex = complex(0.6,0.7), # function offset
        res::Integer = 200, # x resolution
        R::Float64 = 2.0, # Escape radius
        aleph::Float64 = 0.8, # Proportion of escape radius to draw
        I::Integer = 100, # maximum iteration number
        f::AbstractVector = [0.0,0.0,1.0], # function to iterate
        size::Tuple{Integer,Integer} = (1920,1080),
        filename::Union{String,Nothing} = nothing,
        color::Symbol = :hawaii,
        labelplatecolor::Union{Symbol,Nothing} = :white,
        pl = heatmap(
                     aspect_ratio=:equal,
                     axis=false,
                     legend=false,
                     ticks=[],
                     bg=:black,
                    )
    )
    g = Polynomial{Complex{Float64}}(f)+c
    x = range(-aleph*R,aleph*R,length=res)
    y = range(-aleph*R/size[1]*size[2],aleph*R/size[1]*size[2],
        length=round(Int,res/size[1]*size[2]))
    values = juliaValue.(g,complex.(x',y),R,I)
    heatmap!(pl,
        x,y,values,
        color=color,
       )
    #= Annotation (disable for now)
    io = IOBuffer()
    print(io,"\$")
    printpoly(io,g,MIME"text/latex"(),descending_powers=true)
    print(io,"\$")
    annotation = LaTeXString(String(take!(io)))
    annotation_size = Plots.text_size(annotation,14)
    annotcoords=(0.9-0.011*annotation_size[1]/annotation_size[2],1.0,-0.95,-0.85) # x1, x2, y1, y2
    if !isnothing(labelplatecolor)
    plot!(pl,
    Shape(
    [annotcoords[[1,2,2,1]]...]*aleph*R, # x1, x2, x2, x1
    [annotcoords[[3,3,4,4]]...]*aleph*R/size[1]*size[2] # y1, y1, y2, y2
    ),
    opacity=0.1,color=labelplatecolor,linecolor=nothing,
    padding=0mm,margins=0mm,
    axis=false,
    bg=:black,
    )
    end
    annotate!(pl,
    0.9*aleph*R,
    -0.9*aleph*R/size[1]*size[2],
    text(annotation,14,:right),
    size=size,
    padding=0mm,margins=0mm,
    axis=false,
    )
    =#

    if !isnothing(filename)
        #closeall() # workaround for darkening issue
        savefig(pl,filename)
    end
    return pl
end # function juliaPlot

function mandelbrotPlot(
        ;
        res::Integer = 200,
        R::Float64 = 2.0,
        aleph::Float64 = 0.8,
        I::Integer = 100,
        f::AbstractVector = [0.0,0.0,1.0],
        size::Tuple{Integer,Integer} = (1920,1080),
        filename::Union{String,Nothing} = nothing,
        color::Symbol = :hawaii,
        pl = heatmap(
                     aspect_ratio=:equal,
                     axis=false,
                     legend=false,
                     ticks=[],
                     bg=:black,
                    ),
    )::Complex{Float64}
    g = Polynomial{Complex{Float64}}(f)
    x = range(-aleph*R,aleph*R,length=res)
    y = range(-aleph*R/size[1]*size[2],aleph*R/size[1]*size[2],
        length=round(Int,res/size[1]*size[2]))
    values = juliaValue.(g.+complex.(x',y),complex(0.0,0.0),R,I)
    (y_i,x_i) = div.(size,2)
    try
        (y_i,x_i) = Tuple(rand(findall( (values .> 0.5*I) .& (values.< 0.9*I))))
    catch
    end
    c = complex(x[x_i],y[y_i])
    if !isnothing(pl)
        heatmap!(pl,
                 x,y,values,
                 color=color,
                )
        plot!(pl,
              [x[x_i]],[y[y_i]],
              seriestype=:scatter,
              marker = (:circle, size[1]/250, :white, stroke(0.2, 0.2, :black)),
             )
        if !isnothing(filename)
            savefig(pl,filename)
        end
    end
    return c
end # mandelbrotPlot

function plotBoth(
        ;
        f=[0.0,0.0,1.0],
        res=2000,
        sizes=(2000,1000),
        filename=nothing,
        color=:hawaii,
    )
    sizes = eltype(sizes)<:Integer ? (sizes,sizes) : sizes
    res = typeof(res)<:Tuple ? res : (res,res)
    filenames = eltype(filename)<:String ? filename : (nothing,nothing)
    pl1 = heatmap(
        aspect_ratio=:equal,
        size=sizes[1],
        axis=false,
        legend=false,
        ticks=[],
        bg=:black,
    )
    pl2 = heatmap(
        aspect_ratio=:equal,
        size=sizes[2],
        axis=false,
        legend=false,
        ticks=[],
        bg=:black,
    )
    c = mandelbrotPlot(
        ;
        f=f,
        res=res[1],
        size=sizes[1],
        pl=pl1,
        filename=filenames[1],
        color=color,
    )
    juliaPlot(
        ;
        c=c,
        f=f,
        res=res[2],
        size=sizes[2],
        pl=pl2,
        filename=filenames[2],
        color=color,
    )
    l = @layout([
                 a;
                 b
                ])
    pl = plot(pl1,pl2,
        layout=( # TODO I think you have to specify size here...
                l
               ),
        size=(maximum(getindex.(sizes,1)),sum(getindex.(sizes,2))))
    if eltype(filename)<:String
        savefig(pl,filename)
    end
    return pl
end # plotBoth

function moreLikelyToBeInterestingPlot(
        ;
        f=[0.0,0.0,1.0],
        res=(300,2000),
        sizes=((300,200),(2000,1000)),
        filename=nothing,
        color=:hawaii,
        labelplatecolor=:white,
    )
    sizes = eltype(sizes)<:Integer ? (sizes,sizes) : sizes
    res = typeof(res)<:Tuple ? res : (res,res)
    c = mandelbrotPlot(
        res=res[1],
        f=f,
        size=sizes[1],
        color=color,
        pl=nothing,
    )
    juliaPlot(
        c=c, # function offset
        res=res[2], # x resolution
        f=f, # function to iterate
        size=sizes[2],
        filename=filename,
        color=color,
        labelplatecolor=labelplatecolor,
    )
end

function juliaValue(
        f::Polynomial, # Function to evaluate
        z_0::Complex, # Starting point
        R::Real, # Escape radius
        I::Integer=100, # Max i
    )::Int64
    i = 0
    z = z_0
    R = R^2
    while( abs2(z) < R && i<I )
        z = f(z)
        i+=1
    end
    return i
end # function juliaValue

function juliaValues(
        f::Union{Function,Polynomial}, # Function to evaluate
        z_0::Complex, # Starting point
        R::Real, # Escape radius
        I::Integer=100, # Max i
    )::Vector{Complex{Float64}}
    R = R^2
    return collect(Complex{Float64},takewhile(x->abs2(x)<R,IterTools.take(iterated(f,z_0),I)))
end # function juliaValues

function julianimation(
        ;
        f=[0.0,0.0,1.0],
        res=500,
        N_t=10,
        filename=nothing,
        color=:rainbow,
        R::Float64 = 2.0, # Escape radius
        aleph::Float64 = 0.8, # Proportion of escape radius to draw
        I::Integer = 100, # maximum iteration number
        size::Tuple{Integer,Integer} = (1920,1080),
    )

    x = range(-aleph*R,aleph*R,length=res)
    y = range(-aleph*R/size[1]*size[2],aleph*R/size[1]*size[2],length=round(Int,res/size[1]*size[2]))
    mandelbrotvalues=juliaValue.(Polynomial{Complex{Float64}}(f).+complex.(x',y),complex(0.0,0.0),R,I)

    # Choose one coordinate inside and one outside the set
    extr = extrema(mandelbrotvalues)
    lims = [0.9 0.1;0.1 0.9]*[extr...]
    i_small = Tuple(rand(findall( (mandelbrotvalues .< lims[1]) )))
    c_small = Complex(x[i_small[2]],y[i_small[1]])
    i_large = Tuple(rand(findall( (mandelbrotvalues .> lims[2]) )))
    c_large = Complex(x[i_large[2]],y[i_large[1]])
    print("$c_small\t$c_large\t$extr\n")

    @animate for c in range(c_small,c_large,length=N_t)
        print("$c\n")
        juliavalues = juliaValue.(Polynomial{Complex{Float64}}(f)+c,complex.(x',y),R,I)
        pl= heatmap(x,y,juliavalues,ticks=[],colorbar=false)
        heatmap!(pl,x,y,mandelbrotvalues,inset=(1,bbox(0.05,0.05,0.5,0.5*size[2]/size[1],:bottom,:right)),ticks=[],colorbar=false,subplot=2)
        plot!(pl,real.([c_small,c_large]),imag.([c_small,c_large]),subplot=2)
        scatter!(pl,[real(c)],[imag(c)],subplot=2)
    end
end

function mandelbrotMatrix(;
        res::Integer = 200,
        R::Float64 = 2.0,
        aleph::Float64 = 0.8,
        I::Integer = 100,
        f::AbstractVector = [0.0,0.0,1.0],
        size::Tuple{Integer,Integer} = (1920,1080),
    )
    g = Polynomial{Complex{Float64}}(f)
    x = range(-aleph*R,aleph*R,length=res)
    y = range(-aleph*R/size[1]*size[2],aleph*R/size[1]*size[2],
        length=round(Int,res/size[1]*size[2]))
    values = juliaValue.(g.+complex.(x',y),complex(0.0,0.0),R,I)
end # mandelbrotMatrix

function juliaMatrix(;
        c::Complex = complex(0.6,0.7), # function offset
        res::Integer = 200, # x resolution
        R::Float64 = 2.0, # Escape radius
        aleph::Float64 = 0.8, # Proportion of escape radius to draw
        I::Integer = 100, # maximum iteration number
        f::AbstractVector = [0.0,0.0,1.0], # function to iterate
        size::Tuple{Integer,Integer} = (1920,1080),
    )
    g = Polynomial{Complex{Float64}}(f)+c
    x = range(-aleph*R,aleph*R,length=res)
    y = range(-aleph*R/size[1]*size[2],aleph*R/size[1]*size[2],
        length=round(Int,res/size[1]*size[2]))
    values = juliaValue.(g,complex.(x',y),R,I)
end # juliaMatrix

function multiMonitorJuliaPlot(;
        pos = ((0,0,1920,1080),(1920,0,2560,1440)), # tuple of x, y, w, h tuples
        filename = "~/Pictures/Julia/background.png",
        f = vcat(0,rand(-1:0.05:1,rand(2:10))),
        color = :copper,
    )
    if length(pos) == 1
        p = first(pos)
        pos = (p,(p[1]+p[3]÷20, p[2]+p[4]-p[4]÷20-p[4]÷5, p[3]÷5, p[4]÷5))
    end
    sz = (
          maximum(p[1] + p[3] for p in pos) - minimum(p[1] for p in pos),
          maximum(p[2] + p[4] for p in pos) - minimum(p[2] for p in pos)
    );
    b = Tuple( # collection of inset boxes
              bbox(p[1]/sz[1], (1-(p[2]+p[4])/sz[2]), p[3]/sz[1], p[4]/sz[2], :bottom, :left) for p in pos
             );
    p = pos[end]
    insetpos = ((p[1]+p[3]/20)/sz[1],1-(p[2]+0.9*p[4])/sz[2],(p[3]/5)/sz[1],(p[4]/40)/sz[2])
    gr(leg=false,framestyle=:none,ticks=nothing,margin=0mm,bg=:black);
    pl = plot(size=sz);
    for k = 1:length(pos)
        plot!(pl,[missing],inset=b[k],subplot=k+1)
    end
    plot!(pl,[missing],inset=bbox(insetpos..., :bottom,:left),subplot=length(pos)+2)

    c = mandelbrotPlot(
        res=length(pos)>1 ? pos[2][3] : 200,
        f=f,
        size=length(pos)>1 ? pos[2][3:4] : (200,200),
        color=color,
        pl=length(pos)>1 ? pl.subplots[3] : nothing,
    )
    juliaPlot(
        c=c, # function offset
        res=pos[1][3], # x resolution
        f=f, # function to iterate
        size=pos[1][3:4],
        filename=nothing,
        color=color,
        labelplatecolor=:white,
        pl=pl.subplots[2],
    )
    heatmap!(pl.subplots[length(pos)+2],f',color=:seaborn_icefire_gradient,clim=(-1,1))

    savefig(pl,"~/Pictures/Julia/background.png")

end # mulitMonitorJuliaPlot

end # module


text:
"""
Yagi-like antenna fitting inside a Bott's dot
"""
module YagiAnt_GP

export yagiant_gp

using ExprSearch, YagiAntProblem, DerivTreeVis
using ExprSearch.GP
using NECPP
using RLESUtils, LogSystems, Loggers

const DIR = dirname(@__FILE__)
const RESULTDIR = joinpath(DIR, "..", "..", "..", "results")

"""
Example usage:
yagiant_gp()
"""
function yagiant_gp(; outdir::AbstractString=joinpath(RESULTDIR, "YagiAnt_GP"),
                     seed=1,
                     logfileroot::AbstractString="yagiant_gp_log",

                     pop_size::Int64=10000,
                     maxdepth::Int64=10,
                     iterations::Int64=10,
                     tournament_size::Int64=500,
                     top_keep::Float64=0.01,
                     crossover_frac::Float64=0.4,
                     mutate_frac::Float64=0.2,
                     rand_frac::Float64=0.2,
                     default_code::Any=0.0,

                     departure_angle::Float64=10.0,
                     vis::Bool=true)

    srand(seed)
    mkpath(outdir)

    logsys = GP.logsystem()
    send_to!(STDOUT, logsys, ["verbose1", "current_best_print", "result"])
    logs = TaggedDFLogger()
    send_to!(logs, logsys, ["code", "computeinfo", "current_best", "elapsed_cpu_s", "fitness",
        "fitness5", "parameters", "result"])

    problem = YagiAnt(departure_angle)

    gp_params = GPESParams(pop_size, maxdepth, iterations, tournament_size, top_keep,
        crossover_frac, mutate_frac, rand_frac, default_code, logsys)

    result = exprsearch(gp_params, problem)

    outfile = joinpath(outdir, "$(logfileroot).txt")
    save_log(outfile, logs)

    if vis
        #visualize the deriv tree
        derivtreevis(get_derivtree(result), joinpath(outdir, "$(logfileroot)_derivtreevis"))

        #output the nec file
        f = open("result.nec", "w")
        (gain, swr) = nec_run(problem, result.expr, f) do nec
            gain = nec_gain(nec, 0, 0, 0)
            imp_re = nec_impedance_real(nec, 0)
            imp_im = nec_impedance_imag(nec, 0)
            Z = Complex(imp_re, imp_im)
            Z0 = Complex(50.0, 0.0)
            swr = vswr(Z, Z0)
            (gain, swr)
        end
        close(f)
        @show (gain, swr)
    end
    @show result.expr

    return result
end

end #module

ext: jl


text:
# it seems that currently, map operation over Nullable array is not optimzed in julia 0.4.1.

import Base: .+, .-, .*, *, ./, /, .\, .//, .==, .<, .!=, .<=, .%, .<<, .>>, .^, +, -, ~, &, |, $, ==, !=, .>=, .>
import Base.return_types
import DataFrames: DataFrame

"""

`wrap_array(arr)`

Wrap an array by `AbstractArrayWrapper` if it is not `DictArray` or `labeledArray`, and not already `AbstractArrayWrapper`.

"""
wrap_array(arr::AbstractArrayWrapper) = arr
wrap_array(arr::LabeledArray) = arr
wrap_array(arr::DictArray) = arr
wrap_array(arr::AbstractArray) = AbstractArrayWrapper(arr)
wrap_array(arr::DataFrame) = arr

Base.setindex!{T,N}(arr::AbstractArrayWrapper{T,N}, v::T, arg::Int) = setindex!(arr.a, v, arg)
Base.setindex!{T,N}(arr::AbstractArrayWrapper{T,N}, v::T, args::Int...) = setindex!(arr.a, v, args...)
Base.setindex!{T,N}(arr::AbstractArrayWrapper{Nullable{T},N}, v::T, args::Int...) = setindex!(arr.a, v, args...)
Base.eltype{T,N,A}(::Type{AbstractArrayWrapper{T,N,A}}) = T
Base.IndexStyle{T,N,A}(::Type{AbstractArrayWrapper{T,N,A}}) = IndexStyle(A)
Base.view(arr::AbstractArrayWrapper, args::Union{Colon,Int,AbstractVector}...) = AbstractArrayWrapper(view(arr.a, args...))
Base.view(arr::AbstractArrayWrapper, args::Tuple{Vararg{Union{Colon,Int,AbstractVector}}})= AbstractArrayWrapper(view(arr.a, args))
Base.repmat{T}(arr::AbstractArrayWrapper{T,1}, n::Int) = AbstractArrayWrapper(repmat(arr.a, n))
Base.repmat{T}(arr::AbstractArrayWrapper{T,2}, n::Int) = AbstractArrayWrapper(repmat(arr.a, n))
Base.repmat{T}(arr::AbstractArrayWrapper{T,1}, m::Int, n::Int) = AbstractArrayWrapper(repmat(arr.a, m, n))
Base.repmat{T}(arr::AbstractArrayWrapper{T,2}, m::Int, n::Int) = AbstractArrayWrapper(repmat(arr.a, m, n))
@delegate(AbstractArrayWrapper.a, Base.start, Base.next, Base.done, Base.size,
                           Base.ndims, Base.length, Base.setindex!, Base.find, Base.fill!)
@delegate_and_lift(AbstractArrayWrapper.a, Base.transpose, Base.permutedims,
                                   Base.sort, Base.sort!, Base.sortperm, Base.reverse, Base.view)
Base.sort(arr::AbstractArrayWrapper, args::Integer) = AbstractArrayWrapper(sort(arr.a, args))
Base.sort!(arr::AbstractArrayWrapper, args::Integer) = AbstractArrayWrapper(sort!(arr.a, args))
Base.reshape(arr::AbstractArrayWrapper, args::Tuple{Vararg{Int}}) = AbstractArrayWrapper(reshape(arr.a, args))
Base.reshape(arr::AbstractArrayWrapper, args::Int...) = AbstractArrayWrapper(reshape(arr.a, args...))
Base.similar{T,N}(arr::AbstractArrayWrapper, ::Type{T}, dims::NTuple{N,Int}) = AbstractArrayWrapper(similar(arr.a, T, dims))
Base.similar{T}(arr::AbstractArrayWrapper, ::Type{T}, dims::Int...) = AbstractArrayWrapper(similar(arr.a, T, dims...))
Base.similar{T<:AbstractFloat,U<:AbstractFloat,N,A,M}(arr::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}},
                                                    ::Type{Nullable{U}},
                                                    dims::NTuple{M,Int}) =
  AbstractArrayWrapper(FloatNAArray(similar(arr.a.data, U, dims)))
Base.similar{T<:AbstractFloat,U<:AbstractFloat,N,A}(arr::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}},
                                                    ::Type{Nullable{U}},
                                                    dims::Int...) =
  AbstractArrayWrapper(FloatNAArray(similar(arr.a.data, U, dims...)))
Base.similar{U<:AbstractFloat,M}(arr::AbstractArrayWrapper,
                                 ::Type{Nullable{U}},
                                 dims::NTuple{M,Int}) =
  AbstractArrayWrapper(FloatNAArray(similar(arr.a, U, dims)))
Base.similar{U<:AbstractFloat}(arr::AbstractArrayWrapper,
                                 ::Type{Nullable{U}},
                                 dims::Int...) =
  AbstractArrayWrapper(FloatNAArray(similar(arr.a, U, dims...)))
Base.repeat(arr::AbstractArrayWrapper; kwargs...) = AbstractArrayWrapper(repeat(arr.a; kwargs...))
Base.sort(arr::AbstractArrayWrapper; kwargs...) = AbstractArrayWrapper(sort(arr.a; kwargs...))
Base.sort!(arr::AbstractArrayWrapper; kwargs...) = AbstractArrayWrapper(sort!(arr.a; kwargs...))
Base.copy(arr::AbstractArrayWrapper) = AbstractArrayWrapper(copy(arr.a))
Base.copy!(tgt::AbstractArrayWrapper, src::AbstractArrayWrapper) = copy!(tgt.a, src.a)
Base.copy!(tgt::AbstractArrayWrapper, src::AbstractArray) = copy!(tgt.a, src)
Base.cat(dim::Int, arr::AbstractArrayWrapper, arrs::AbstractArrayWrapper...) = AbstractArrayWrapper(cat(dim, arr.a, map(x->x.a, arrs)...))
Base.vcat(arrs::AbstractArrayWrapper...) = cat(1, arrs...)
Base.hcat(arrs::AbstractArrayWrapper...) = cat(2, arrs...)
Base.getindex{T,N}(arr::AbstractArrayWrapper{T,N}, arg::Int) = getindex(arr.a, arg)
Base.getindex{T,N}(arr::AbstractArrayWrapper{T,N}, args::Int...) = getindex(arr.a, args...)
Base.getindex{T,N}(arr::AbstractArrayWrapper{T,N}, indices::CartesianIndex) = getindex(arr.a, indices)
Base.getindex(arr::AbstractArrayWrapper, args...) = begin
  res = getindex(arr.a, args...)
  #if is_scalar_indexing(args)
  #  res
  #else
  AbstractArrayWrapper(res)
  #end
end
getindexvalue(arr::AbstractArrayWrapper, args...) = getindexvalue(arr.a, args...)

# necessary to avoid some 'no-op transpose fallback' warning.
Base.transpose(x::Nullable) = x

Base.map(f::Function, arr::AbstractArrayWrapper) = AbstractArrayWrapper(map(f, arr.a))
Base.map(f::Function, arr::AbstractArrayWrapper, arrs::AbstractArrayWrapper...) = AbstractArrayWrapper(map(f, arr.a, map(x->x.a, arrs)...))
Base.push!(arr::AbstractArrayWrapper, elems...) = push!(arr.a, elems...)

absarray_unary_wrapper(op) = begin
  if startswith(string(op[1]), ".")
    typeofop1 = :(typeof($(Symbol(string(op[1])[2:end]))))
    quote
      Base.broadcast(::$typeofop1, arr::DictArray) = mapvalues(x->$(op[1])(x), arr)
      Base.broadcast(::$typeofop1, arr::LabeledArray) = LabeledArray($(op[1])(peel(arr)), pickaxis(arr))
      Base.broadcast(::$typeofop1, x::AbstractArrayWrapper) = begin
        result = similar(x)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x)
        result
      end
      $(Symbol(mangle_dot(op[1]),naop_suffix))(result, x::AbstractArrayWrapper) = begin
        xa = x.a
        for i in eachindex(xa)
          @inbounds result[i] = $(op[2])(xa[i])
        end
      end
      $(Symbol(mangle_dot(op[1]),naop_suffix)){T<:AbstractFloat,N,A}(result, x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}}) = begin
        xadata = x.a.data
        resultdata = result.data
        for i in eachindex(xadata)
          @inbounds resultdata[i] = $(op[2])(xadata[i])
        end
      end
    end
  else
    quote
      $(op[1])(arr::DictArray) = mapvalues(x->$(op[1])(x), arr)
      $(op[1])(arr::LabeledArray) = LabeledArray($(op[1])(peel(arr)), pickaxis(arr))
      $(op[1])(x::AbstractArrayWrapper) = begin
        # AbstractArrayWrapper(map($(op[2]), x.a))
        result = similar(x)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x)
        result
      end
      $(Symbol(mangle_dot(op[1]),naop_suffix))(result, x::AbstractArrayWrapper) = begin
        xa = x.a
        for i in eachindex(xa)
          @inbounds result[i] = $(op[2])(xa[i])
        end
      end
      $(Symbol(mangle_dot(op[1]),naop_suffix)){T<:AbstractFloat,N,A}(result, x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}}) = begin
        xadata = x.a.data
        resultdata = result.data
        for i in eachindex(xadata)
          @inbounds resultdata[i] = $(op[2])(xadata[i])
        end
      end
    end
  end
end

# Ideally, lift every possible types using some supertypes. However, a lot of annoying ambiguity warnings may occur.
# So, try to fiddle around with possible combinations that do not give any ambiguity warnings.
const LiftToNullableTypes = [:Bool,
                             :Integer,
                             :AbstractFloat,
                             :Rational,
                             :(Irrational{:e}),
                             :Real,
                             :Complex,
                             :AbstractString,
                             :Char,
                             :Symbol]

promote_nullable_optypes{T,U}(op,::Type{Nullable{T}},::Type{Nullable{U}}) = Nullable{return_types(op,(T,U))[1]}
promote_nullable_optypes{T,U}(op,::Type{T},::Type{Nullable{U}}) = Nullable{return_types(op,(T,U))[1]}
promote_nullable_optypes{T,U}(op,::Type{Nullable{T}},::Type{U}) = Nullable{return_types(op,(T,U))[1]}
promote_nullable_optypes{T,U}(op,::Type{T},::Type{U}) = return_types(op,(T,U))[1]

preset_nullable_type{T,U}(::Type{Nullable{T}},::Type{Nullable{U}}, tpe) = Nullable{tpe}
preset_nullable_type{T,U}(::Type{T},::Type{Nullable{U}}, tpe) = Nullable{tpe}
preset_nullable_type{T,U}(::Type{Nullable{T}},::Type{U}, tpe) = Nullable{tpe}
preset_nullable_type{T,U}(::Type{T},::Type{U}, tpe) = tpe

const naop_suffix = "!"

mangle_dot(op) = begin
  op = string(op)
  if length(op) > 1 && op[1] == '.'
    Symbol("dot", op[2:end])
  else
    Symbol(op)
  end
end

# some adhoc definitions to suppress ambiguity warnings clashing with irrationals.jl
Base.broadcast(::typeof(^), x::Base.Irrational{:e}, y::AbstractArrayWrapper{Real}) = AbstractArrayWrapper(.^(x, y.a))
Base.broadcast{T<:Real}(::typeof(^), x::Base.Irrational{:e}, y::AbstractArrayWrapper{T}) = AbstractArrayWrapper(.^(x, y.a))

absarray_binary_wrapper(op) = begin
  nullelem = if length(op) == 2
    :(promote_nullable_optypes($(op[1]),T,U))
  elseif length(op) == 3
    :(preset_nullable_type(T,U,$(op[3])))
  end
  preexprs = quote
    $(Symbol(mangle_dot(op[1]),naop_suffix)){T,U}(result, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = begin
      xa = x.a
      ya = y.a
      for i in eachindex(xa,ya)
        @inbounds result[i] = $(op[2])(xa[i],ya[i])
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T,A,U,B}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}},
                                                   y::AbstractArrayWrapper{Nullable{U},N,FloatNAArray{U,N,B}}) = begin
      xadata = x.a.data
      yadata = y.a.data
      resultdata = result.data
      for i in eachindex(xadata,yadata)
        @inbounds resultdata[i] = $(op[2])(xadata[i],yadata[i])
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T,A,U<:Nullable}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}},
                                                   y::AbstractArrayWrapper{U,N}) = begin
      xadata = x.a.data
      ya= y.a
      resultdata = result.data
      na = convert(K,NaN)
      for i in eachindex(xadata,ya)
        @inbounds yai = ya[i]
        if isnull(yai)
          @inbounds resultdata[i] = na
        else
          @inbounds resultdata[i] = $(op[2])(xadata[i],yai.value)
        end
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T<:Nullable,U,B}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{T,N},
                                                   y::AbstractArrayWrapper{Nullable{U},N,FloatNAArray{U,N,B}}) = begin
      xa= x.a
      yadata = y.a.data
      resultdata = result.data
      na = convert(K,NaN)
      for i in eachindex(xa,yadata)
        @inbounds xai = xa[i]
        if isnull(xai)
          @inbounds resultdata[i] = na
        else
          @inbounds resultdata[i] = $(op[2])(xai.value, yadata[i])
        end
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T<:Nullable,U<:Nullable}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{T,N},
                                                   y::AbstractArrayWrapper{U,N}) = begin
      xa= x.a
      ya = y.a
      resultdata = result.data
      na = convert(K,NaN)
      for i in eachindex(xa,ya)
        @inbounds v = $(op[2])(xa[i], ya[i])
        @inbounds resultdata[i] = isnull(v) ? na : v.value
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T,A,U}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}},
                                                   y::AbstractArrayWrapper{U,N}) = begin
      xadata = x.a.data
      ya= y.a
      resultdata = result.data
      for i in eachindex(xadata,ya)
        @inbounds resultdata[i] = $(op[2])(xadata[i],ya[i])
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T,U,B}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{T,N},
                                                   y::AbstractArrayWrapper{Nullable{U},N,FloatNAArray{U,N,B}}) = begin
      xa= x.a
      yadata = y.a.data
      resultdata = result.data
      for i in eachindex(xa,yadata)
        @inbounds resultdata[i] = $(op[2])(xa[i], yadata[i])
      end
    end

    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T<:Nullable,U}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{T,N},
                                                   y::AbstractArrayWrapper{U,N}) = begin
      xa= x.a
      ya = y.a
      resultdata = result.data
      na = convert(K,NaN)
      for i in eachindex(xa,ya)
        @inbounds v = $(op[2])(xa[i], ya[i])
        @inbounds resultdata[i] = isnull(v) ? na : v.value
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T,U<:Nullable}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{T,N},
                                                   y::AbstractArrayWrapper{U,N}) = begin
      xa= x.a
      ya = y.a
      resultdata = result.data
      na = convert(K,NaN)
      for i in eachindex(xa,ya)
        @inbounds v = $(op[2])(xa[i], ya[i])
        @inbounds resultdata[i] = isnull(v) ? na : v.value
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){N,K,T,U}(result::FloatNAArray{K,N},
                                                   x::AbstractArrayWrapper{T,N},
                                                   y::AbstractArrayWrapper{U,N}) = begin
      xa= x.a
      ya = y.a
      resultdata = result.data
      for i in eachindex(xa,ya)
        @inbounds resultdata[i] = $(op[2])(xa[i], ya[i])
      end
    end

    $(Symbol(mangle_dot(op[1]),naop_suffix)){T,U}(result, x::AbstractArrayWrapper{T}, y::U) = begin
      xa = x.a
      for i in eachindex(xa)
        @inbounds result[i] = $(op[2])(xa[i],y)
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){K,T,U<:Nullable,N,A}(result::FloatNAArray{K,N}, x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}}, y::U) = begin
      if isnull(y)
        setna!(result)
      else
        xadata = x.a.data
        yvalue = y.value
        resultdata = result.data
        for i in eachindex(xadata)
          @inbounds resultdata[i] = $(op[2])(xadata[i],yvalue)
        end
      end
    end

    $(Symbol(mangle_dot(op[1]),naop_suffix)){T,U}(result, x::T, y::AbstractArrayWrapper{U}) = begin
      ya = y.a
      for i in eachindex(ya)
        @inbounds result[i] = $(op[2])(x,ya[i])
      end
    end
    $(Symbol(mangle_dot(op[1]),naop_suffix)){K,T<:Nullable,U,N,A}(result::FloatNAArray{K,N}, x::T, y::AbstractArrayWrapper{Nullable{U},N,FloatNAArray{U,N,A}}) = begin
      if isnull(x)
        setna!(result)
      else
        xvalue = x.value
        yadata = y.a.data
        resultdata = result.data
        for i in eachindex(yadata)
          @inbounds resultdata[i] = $(op[2])(xvalue, yadata[i])
        end
      end
    end

    $(Symbol(mangle_dot(op[1]),"adhoctype2", naop_suffix))(result, x::AbstractArrayWrapper, y) = begin
      xa = x.a
      for i in eachindex(xa)
        @inbounds result[i] = $(op[2])(xa[i],y)
      end
    end
    $(Symbol(mangle_dot(op[1]),"adhoctype2", naop_suffix)){K,T,N,A}(result::FloatNAArray{K,N}, x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}}, y) = begin
      xadata = x.a.data
      resultdata = result.data
      for i in eachindex(xadata)
        @inbounds resultdata[i] = $(op[2])(xadata[i],y)
      end
    end

    $(Symbol(mangle_dot(op[1]),"adhoctype1", naop_suffix))(result, x, y::AbstractArrayWrapper) = begin
      ya = y.a
      for i in eachindex(ya)
        @inbounds result[i] = $(op[2])(x,ya[i])
      end
    end
    $(Symbol(mangle_dot(op[1]),"adhoctype1", naop_suffix)){K,N,T,A}(result::FloatNAArray{K,N}, x, y::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}}) = begin
      yadata = y.a.data
      resultdata = result.data
      for i in eachindex(yadata)
        @inbounds resultdata[i] = $(op[2])(x,yadata[i])
      end
    end
  end

  mainexprs = if startswith(string(op[1]), ".")
    typeofop1 = :(typeof($(Symbol(string(op[1])[2:end]))))
    quote
      Base.broadcast{T,U}(::$typeofop1, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = begin
        @assert(size(x) == size(y))
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast{T,U<:Nullable}(::$typeofop1, x::AbstractArrayWrapper{T}, y::U) = begin
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x, y)
        result
      end
      Base.broadcast{T<:Nullable,U}(::$typeofop1, x::T, y::AbstractArrayWrapper{U}) = begin
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x, y)
        result
      end
      Base.broadcast(::$typeofop1, x::Nullable, y::Union{DictArray,LabeledArray}) = mapvalues($(op[1]), x, y)
      Base.broadcast(::$typeofop1, x::Union{DictArray,LabeledArray}, y::Nullable) = mapvalues($(op[1]), x, y)
      Base.broadcast(::$typeofop1, x::Union{DictArray,LabeledArray}, y::Union{DictArray,LabeledArray}) = mapvalues($(op[1]), x, y)

      # to avoid some type ambiguity.
      Base.broadcast(::$typeofop1, x::Bool, y::LabeledArray{Bool}) = mapvalues($(op[1]), x, y)
      Base.broadcast(::$typeofop1, x::LabeledArray{Bool}, y::Bool) = mapvalues($(op[1]), x, y)
    end
  else
    quote
      $(op[1]){T,U}(x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = begin
        @assert(size(x) == size(y))
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x, y)
        result
      end
      $(op[1]){T,U<:Nullable}(x::AbstractArrayWrapper{T}, y::U) = begin
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x, y)
        result
      end
      $(op[1]){T<:Nullable,U}(x::T, y::AbstractArrayWrapper{U}) = begin
        #AbstractArrayWrapper(map(v->$(op[2])(x,v), y.a))
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),naop_suffix))(result.a, x, y)
        result
      end
      $(op[1])(x::Nullable, y::Union{DictArray,LabeledArray}) = mapvalues($(op[1]), x, y)
      $(op[1])(x::Union{DictArray,LabeledArray}, y::Nullable) = mapvalues($(op[1]), x, y)
      $(op[1])(x::Union{DictArray,LabeledArray}, y::Union{DictArray,LabeledArray}) = mapvalues($(op[1]), x, y)

      # to avoid some type ambiguity.
      $(op[1])(x::Bool, y::LabeledArray{Bool}) = mapvalues($(op[1]), x, y)
      $(op[1])(x::LabeledArray{Bool}, y::Bool) = mapvalues($(op[1]), x, y)
    end
  end

  Expr(:block, preexprs, mainexprs)
end

#macro create_op(op, args...)
#  if startswith(string(op), ".")
#    Expr(:call, :(Base.broadcast), :(::typeof($(Symbol(string(op)[2:end])))), args...)
#  else
#    Expr(:($(op)), args...)
#  end
#end

absarray_binary_wrapper_adhoc(op, adhoctype) = begin
  nullelem = if length(op) == 2
    :(promote_nullable_optypes($(op[1]),T,U))
  elseif length(op) == 3
    :(preset_nullable_type(T,U,$(op[3])))
  end

  if startswith(string(op[1]), ".")
    typeofop1 = :(typeof($(Symbol(string(op[1])[2:end]))))
    quote
      Base.broadcast(::$typeofop1, x::AbstractArrayWrapper{$adhoctype}, y::$adhoctype) = begin
        T = eltype(x)
        U = typeof(y)
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype2",naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast(::$typeofop1, x::$adhoctype, y::AbstractArrayWrapper{$adhoctype}) = begin
        T = typeof(x)
        U = eltype(y)
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype1",naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast{T<:$adhoctype}(::$typeofop1, x::AbstractArrayWrapper{T}, y::$adhoctype) = begin
        U = typeof(y)
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype2",naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast{U<:$adhoctype}(::$typeofop1, x::$adhoctype, y::AbstractArrayWrapper{U}) = begin
        T = typeof(x)
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype1",naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast{T<:Nullable}(::$typeofop1, x::AbstractArrayWrapper{T}, y::$adhoctype) = begin
        U = typeof(y)
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype2",naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast{U<:Nullable}(::$typeofop1, x::$adhoctype, y::AbstractArrayWrapper{U}) = begin
        T = typeof(x)
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype1",naop_suffix))(result.a, x, y)
        result
      end

      Base.broadcast(::$typeofop1, x::$adhoctype, y::DictArray) = mapvalues($(op[1]), x, y)
      Base.broadcast(::$typeofop1, x::$adhoctype, y::LabeledArray) = mapvalues($(op[1]), x, y)
      Base.broadcast(::$typeofop1, x::DictArray, y::$adhoctype) = mapvalues($(op[1]), x, y)
      Base.broadcast(::$typeofop1, x::LabeledArray, y::$adhoctype) = mapvalues($(op[1]), x, y)
    end
  else
    quote
      $(op[1])(x::AbstractArrayWrapper{$adhoctype}, y::$adhoctype) = begin
        T = eltype(x)
        U = typeof(y)
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype2",naop_suffix))(result.a, x, y)
        result
      end

      $(op[1])(x::$adhoctype, y::AbstractArrayWrapper{$adhoctype}) = begin
        T = typeof(x)
        U = eltype(y)
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype1",naop_suffix))(result.a, x, y)
        result
      end

      $(op[1]){T<:$adhoctype}(x::AbstractArrayWrapper{T}, y::$adhoctype) = begin
        U = typeof(y)
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype2",naop_suffix))(result.a, x, y)
        result
      end

      $(op[1]){U<:$adhoctype}(x::$adhoctype, y::AbstractArrayWrapper{U}) = begin
        T = typeof(x)
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype1",naop_suffix))(result.a, x, y)
        result
      end

      $(op[1]){T<:Nullable}(x::AbstractArrayWrapper{T}, y::$adhoctype) = begin
        U = typeof(y)
        result = similar(x, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype2",naop_suffix))(result.a, x, y)
        result
      end

      $(op[1]){U<:Nullable}(x::$adhoctype, y::AbstractArrayWrapper{U}) = begin
        T = typeof(x)
        result = similar(y, $nullelem)
        $(Symbol(mangle_dot(op[1]),"adhoctype1",naop_suffix))(result.a, x, y)
        result
      end

      $(op[1])(x::$adhoctype, y::DictArray) = mapvalues($(op[1]), x, y)
      $(op[1])(x::$adhoctype, y::LabeledArray) = mapvalues($(op[1]), x, y)
      $(op[1])(x::DictArray, y::$adhoctype) = mapvalues($(op[1]), x, y)
      $(op[1])(x::LabeledArray, y::$adhoctype) = mapvalues($(op[1]), x, y)
    end
  end
end

*(x::Real, y::AbstractArrayWrapper) = broadcast(*, x, y)
*(x::AbstractArrayWrapper, y::Real) = broadcast(*, x, y)
*(x::Complex, y::AbstractArrayWrapper) = broadcast(*, x, y)
*(x::AbstractArrayWrapper, y::Complex) = broadcast(*, x, y)
*{T<:Real}(x::Nullable{T}, y::AbstractArrayWrapper) = broadcast(*, x, y)
*{T<:Real}(x::AbstractArrayWrapper, y::Nullable{T}) = broadcast(*, x, y)
*{T<:Complex}(x::Nullable{T}, y::AbstractArrayWrapper) = broadcast(*, x, y)
*{T<:Complex}(x::AbstractArrayWrapper, y::Nullable{T}) = broadcast(*, x, y)
/(x::Real, y::AbstractArrayWrapper) = broadcast(/, x, y)
/(x::AbstractArrayWrapper, y::Real) = broadcast(/, x, y)
/(x::Complex, y::AbstractArrayWrapper) = broadcast(/, x, y)
/(x::AbstractArrayWrapper, y::Complex) = broadcast(/, x, y)
/{T<:Real}(x::Nullable{T}, y::AbstractArrayWrapper) = broadcast(/, x, y)
/{T<:Real}(x::AbstractArrayWrapper, y::Nullable{T}) = broadcast(/, x, y)
/{T<:Complex}(x::Nullable{T}, y::AbstractArrayWrapper) = broadcast(/, x, y)
/{T<:Complex}(x::AbstractArrayWrapper, y::Nullable{T}) = broadcast(/, x, y)

*(x::Real, y::Union{DictArray,LabeledArray}) = broadcast(*, x, y)
*(x::Union{DictArray,LabeledArray}, y::Real) = broadcast(*, x, y)
*(x::Complex, y::Union{DictArray,LabeledArray}) = broadcast(*, x, y)
*(x::Union{DictArray,LabeledArray}, y::Complex) = broadcast(*, x, y)
*{T<:Real}(x::Nullable{T}, y::Union{DictArray,LabeledArray}) = broadcast(*, x, y)
*{T<:Real}(x::Union{DictArray,LabeledArray}, y::Nullable{T}) = broadcast(*, x, y)
*{T<:Complex}(x::Nullable{T}, y::Union{DictArray,LabeledArray}) = broadcast(*, x, y)
*{T<:Complex}(x::Union{DictArray,LabeledArray}, y::Nullable{T}) = broadcast(*, x, y)
*(x::FloatNAArray, y::FloatNAArray) = FloatNAArray(x.data * y.data)
/(x::FloatNAArray, y::FloatNAArray) = FloatNAArray(x.data / y.data)
# these are not supported yet properly other than for FloatNAArray.
*(x::AbstractArrayWrapper, y::AbstractArrayWrapper) = AbstractArrayWrapper(x.a * y.a)
/(x::AbstractArrayWrapper, y::AbstractArrayWrapper) = AbstractArrayWrapper(x.a / y.a)
*(x::DictArray, y::DictArray) = mapvalues(*, x, y)
/(x::DictArray, y::DictArray) = mapvalues(/, x, y)
*(x::LabeledArray, y::LabeledArray) = begin
  assert(x.axes[end] == y.axes[1])
  LabeledArray(mapvalues(*, x.data, y.data), (x.axes[1:end-1]...,y.axes[2:end]...))
end
/{T,U}(x::LabeledArray{T,2}, y::LabeledArray{U,2}) = begin
  assert(x.axes[end] == y.axes[end])
  LabeledArray(mapvalues(/, x.data, y.data), (x.axes[1], y.axes[1]))
end
/(x::Real, y::Union{DictArray,LabeledArray}) = broadcast(/, x, y)
/(x::Union{DictArray,LabeledArray}, y::Real) = broadcast(/, x, y)
/(x::Complex, y::Union{DictArray,LabeledArray}) = broadcast(/, x, y)
/(x::Union{DictArray,LabeledArray}, y::Complex) = broadcast(/, x, y)
/{T<:Real}(x::Nullable{T}, y::Union{DictArray,LabeledArray}) = broadcast(/, x, y)
/{T<:Real}(x::Union{DictArray,LabeledArray}, y::Nullable{T}) = broadcast(/, x, y)
/{T<:Complex}(x::Nullable{T}, y::Union{DictArray,LabeledArray}) = broadcast(/, x, y)
/{T<:Complex}(x::Union{DictArray,LabeledArray}, y::Nullable{T}) = broadcast(/, x, y)

nullable_unary_wrapper(op) = begin
  nullelem = if length(op) == 2
    Expr(:curly, :Nullable, :T)
  elseif length(op) == 3
    Expr(:curly, :Nullable, op[3])
  end
  quote
    $(op[2]){T}(x::Nullable{T}) = isnull(x) ? $nullelem() : Nullable($(op[1])(x.value))
    $(op[2]){T}(x::T) = $(op[1])(x)
  end
end

nullable_binary_wrapper(op) = begin
  nullelem = if length(op) == 2
    Expr(:curly, :Nullable, Expr(:ref, Expr(:call, :return_types, op[1], Expr(:tuple, :T, :V)), 1))
    #Expr(:curly, :Nullable, Expr(:call, :promote_type, :T, :V))
  elseif length(op) == 3
    Expr(:curly, :Nullable, op[3])
  end
  quote
    $(op[2]){T,V}(x::Nullable{T}, y::Nullable{V}) =
      isnull(x) || isnull(y) ? $nullelem() : $nullelem($(op[1])(x.value, y.value))
    $(op[2]){T,V}(x::Nullable{T}, y::V) =
      isnull(x) ? $nullelem() : $nullelem($(op[1])(x.value, y))
    $(op[2]){T,V}(x::T, y::Nullable{V}) =
      isnull(y) ? $nullelem() : $nullelem($(op[1])(x, y.value))
    $(op[2]){T,V}(x::T, y::V) = $(op[1])(x, y)
  end
end

for op in ((:+, :naop_plus), (:-, :naop_minus), (:~, :naop_not))
  eval(nullable_unary_wrapper(op))
end
for op in ((:.+, :naop_plus), (:.-, :naop_minus), (:.*, :naop_mul),
                         (:./, :naop_div),
                         # could not use .== or .!= over general types such as AbstractString.
                         # let's settle down to using == and != instead for now.
                         # at least, we don't have to provide a blanket definition (:.==)(x,y) = x==y then.
                         (:.\, :naop_invdiv), (:.//, :naop_frac), (:(==), :naop_eq, Bool), (:.<, :naop_lt, Bool),
                         (:!=, :naop_noeq, Bool), (:.<=, :naop_le, Bool), (:.%, :naop_mod), (:.<<, :naop_lsft),
                         (:.>>, :naop_rsft), (:.^, :naop_exp), (:.>, :naop_gt, Bool), (:.>=, :naop_ge, Bool),
                         (:&, :naop_and), (:|, :naop_or), (:$, :naop_xor))
  eval(nullable_binary_wrapper(op))
end

for op in ((:+, :naop_plus), (:-, :naop_minus), (:.+, :naop_plus), (:.-, :naop_minus), (:~, :naop_not))
  eval(absarray_unary_wrapper(op))
end
for op in ((:+, :naop_plus), (:-, :naop_minus), (:.+, :naop_plus), (:.-, :naop_minus), (:.*, :naop_mul),
                         (:./, :naop_div),
                         (:.\, :naop_invdiv), (:.//, :naop_frac), (:.==, :naop_eq, Bool), (:.<, :naop_lt, Bool),
                         (:.!=, :naop_noeq, Bool), (:.<=, :naop_le, Bool), (:.%, :naop_mod), (:.<<, :naop_lsft),
                         (:.>>, :naop_rsft), (:.^, :naop_exp), (:.>, :naop_gt, Bool), (:.>=, :naop_ge, Bool),
                         (:&, :naop_and), (:|, :naop_or), (:$, :naop_xor))
  eval(absarray_binary_wrapper(op))
  for adhoctype in LiftToNullableTypes
    eval(absarray_binary_wrapper_adhoc(op, adhoctype))
  end
end

(==){T<:Nullable,U<:Nullable}(x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = begin
  if x === y
    return true
  else
    for (elx, ely) in zip(x.a, y.a)
      if isnull(elx) && !isnull(ely)
        return false
      elseif !isnull(elx) && isnull(ely)
        return false
      elseif !isnull(elx) && !isnull(ely) && elx.value != ely.value
        return false
      end
    end
    return true
  end
end

(==){T<:AbstractFloat,U<:AbstractFloat,N,A,B}(x::AbstractArrayWrapper{Nullable{T},N,FloatNAArray{T,N,A}}, y::AbstractArrayWrapper{Nullable{U},N,FloatNAArray{U,N,B}}) = begin
  if x === y
    return true
  else
    for (elx, ely) in zip(x.a.data, y.a.data)
      if !(isnan(elx) && isnan(ely)) && elx != ely
        return false
      end
    end
    return true
  end
end

#ifelse_inner{T,U,M,N,P,X,Y,Z}(cond::Union{Bool,Nullable{Bool},
#                              AbstractArrayWrapper{Bool,M,X},
#                              AbstractArrayWrapper{Nullable{Bool},M,X}},
#                 x::Union{T,Nullable{T},
#                          AbstractArrayWrapper{T,N,Y},
#                          AbstractArrayWrapper{Nullable{T},N,Y}},
#                 y::Union{U,Nullable{U},
#                          AbstractArrayWrapper{U,P,Z},
#                          AbstractArrayWrapper{Nullable{U},P,Z}}) = begin
#  promoted_type = promote_type(T,U)
#  result = ifelse_inner_similar(cond, x, y, promoted_type)
#  for i in ifelse_inner_eachindex(cond, x, y, result)
#    condi = ifelse_inner_getindex(cond, i)
#    xi = ifelse_inner_getindex(x, i)
#    yi = ifelse_inner_getindex(y, i)
#    result[i] = condi.isnull ? yi : condi.value ? xi : yi
#  end
#  result
#end
ifelse_inner{T}(::Type{T}, cond::AbstractArray, x, y) = begin
  result = ifelse_inner_similar(cond, x, y, Nullable{T})
  nullelem = Nullable{T}()
  for i in ifelse_inner_eachindex(cond, x, y, result)
    condi = ifelse_inner_getindex(cond, i)
    xi = ifelse_inner_getindex(x, i)
    yi = ifelse_inner_getindex(y, i)
    result[i] = isnull(condi) ? nullelem : condi.value ? ifelse_inner_wrap_nullable(xi) : ifelse_inner_wrap_nullable(yi)
  end
  result
end
ifelse_inner{T}(::Type{T}, cond::Bool, x::AbstractArray, y::AbstractArray) = cond ? nalift(x) : nalift(y)
ifelse_inner{T}(::Type{T}, cond::Nullable{Bool}, x::AbstractArray, y::AbstractArray) = isnull(cond) ? ifelse_inner_broadcast_null(T,x) : cond.value ? nalift(x) : nalift(y)
ifelse_inner{T}(::Type{T}, cond::Bool, x::AbstractArray, y) = cond ? nalift(x) : ifelse_inner_broadcast(x,y)
ifelse_inner{T}(::Type{T}, cond::Nullable{Bool}, x::AbstractArray, y) = isnull(cond) ? ifelse_inner_broadcast_null(T,x) : cond.value ? nalift(x) : ifelse_inner_broadcast(x,y)
ifelse_inner{T}(::Type{T}, cond::Bool, x, y::AbstractArray) = cond ? ifelse_inner_broadcast(y,x) : nalift(y)
ifelse_inner{T}(::Type{T}, cond::Nullable{Bool}, x, y::AbstractArray) = isnull(cond) ? ifelse_inner_broadcast_null(T,y) : cond.value ? ifelse_inner_broadcast(y,x) : nalift(y)

ifelse_inner_broadcast{U<:Nullable}(template::AbstractArray, v::U) = (r=similar(template,U);fill!(r,v);r)
ifelse_inner_broadcast{U}(template::AbstractArray, v::U) = (r=similar(template,Nullable{U});nv=Nullable(v);fill!(r,nv);r)

ifelse_inner_broadcast_null{T}(::Type{T}, template::AbstractArray) = (r=similar(template,Nullable{T});n=Nullable{T}();fill!(r,n);r)

ifelse_inner_wrap_nullable(x::Nullable) = x
ifelse_inner_wrap_nullable(x) = Nullable(x)

# it's difficult to avoid all type ambiguity warnings without listing all these methods...
Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{Nullable{T}}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{Nullable{T}}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{T}, y::AbstractArray{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::AbstractArrayWrapper{Nullable{T}}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::AbstractArray{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArrayWrapper{Nullable{T}}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::AbstractArrayWrapper{Nullable{T}}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::Nullable{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::Nullable{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::Nullable{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::Nullable{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::T, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::T, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Bool, x::Nullable{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::Nullable{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArray{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::T, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::T, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::Nullable{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::Nullable{T}, y::AbstractArrayWrapper{U}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::Bool, x::T, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::Nullable{Bool}, x::T, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArray{T}, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::T, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::T, y::AbstractArrayWrapper{Nullable{U}}) = ifelse_inner(promote_type(T,U), cond, x, y)

Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::Nullable{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArray{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::T, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::Nullable{T}, y::AbstractArray{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::Nullable{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArray{T}, y::AbstractArray{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::AbstractArray{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::T, y::AbstractArray{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Bool}, x::T, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::Nullable{T}, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::T, y::Nullable{U}) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::Nullable{T}, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)
Base.ifelse{T,U}(cond::AbstractArrayWrapper{Nullable{Bool}}, x::T, y::U) = ifelse_inner(promote_type(T,U), cond, x, y)

ifelse_inner_similar(cond::AbstractArray, x::AbstractArray, y::AbstractArray, promoted_type) = similar(cond, promoted_type)
ifelse_inner_similar(cond::AbstractArray, x::AbstractArray, y, promoted_type) = similar(cond, promoted_type)
ifelse_inner_similar(cond::AbstractArray, x, y::AbstractArray, promoted_type) = similar(cond, promoted_type)
ifelse_inner_similar(cond::AbstractArray, x, y, promoted_type) = similar(cond, promoted_type)

ifelse_inner_eachindex(cond::AbstractArray, x::AbstractArray, y::AbstractArray, promoted_type) = eachindex(cond, x, y, promoted_type)
ifelse_inner_eachindex(cond::AbstractArray, x::AbstractArray, y, promoted_type) = eachindex(cond, x, promoted_type)
ifelse_inner_eachindex(cond::AbstractArray, x, y::AbstractArray, promoted_type) = eachindex(cond, y, promoted_type)
ifelse_inner_eachindex(cond::AbstractArray, x, y, promoted_type) = eachindex(cond, promoted_type)

ifelse_inner_getindex{T<:Nullable}(x::AbstractArray{T}, i) = x[i]
ifelse_inner_getindex(x::AbstractArray, i) = Nullable(x[i])
ifelse_inner_getindex(x, i) = x

# TODO make sure this blanket definition is okay.
# remvoed in favor of using == instead of .== for naop_eq (and similarly for naop_noeq).
# (.==)(x, y) = x == y


text:
import Base: show

function show(io::IO, x::UnknownDimension)
    print(io, "UD[$(x.id)]")
end

function show(io::IO, x::BasicDimension)
    print(io, "BD[$(x.id)](")
    show(io, x.grid)
    print(io, ")")
end

function show(io::IO, x::RegularGrid)
    print(io, "UG(")
    show(io, x.left)
    print(io, ", ")
    show(io, x.right)
    print(io, ", ")
    show(io, x.points)
    print(io, ")")
end

function show(io::IO, x::TransformedDimension)
    print(io, "TD[$(x.id)](")
    show(io, x.base_dimension)
    print(io, ")")
end

function show(io::IO, x::UnknownField)
    print(io, "UF[$(x.id)](")
    for (i, dim) in enumerate(x.dimensions)
        show(io, dim)
        if i != length(x.dimensions)
            print(io, ", ")
        end
    end
    print(io, ")")
end

function show(io::IO, x::KnownField)
    print(io, "KF(")
    if length(x.dimensions) > 0
        for (i, dim) in enumerate(x.dimensions)
            show(io, dim)
            if i != length(x.dimensions)
                print(io, ", ")
            end
        end
    else
        show(io, reshape(from_backend(x.data), 1)[1])
    end
    print(io, ")")
end

function show(io::IO, x::Fourier)
    if x.inverse
        print(io, "I")
    end
    print(io, "Fourier[")
    for (i, (from, to)) in enumerate(x.dimensions)
        show(io, from)
        print(io, "->")
        show(io, to)
        if i != length(x.dimensions)
            print(io, ", ")
        end
    end
    print(io, "](")
    show(io, x.fexpr)
    print(io, ")")
end

function show(io::IO, fexpr::FieldExprNode)
    print(io, "{")
    show(io, fexpr.head)
    print(io, ", ")

    args = fexpr.args
    for (i, arg) in enumerate(args)
        show(io, arg)
        if i != length(args)
            print(io, ", ")
        end
    end
    print(io, "}")
end


text:
function f()
   x = zeros(10)
   for i in 1:10
      x[i] += 1.
   end
   return x
end


text:
using Distributions
using Random
using LinearAlgebra
using Plots

include("./src/Leabra.jl")

# This is an example network designed simply to be a pattern associator.
# This is pretty much the example from the Leabra Matlab code by Sergio Verduzco-Flores.

# 3 layers and their dimensions
dim_lays = [(5 , 5),
            (10, 10),
            (5 , 5)]

# connections[i,j] = c
# Layer i receives connections from layer j, and they have a relative strength c.
# The network constructor will normalize this matrix so that if there
# are non-zero entries in a row, they add to 1.
# 2 receives connections from 1 at relative strength of 1
# 2 receives connections from 3 at relative strength of 0.2
# 3 receives connections from 2 at relative strength of 1
connections = [0  0  0;
               1  0 .2;
               0  1  0]

net = Leabra.build_network(dim_lays, connections)

n_inputs = 5

# inputs = Leabra.create_random_inputs(n_inputs) # this just creates random patterns for any size input/output layers
inputs = Leabra.create_patterns(n_inputs) # assumes a 5x5 input and output layer

n_epochs = 20
errors = Leabra.train_network!(net, inputs, n_epochs);

mean_errs = mean(errors, dims=2);
println("Mean errors: $mean_errs")
plot(mean_errs)
ylims!((0.0,1.0))


text:
# 1D Non-Linear Diffusion tests
# See doi:10.1016/j.camwa.2006.12.077

# TODO: Add more complex tests.

# Packages and inclusions
using ModelingToolkit,DiffEqOperators,LinearAlgebra,Test,OrdinaryDiffEq, DomainSets
using ModelingToolkit: Differential
# Tests
@testset "Test 00: Dt(u(t,x)) ~ Dx(u(t,x)^(-1) * Dx(u(t,x)))" begin
    # Variables, parameters, and derivatives
    @parameters t x
    @variables u(..)
    Dx = Differential(x)
    Dt = Differential(t)
    t_min= 0.
    t_max = 2.
    x_min = 0.
    x_max = 2.
    c = 1.0
    a = 1.0

    # Analytic solution
    analytic_sol_func(t,x) = 2.0 * (c + t) / (a + x)^2

    # Equation
    eq = Dt(u(t,x)) ~ Dx(u(t,x)^(-1) * Dx(u(t,x)))

    # Initial and boundary conditions
    bcs = [u(t_min,x) ~ analytic_sol_func(t_min,x),
           u(t,x_min) ~ analytic_sol_func(t,x_min),
           u(t,x_max) ~ analytic_sol_func(t,x_max)]

    # Space and time domains
    domains = [t ∈ Interval(t_min,t_max),
               x ∈ Interval(x_min,x_max)]

    # PDE system
    @named pdesys = PDESystem([eq],bcs,domains,[t,x],[u(t,x)])

    # Method of lines discretization
    dx = 0.01
    discretization = MOLFiniteDifference([x=>dx],t)
    prob = ModelingToolkit.discretize(pdesys,discretization)

    # Solution of the ODE system
    using OrdinaryDiffEq
    sol = solve(prob,Rosenbrock32())

    # Test against exact solution
    r_space = x_min:dx:x_max
    asf = [analytic_sol_func(t_max,x) for x in r_space]
    Nx = floor(Int64, (x_max - x_min) / dx) + 1
    @variables u[1:Nx](t)
    sol′ = [sol[u[i]][end] for i in 1:Nx]
    @test asf ≈ sol′ atol=0.1

    # Plots
    #using Plots
    #plot(r_space, asf, seriestype = :scatter,label="analytic solution")
    #plot!(r_space, sol′, label="numeric solution")
    #savefig("MOL_NonLinear_Diffusion_1D_Test00.png")

end

@testset "Test 01: Dt(u(t,x)) ~ Dx(u(t,x)^2 * Dx(u(t,x)))" begin

    # Variables, parameters, and derivatives
    @parameters t x
    @variables u(..)
    Dx = Differential(x)
    Dt = Differential(t)
    t_min= 0.
    t_max = 2.
    x_min = 0.
    x_max = 2.
    c = 50.0
    h = 0.50

    # Analytic solution
    analytic_sol_func(t,x) = 0.5 * (x + h) / sqrt(c - t)

    # Equation
    eq = Dt(u(t,x)) ~ Dx(u(t,x)^2 * Dx(u(t,x)))

    # Initial and boundary conditions
    bcs = [u(t_min,x) ~ analytic_sol_func(t_min,x),
           u(t,x_min) ~ analytic_sol_func(t,x_min),
           u(t,x_max) ~ analytic_sol_func(t,x_max)]

    # Space and time domains
    domains = [t ∈ Interval(t_min,t_max),
               x ∈ Interval(x_min,x_max)]

    # PDE system
    @named pdesys = PDESystem([eq],bcs,domains,[t,x],[u(t,x)])

    # Method of lines discretization
    dx = 0.01
    discretization = MOLFiniteDifference([x=>dx],t)
    prob = ModelingToolkit.discretize(pdesys,discretization)

    # Solution of the ODE system
    using OrdinaryDiffEq
    sol = solve(prob,Rosenbrock32())

    # Test against exact solution
    r_space = x_min:dx:x_max
    asf = [analytic_sol_func(t_max,x) for x in r_space]
    Nx = floor(Int64, (x_max - x_min) / dx) + 1
    @variables u[1:Nx](t)
    sol′ = [sol[u[i]][end] for i in 1:Nx]
    @test asf ≈ sol′ atol=0.01

    # Plots
    #using Plots
    #plot(r_space, asf, seriestype = :scatter,label="analytic solution")
    #plot!(r_space, sol′, label="numeric solution")
    #savefig("MOL_NonLinear_Diffusion_1D_Test01.png")

end

@testset "Test 02: Dt(u(t,x)) ~ Dx(1. / (1. + u(t,x)^2) * Dx(u(t,x)))" begin

    # Variables, parameters, and derivatives
    @parameters t x
    @variables u(..)
    Dx = Differential(x)
    Dt = Differential(t)
    t_min= 0.
    t_max = 2.
    x_min = 0.
    x_max = 2.

    # Analytic solution
    analytic_sol_func(t,x) = tan(x)

    # Equation
    eq = Dt(u(t,x)) ~ Dx(1. / (1. + u(t,x)^2) * Dx(u(t,x)))

    # Initial and boundary conditions
    bcs = [u(t_min,x) ~ analytic_sol_func(t_min,x),
           u(t,x_min) ~ analytic_sol_func(t,x_min),
           u(t,x_max) ~ analytic_sol_func(t,x_max)]

    # Space and time domains
    domains = [t ∈ Interval(t_min,t_max),
               x ∈ Interval(x_min,x_max)]

    # PDE system
    @named pdesys = PDESystem([eq],bcs,domains,[t,x],[u(t,x)])

    # Method of lines discretization
    dx = 0.01
    discretization = MOLFiniteDifference([x=>dx],t)
    prob = ModelingToolkit.discretize(pdesys,discretization)

    # Solution of the ODE system
    using OrdinaryDiffEq
    sol = solve(prob,Rosenbrock32()) # TODO: check warnings

    # Test against exact solution
    r_space = x_min:dx:x_max
    asf = [analytic_sol_func(t_max,x) for x in r_space]
    Nx = floor(Int64, (x_max - x_min) / dx) + 1
    @variables u[1:Nx](t)
    sol′ = [sol[u[i]][end] for i in 1:Nx]

    m = max(asf...,sol′...)
    @test asf / m ≈ sol′ / m atol=0.16 # the difference occurs when tan(x) goes to infinite

    # Plots
    #using Plots
    #plot(r_space, asf, seriestype = :scatter,label="Analytic solution")
    #plot!(r_space, sol′, label="Numeric solution")
    #savefig("MOL_NonLinear_Diffusion_1D_Test02.png")

end

@testset "Test 03: Dt(u(t,x)) ~ Dx(1. / (u(t,x)^2 - 1.) * Dx(u(t,x)))" begin

    # Variables, parameters, and derivatives
    @parameters t x
    @variables u(..)
    Dx = Differential(x)
    Dt = Differential(t)
    t_min= 0.
    t_max = 2.
    x_min = 0.
    x_max = 2.

    # Analytic solution
    analytic_sol_func(t,x) = -coth(x)

    # Equation
    eq = Dt(u(t,x)) ~ Dx(1. / (u(t,x)^2 - 1.) * Dx(u(t,x)))

    # Initial and boundary conditions
    bcs = [u(t_min,x) ~ analytic_sol_func(t_min,x),
           u(t,x_min) ~ analytic_sol_func(t,x_min),
           u(t,x_max) ~ analytic_sol_func(t,x_max)]

    # Space and time domains
    domains = [t ∈ Interval(t_min,t_max),
               x ∈ Interval(x_min,x_max)]

    # PDE system
    @named pdesys = PDESystem([eq],bcs,domains,[t,x],[u(t,x)])

    # Method of lines discretization
    dx = 0.01
    discretization = MOLFiniteDifference([x=>dx],t)
    prob = ModelingToolkit.discretize(pdesys,discretization)

    # Solution of the ODE system
    using OrdinaryDiffEq
    sol = solve(prob,Rosenbrock32())

    # Test against exact solution
    r_space = x_min:dx:x_max
    asf = [analytic_sol_func(t_max,x) for x in r_space]
    Nx = floor(Int64, (x_max - x_min) / dx) + 1
    @variables u[1:Nx](t)
    sol′ = [sol[u[i]][end] for i in 1:Nx]

    m = max(asf...,sol′...)
    @test asf / m ≈ sol′ / m atol=0.16

    # Plots
    #using Plots
    #plot(r_space, asf, seriestype = :scatter,label="Analytic solution")
    #plot!(r_space, sol′, label="Numeric solution")
    #savefig("MOL_NonLinear_Diffusion_1D_Test03.png")

end


text:
export incrementer, multiplier, inverse_decay, cached_inverse_decay, sample_avg, cached_sample_avg, exp_decay

"""
    incrementer(;start=1, step=1)

# Example
```julia
julia> f = incrementer();

julia> [f() for _ in 1:3]
3-element Array{Int64,1}:
 1
 2
 3
```
"""
function incrementer(;start=1, step=1)
    i = start - step
    () -> i += step
end

"""
    multiplier(;start=1.0, ratio=1.0)

# Example
```julia
julia> f = multiplier(ratio=0.9);

julia> [f() for _ in 1:3]
3-element Array{Float64,1}:
 1.0
 0.9
 0.81
```
"""
function multiplier(;start=1.0, ratio=1.0)
    x = start / ratio
    () -> x *= ratio
end

"""
    inverse_decay()

# Example
```julia
julia> f = inverse_decay()
#25 (generic function with 1 method)

julia> [f() for _ in 1:5]
5-element Array{Float64,1}:
 1.0
 0.5
 0.3333333333333333
 0.25
 0.2
```
"""
function inverse_decay()
    x = 0
    (args...) -> begin
        x += 1
        1/x
    end
end

"""
    sample_avg()

# Example
```julia
julia> f = sample_avg();

julia> f(2)
2.0

julia> f(3) # (2+3)/2
2.5

julia> f(5) # (2+3+5)/3
3.3333333333333335
```
"""
function sample_avg()
    t = 0
    avg = 0
    (x) -> begin
        t += 1
        avg += (x-avg)/t
    end
end

"""
    cached_inverse_decay()

# Example
```julia
julia> f = cached_inverse_decay();

julia> f(:a) # cached!
1.0

julia> f(:a) # call again
0.5

julia> f(:a) # call again
0.3333333333333333

julia> f(:b) # a new cache
1.0
```
"""
function cached_inverse_decay()
    cache = Dict()
    function (x)
        if !haskey(cache, x)
            cache[x] = inverse_decay()
        end
        cache[x]()
    end
end

"""
    cached_sample_avg()

# Example
```julia
julia> f = cached_sample_avg();

julia> f(:a, 3)  # cache :a
3.0

julia> f(:a, 5)  # calculate avg value of  :a (3 + 5) / 2
4.0

julia> f(:a, 8)  # calculate avg value of  :a (3 + 5 + 8) /3
5.333333333333333

julia> f(:b, 0)  # cache another value
0.0
```
"""
function cached_sample_avg()
    cache = Dict()
    function (k, x)
        if !haskey(cache, k)
            cache[k] = sample_avg()
        end
        cache[k](x)
    end
end

"""
    exp_decay(init=1.0, λ=0.1, decay_step=1000, clip=1e-4)

See [Exponential Decay](https://en.wikipedia.org/wiki/Exponential_decay)
"""
function exp_decay(;init=1.0, λ=0.1, decay_step=1000, clip=1e-4)
    i = -1
    function f()
        i += 1
        max(init * exp(- λ * i / decay_step), clip)
    end
end


text:
module A

mutable struct Parameters
	a::Int
end

end # module

max_stars_repo_name: tgflynn/IncludeGuards

max_issues_repo_name: tgflynn/IncludeGuards

max_forks_repo_name: tgflynn/IncludeGuards


text:
# Generate voicings exercises.

import JSON

# This ID is incremented for every problem generated.  Barring changes
# to the generation, this should generate the same ids on subsequent
# runs.
cur_id = 1

function emit(question, answer)
    global cur_id
    println("INSERT INTO \"probs\" VALUES($cur_id, '$question', '$answer');")
    cur_id += 1
end

type Transpose
    name :: String
    amount :: Int
end

# Transpositions for licks.  The order is mixed up a bit.
transposes = [
    Transpose("C", 0),
    Transpose("G", 7),
    Transpose("F", 5),
    Transpose("D", 2),
    Transpose("B♭", 10),
    Transpose("A", 9),
    Transpose("E♭", 3),
    Transpose("E", 4),
    Transpose("B", 11),
    Transpose("A♭", 8),
    Transpose("F♯", 6),
    Transpose("C♯", 1),
    Transpose("G♭", 6) ]

function transpose(notes :: Vector{Vector{Int}}, by :: Transpose)
    map(notes) do chord
        chord .+ by.amount
    end
end

function make_notes(notes :: Vector{Vector{Int}}, by :: Transpose)
    Dict("type" => "lick",
        "notes" => transpose(notes, by))
end

# Generate patterns for a particular voicing.  The name will be
# printed before, and the hint afterwards.  This should be enough for
# the reader to understand how to voice the chord.  The chord start
# can be used to skip part of the progression.
function gen_cycle(name, notes)
    for by in transposes
        emit("$name in $(by.name)", JSON.json(make_notes(notes, by)))
    end
end

println("DELETE FROM probs;")

# Jaki Byard, lick 1
gen_cycle("Byard 1",
    [ [60],[62],[64],[67],[64,65],[67],[63,64],[60],[57],[60] ])
gen_cycle("Byard 2",
    [ [50], [53], [57], [60], [59], [62], [65], [67], [68], [70], [71], [74], [72], [67] ])
gen_cycle("Martin 1",
    [ [63], [64], [69], [67], [64], [60], [65], [63], [64], [60],
      [57], [56], [55], [55] ])
gen_cycle("Martin 2",
    map([ [63],[64],[67],[70],[73],[75],[72] ]) do chord
        chord .- 5
    end)


text:
import Base: ==, convert, promote_rule, length, reverse, show, div

struct Line{T <: Number}
    sxv::T
    syv::T
    exv::T
    eyv::T
    function Line{T}(m::Matrix{T}) where T <: Number
        @assert size(m) == (2, 2) "Invalid values."
        new(m[1, 1], m[2, 1], m[1, 2], m[2, 2])
    end
    Line{T}(sxv::T, syv::T, exv::T, eyv::T) where T <: Number =
        new(sxv, syv, exv, eyv)
end

Line(m::Matrix{T}) where {T <: Number} = Line{T}(m)

sx(l::Line) = l.sxv
sy(l::Line) = l.syv
ex(l::Line) = l.exv
ey(l::Line) = l.eyv

start(l::Line) = (sx(l), sy(l))
start(l::Line, coord::Int) = coord == 1 ? sx(l) : sy(l)
endof(l::Line) = (ex(l), ey(l))
endof(l::Line, coord::Int) = coord == 1 ? ex(l) : ey(l)
point(l::Line, id::Int=1) = id == 1 ? start(l) : endof(l)

matrix(l::Line) = [sx(l) ex(l); sy(l) ey(l)]
coord(l::Line, axis::Int) = axis == 1 ? (sx(l), ex(l)) : (sy(l), ey(l))

xplot(l::Line) = [sx(l), ex(l)]
yplot(l::Line) = [sy(l), ey(l)]

area(::Line{T}) where {T <:Number} = zero(T)

function Line(lx::Number, ly::Number, rx::Number, ry::Number)
    t = promote(lx, ly, rx, ry)
    return Line{eltype(t)}(t...)
end

function convert(::Type{Line{T}}, l::Line{S}) where {T <: Number, S <: Number}
    S === T && return l
    return Line(convert(T, sx(l)), convert(T, sy(l)),
                convert(T, ex(l)), convert(T, ey(l)))
end

promote_rule(::Type{Line{T}}, ::Type{Line{S}}) where {T <: Number, S <: Number} =
    Line{promote_type(T, S)}

show(io::IO, l::Line) =
    print(io, "Line:[$(sx(l)) $(sy(l)) $(ex(l)) $(ey(l))]")

==(l1::Line{T}, l2::Line{T}) where {T <: Number} =
    abs(sx(l1) - sx(l2)) <= pcTol(T) &&
    abs(sy(l1) - sy(l2)) <= pcTol(T) &&
    abs(ex(l1) - ex(l2)) <= pcTol(T) &&
    abs(ey(l1) - ey(l2)) <= pcTol(T)
==(l1::Line, l2::Line) = ==(promote(l1, l2)...)

reverse(l::Line) = Line(ex(l), ey(l), sx(l), sy(l))

axis_parallel(l::Line{T}; dir::Int=1) where {T <: Number} =
    dir == 2 ? isHorizontal(l) : isVertical(l)

"""
```
    isHorizontal(l::Line) -> Bool
    isVertcal(l::Line) -> Bool
```
If the `Line` is horizontal or vertical.
"""
isHorizontal(l::Line) = iszero(sy(l) - ey(l))
isVertical(l::Line)   = iszero(sx(l) - ex(l))

"""
```
    length(l::Line) -> Float64
```
The length of the line segment.
"""
function length(l::Line)
    dx = ex(l) - sx(l)
    dy = ey(l) - sy(l)
    return sqrt(dx*dx + dy*dy)
end

"""
```
    ratio(l1::Line{T}, p::Vector{T}) -> r::Real
```
If `p` is on `l1` it divides the line at ratio `r:(1-r)` else nothing.
"""
function ratio(l::Line{T}, p::Tuple{T, T}) where {T <: Real}
    dv = (ex(l) - sx(l), ey(l) - sy(l))
    sl = start(l)
    dp = (p[1] - sl[1], p[2] - sl[2])
    r, c = !iszero(dv[1]) ? (dp[1] / dv[1], 1) : (dp[2] / dv[2], 2)
    if c == 1
        tp = dv[2]*r + l.syv
        iszero(tp - p[2]) && return r
    else
        iszero(dp[1]) && return r
    end
    return nothing
end

ratio(l::Line{T}, p::Tuple{S, S}) where {T <: Number, S <: Number} =
    (ST = promote_type(S, T);
     ratio(convert(Line{ST}, l), convert(Tuple{ST, ST}, p)))

div(l::Line{T}, r::R) where {T <: Number, R <: Real} =
    (sx(l)*(one(R) - r) + ex(l)*r, sy(l)*(one(R) - r) + ey(l)*r)

"""
```
    intersects(l1::Line{T}, l2::Line{T}) where {T <: Real} -> Bool
```
If `l1` and `l2` intersect each other.
"""
function intersects(l1::Line{T}, l2::Line{T}) where T <: Real
    l = Matrix{Line{T}}(undef, 2, 2)
    l[1, 1] = l[2, 2] = l1
    l[1, 2] = l[2, 1] = l2

    l1l21 = parallelogram_area(start(l1), endof(l1), start(l2))
    l1l22 = parallelogram_area(start(l1), endof(l1), endof(l2))
    l2l11 = parallelogram_area(start(l2), endof(l2), start(l1))
    l2l12 = parallelogram_area(start(l2), endof(l2), endof(l1))
    t = [l1l21 l1l22; l2l11 l2l12]

    for i = 1:2
        for j = 1:2
            if iszero(t[i, j])
                r = ratio(l[i, 1], point(l[i, 2], j))
                r === nothing && continue
                zero(T) <= notvoid(r) <= one(T) && return true
            end
        end
    end
    return t[1, 1]*t[1, 2] < zero(T) && t[2, 1]*t[2, 2] < zero(T)
end

intersects(l1::Line, l2::Line) = intersects(promote(l1, l2)...)

"""
```
    merge_axis_aligned(alines::Vector{Line{T}},
                       axis::Int=1,
                       order::Symbol=:increasing,
                       tol::T=pcTol(T)) -> Vector{Line{T}}
```
Given an array of axis aligned lines, if the line ends touch or have an overlap
the lines are merged into a larger segment. Lines which are not touching the
other lines are left intact.

`order` parameter can be in `:increasing` or `:decreasing` order in the direction
of the axis.

`axis` parameter can be `1` for horizontal lines and `2` for vertical lines.
"""
function merge_axis_aligned(alines::Vector{Line{T}},
                            axis::Int=1,
                            order::Symbol=:increasing,
                            tol::T=pcTol(T)) where {T}
    length(alines) == 0 && return Line{T}[]
    pl = alines[1]
    m = matrix(pl)
    oaxis = axis == 1 ? 2 : 1
    vl = Vector{Line{T}}()
    for i = 2:length(alines)
        l = alines[i]
        if iszero(start(l, oaxis) - start(pl, oaxis), tol)
            if order === :increasing && start(l, axis) - endof(pl, axis) <= tol
                m[axis, 2] = max(endof(l, axis),  endof(pl, axis))
            elseif order === :decreasing &&
                start(pl, axis) - endof(l, axis) <= tol
                m[axis, 1] = min(start(l, axis), start(pl, axis))
            else
                push!(vl, Line{T}(m))
                m = matrix(l)
            end
        else
            push!(vl, Line{T}(m))
            m = matrix(l)
        end
        pl = l
    end
    push!(vl, Line{T}(m))
    return vl
end

function intersect_axis_aligned(hl::Line{T},
                                vl::Line{T}, tol::T) where T <: Number
    x, y  = sx(vl), sy(hl)

    sx(hl) > ex(hl) && (hl = reverse(hl))
    sy(vl) > ey(vl) && (vl = reverse(vl))

    return sx(hl) - tol <= x <= ex(hl) + tol &&
           sy(vl) - tol <= y <= ey(vl) + tol ? T[x, y] : T[]
end

function intersect_axis_aligned(hl::Line{T1},
                                vl::Line{T2},
                                tol::T=pcTol(T)) where {T1 <: Number,
                                                        T2 <: Number,
                                                        T  <: Number}
    ST = promote_type(T1, T2, T)
    return intersect_axis_aligned(convert(Line{ST}, hl),
                                  convert(Line{ST}, vl),
                                  convert(ST, tol))
end

"""
    `isless` function  that can be used to sort horizonal lines in descending
    order (top to bottom).
"""
horiz_desc(l1::Line{T1},
           l2::Line{T2},
           tol::Union{T1, T2}=pcTol(promote_type(T1, T2))) where {T1 <: Number,
                                                                  T2 <: Number} =
    horiz_desc(convert(Line{promote_type(T1, T2)}, l1),
               convert(Line{promote_type(T1, T2)}, l2), tol)

@inline function horiz_desc(l1::Line{T}, l2::Line{T},
                            tol::T=pcTol(T)) where T <: Number
    dy = sy(l1) - sy(l2)
    dy > tol && return true
    return iszero(dy, tol) && sx(l1) - sx(l2) < -tol
end

"""
    `isless` function  that can be used to sort vertical lines in ascending
    order (left to right).
"""
vert_asc(l1::Line{T1},
         l2::Line{T2},
         tol::Union{T1, T2}=pcTol(promote_type(T1, T2))) where {T1 <: Number,
                                                                T2 <: Number} =
    vert_asc(convert(Line{promote_type(T1, T2)}, l1),
             convert(Line{promote_type(T1, T2)}, l2), tol)

@inline function vert_asc(l1::Line{T}, l2::Line{T},
                          tol::T=pcTol(T)) where T <: Number
    dx = sx(l1) - sx(l2)
    dx < -tol && return true
    return iszero(dx, tol) && ey(l1) - ey(l2) > tol
end


text:
# CNN

function get_index(p, u, R, r)
	p*u + R - r
end

@polly function cnn_forward(inp_F, W, out_F,nu,nv)
	nn,nk,np,nq = size(out_F)
	nc,nh,nw = size(inp_F)[2:end]
	nr,ns = size(W)[3:end]

	for n=1:nn, k=1:nk, p=1:np, q=1:nq, c=1:nc, r=1:nr, s=1:ns
		out_F[n,k,p,q] += W[k,c,r,s] * inp_F[n,c,get_index(p,nu,nr,r),get_index(q,nv,ns,s)]
	end

end

@polly function cnn_backward(err_out, W, err_in, nu, nv)
	nn,nk,np,nq = size(err_out)
	nc,nh,nw = size(err_in)[2:end]
	nr,ns = size(W)[3:end]

	for n=1:nn, c=1:nc, h=1:nh, w=1:nw, k=1:nk, r=1:nr, s=1:ns, p=1:np, q=1:nq
		if (nu*p - (h - nr + r)==0) && (nv*q - (w - ns + s)==0)
			err_in[n,c,h,w] += W[k,c,r,s] * err_out[n,k,p,q]
		end
	end

end

let
	nn = 50
	nk = 40
	nc = 75
	nr = 6
	ns = 6
	nw = 50
	nh = 50
	nu = 5
	nv = 5
	np = div((nh - nr),nu) + 1
	nq = div((nw - ns),nv) + 1
	out_F = zeros(Float32,nn,nk,np,nq)
	W = zeros(Float32,nk,nc,nr,ns)
	inp_F = zeros(Float32,nn,nc,nh,nw)
	err_in = zeros(Float32,nn,nc,nh,nw)
	err_out = zeros(Float32,nn,nk,np,nq)

	#init_array

	for a=1:nn, b=1:nk, e=1:np, d=1:nq
		out_F[a,b,e,d] = Float32((a*b)%nn)
		err_out[a,b,e,d] = Float32((e*d)%nk)
	end

	for a=1:nk, b=1:nc, e=1:nr, d=1:ns
		W[a,b,e,d] = Float32(((a*b)%nc) / (10*nc))
	end

	for a=1:nn, b=1:nc, e=1:nh, d=1:nw
		inp_F[a,b,e,d] = Float32((a*b)%nc)
		err_in[a,b,e,d] = Float32((e*d)%nc)
	end

	if length(ARGS)==0 || ARGS[1]=="forward"
		print("Forward pass loaded")
		SUITE["cnn"] =@benchmarkable cnn_forward(inp_F, W, out_F, $nu, $nv) setup = (inp_F = copy($inp_F); W = copy($W); out_F = copy($out_F); err_out = copy($err_out); err_in = copy($err_in))
	else
		print("Backward pass loaded")
		SUITE["cnn"] =@benchmarkable cnn_backward(err_out, W, err_in, $nu, $nv) setup = (inp_F = copy($inp_F); W = copy($W); out_F = copy($out_F); err_out = copy($err_out); err_in = copy($err_in))
	end

end

max_stars_repo_name: akileshbadrinaaraayanan/IITH

max_issues_repo_name: akileshbadrinaaraayanan/IITH

max_forks_repo_name: akileshbadrinaaraayanan/IITH


text:
#### STATE SPACE
function POMDPs.states(pomdp::SingleOCFPOMDP)
    return pomdp.state_space
end

function POMDPs.stateindex(pomdp::SingleOCFPOMDP, s::SingleOCFState)

    if s != pomdp.state_space[end]
        id_tmp = interpolants(pomdp.state_space_grid, [s.ego_y, s.ego_v, s.ped_s, s.ped_T, s.ped_theta, s.ped_v])
        return findmax(id_tmp[1])[1]
    else
        return n_states(pomdp)
    end
end

function POMDPs.n_states(pomdp::SingleOCFPOMDP)
    return length(pomdp.state_space)
end

#### ACTION SPACE
function POMDPs.actions(pomdp::SingleOCFPOMDP)
    return pomdp.action_space

end

function POMDPs.n_actions(pomdp::SingleOCFPOMDP)
  return length(pomdp.action_space)
end

function POMDPs.actionindex(pomdp::SingleOCFPOMDP, a::SingleOCFAction)

    lateral_id = 0;
    if a.lateral_movement > 0
        lateral_id = 0
    elseif a.lateral_movement < 0
        lateral_id = 2
    else
        lateral_id = 1
    end

    if a.acc == 1.
        longitudinal_id = 1
    elseif a.acc == 0.
        longitudinal_id = 2
    elseif a.acc == -1.
        longitudinal_id = 3
    elseif a.acc == -2.
        longitudinal_id = 4
    else
        longitudinal_id = 5
    end

    if ( length(pomdp.lateral_actions) > 1)
        return longitudinal_id + (lateral_id * length(pomdp.longitudinal_actions))
    else
        return longitudinal_id
    end
end

### OBSERVATION SPACE
function POMDPs.observations(pomdp::SingleOCFPOMDP)
    return states(pomdp)
end

function POMDPs.obsindex(pomdp::SingleOCFPOMDP, o::SingleOCFObs)
    return stateindex(pomdp, o)
end

function POMDPs.n_observations(pomdp::SingleOCFPOMDP)
    return n_states(pomdp)
end


text:
function print_matrix( A )
    Nrows = size(A)[1]
    Ncols = size(A)[2]
    for ir = 1:Nrows
        for ic = 1:Ncols
            @printf("%15.8f ", A[ir,ic])
        end
        @printf("\n")
    end
end


text:
module Distance

using DeformableRegistration
using DeformableRegistration.Transformation
using DeformableRegistration.Interpolation
using DeformableRegistration.ImageProcessing
using DeformableRegistration.Types

using Interpolations

export ssdDistance, maskedSsdDistance, ngfDistance, estimateNGFEpsilon
export calculateHessianStaggered
export ssdDistanceArbitraryGrid

include("distances/ssdDistance.jl")
include("distances/maskedSsdDistance.jl")
include("distances/ngfDistance.jl")
include("distances/estimateNGFEpsilon.jl")

end


text:
# set the number of worker threads for managing experiments
num_worker_procs = length(ARGS) == 0 ? 2 : parse(Int64, ARGS[1])
# setting the number of threads to number of CPUs
# this is default behavior, setting it anyway to override if set otherwise
ENV["JULIA_NUM_THREADS"] = Sys.CPU_THREADS
# start the workers on which experiments are loaded
using Distributed
addprocs(num_worker_procs, topology=:master_worker)
w = workers()

#make sure we are able to load everything
@everywhere begin
    Core.println("loading code")
    import Pkg; Pkg.activate(".")
    using DrugEfficacyPredictor
    Core.println("done loading code")
end

# make sure workers now their ids
@everywhere DrugEfficacyPredictor.Utils.init_workers($w)

# start the webservice
DrugEfficacyPredictor.Webservice.start_server(8888)

# TODO make port configurable


text:
test_data_name = "univ_test.pkl";                                                   # test data set name
test_scene_id = 0;                                                                  # test data id
start_time_idx = 103;                                                               # start time index in test data
ego_pos_init_vec = [13., 8.0] .+ [-8.106635, -7.037256];                            # initial ego position [x, y] [m]
ego_pos_goal_vec = [2.5, 2.5] .+ [-8.106635, -7.037256];                            # goal ego position [x, y] [m]
target_speed = 0.7;                                                                 # target speed [m/s]
sim_horizon = 20.0;                                                                 # simulation time horizon [s]


text:
using MCPhylo
using Test

binary_tree = MCPhylo.parsing_newick_string("((raccoon:19.19959,bear:6.80041):0.84600,((sea_lion:11.99700,seal:12.00300):7.52973,((monkey:100.85930,cat:47.14069):20.59201,weasel:18.87953):2.09460):3.87382);")
MCPhylo.number_nodes!(binary_tree)
MCPhylo.set_binary!(binary_tree)

error_tree = MCPhylo.parsing_newick_string("(raccoon:19.19959):0.84600;")
error_tree_not_binary = MCPhylo.parsing_newick_string("((raccoon:19.19959,bear:6.80041):0.84600,((sea_lion:11.99700,seal:12.00300,lizard:5.03):7.52973,((monkey:100.85930,cat:47.14069):20.59201,weasel:18.87953):2.09460):3.87382);")
tree_binary_two = MCPhylo.parsing_newick_string("((raccoon:19.19959,bear:6.80041)50:0.84600,((sea_lion:11.99700,seal:12.00300)100:7.52973,((monkey:100.85930,cat:47.14069)80:20.59201,weasel:18.87953)75:2.09460)50:3.87382,dog:25.46154);")

@test false == MCPhylo.check_binary(error_tree)
@test false == MCPhylo.check_binary(error_tree_not_binary)
@test_throws ErrorException MCPhylo.SPR(error_tree)
@test true == MCPhylo.check_binary(tree_binary_two)
spr_binary = MCPhylo.SPR(binary_tree)

@test length(Set([n.num for n in post_order(spr_binary)])) == length(Set([n.num for n in post_order(binary_tree)]))

@test round(MCPhylo.tree_length(binary_tree);digits=3) == round(MCPhylo.tree_length(spr_binary);digits=3)

@test length(MCPhylo.post_order(spr_binary)) == length(MCPhylo.post_order(binary_tree))

MCPhylo.number_nodes!(tree_binary_two)

spr_binary_two = MCPhylo.SPR(tree_binary_two)

@test length(Set([n.num for n in post_order(spr_binary_two)])) == length(Set([n.num for n in post_order(tree_binary_two)]))

@test round(MCPhylo.tree_length(tree_binary_two);digits=3) == round(MCPhylo.tree_length(spr_binary_two);digits=3)

@test length(MCPhylo.post_order(spr_binary_two)) == length(MCPhylo.post_order(tree_binary_two))

spr_binary_risky = MCPhylo.risky_SPR(binary_tree)

@test length(Set([n.num for n in post_order(spr_binary_risky)])) == length(Set([n.num for n in post_order(binary_tree)]))
@test round(MCPhylo.tree_length(spr_binary_risky);digits=3) == round(MCPhylo.tree_length(spr_binary);digits=3)
@test length(MCPhylo.post_order(spr_binary_risky)) == length(MCPhylo.post_order(binary_tree))


text:
module HttpIO

export HttpFileIO
export GCSFileIO
export GCSClient

import HTTP
import TranscodingStreams
include("PoorGCloudAuth/PoorGCloudAuth.jl")

######## RemoteResource Interface  #######################

abstract type RemoteResource end

function resc_length(x::RemoteResource) error("not implemented") end

function resc_fetch(x::RemoteResource, range::UnitRange{<:Integer}) error("not implemented") end

######## RemoteResourceIO          #######################

## The incomplete, unbuffered IO.
## It only implements TranscodingStreams.readdata! method. TranscodingStreams.NoopStream
## is then used to provide the buffer and related IO methods.

mutable struct DummyRemoteResourceIO{T} <: IO
    resource::T
    offset::Int64
    function DummyRemoteResourceIO(resource::RemoteResource)
        new{typeof(resource)}(resource, 0)
    end
end

function Base.position(io::DummyRemoteResourceIO)
    io.offset
end

function Base.seek(io::DummyRemoteResourceIO, pos::Integer)
    io.offset = pos
    io
end

function Base.seekstart(io::DummyRemoteResourceIO)
    seek(io, 0)
end

function Base.seekend(io::DummyRemoteResourceIO)
    seek(io, resc_length(io))
end

function Base.eof(io::DummyRemoteResourceIO)
    position(io) >= resc_length(io.resource)
end

function Base.bytesavailable(io::DummyRemoteResourceIO)
    return 0
end

function Base.isopen(io::DummyRemoteResourceIO)
    true
end

function Base.close(io::DummyRemoteResourceIO)
    nothing
end

function resc_length(x::DummyRemoteResourceIO)
    resc_length(x.resource)
end

function resc_fetch(x::DummyRemoteResourceIO, range::UnitRange{<:Integer})
    ### TODO: add stats
    resc_fetch(x.resource, range)
end

function TranscodingStreams.readdata!(input::DummyRemoteResourceIO, output::TranscodingStreams.Buffer)
    ntoread::Int = min(TranscodingStreams.marginsize(output), resc_length(input) - position(input))
    ntoread <= 0 && return 0
    range_start = position(input) + 1
    range_stop = position(input) + ntoread
    data::Vector{UInt8} = resc_fetch(input, range_start:range_stop)
    @assert length(data) == ntoread
    GC.@preserve data output unsafe_copyto!(TranscodingStreams.marginptr(output), pointer(data), ntoread)
    TranscodingStreams.supplied!(output, ntoread)
    input.offset += ntoread
    return ntoread
end

### The complete, buffered IO.

mutable struct RemoteResourceIO{T} <: IO
    dummy_io::DummyRemoteResourceIO{T}
    stream::TranscodingStreams.NoopStream{DummyRemoteResourceIO{T}}
    function RemoteResourceIO(resource::RemoteResource; bufsize=1*1024*1024)
        dummy_io = DummyRemoteResourceIO(resource)
        stream = TranscodingStreams.TranscodingStream(TranscodingStreams.Noop(), dummy_io; bufsize = bufsize)
        new{typeof(resource)}(dummy_io, stream)
    end
    function RemoteResourceIO{T}(resource::T; kwargs...) where {T}
        RemoteResourceIO(resource; kwargs...)
    end
end

function Base.parent(x::RemoteResourceIO) x.stream end

function Base.position(x::RemoteResourceIO) position(parent(x)) end
function Base.read(x::RemoteResourceIO) read(parent(x)) end
function Base.read(x::RemoteResourceIO, nb::Integer) read(parent(x), nb) end
#function Base.read(x::RemoteResourceIO, T::Type) read(parent(x), T) end
function Base.read(x::RemoteResourceIO, T::Type{UInt8}) read(parent(x), T) end
function Base.read(x::RemoteResourceIO, T::Type{Char}) read(parent(x), T) end
function Base.seek(x::RemoteResourceIO, args...; kwargs...) seek(parent(x), args...; kwargs...) end
function Base.seekstart(x::RemoteResourceIO, args...; kwargs...) seekstart(parent(x), args...; kwargs...) end
function Base.seekend(x::RemoteResourceIO, args...; kwargs...) seekend(parent(x), args...; kwargs...) end
function Base.bytesavailable(x::RemoteResourceIO) bytesavailable(parent(x)) end
function Base.eof(x::RemoteResourceIO) eof(parent(x)) end
function Base.peek(x::RemoteResourceIO, args...; kwargs...) peek(parent(x), args...; kwargs...) end
function Base.isopen(x::RemoteResourceIO) isopen(parent(x)) end
function Base.close(x::RemoteResourceIO) close(parent(x)) end

include("http.jl")
include("gcs.jl")

end


text:
module Pass
using Cassette
using Core: CodeInfo, SlotNumber, SSAValue

Cassette.@context Ctx
storage = Any[]

function Cassette.execute(ctx::Ctx, f, args...)
    if Cassette.canoverdub(ctx, f, args...)
        _ctx = Cassette.similarcontext(ctx, metadata = storage)
        return Cassette.overdub(_ctx, f, args...)
    else
        return Cassette.fallback(ctx, f, args...)
    end
end

function Cassette.execute(ctx::Ctx, ::typeof(println), args...)
    @info "dalaying a print"
    push!(ctx.metadata, ()->println(args))
    return false
end

# this should be the identity pass that does nothing.
function sliceprintln(::Type{<:Ctx}, s::Type{S}, ir::CodeInfo) where {S}
    @show s
    @info "slicing"
    # Cassette.replace_match!(

    #    Expr(:call, Expr(:nooverdub, s[1].instance), s.),
    #     ir
    # )
    @show ir
    return ir
end

const sliceprintlnpass = Cassette.@pass sliceprintln

a = rand(3)
b = rand(3)
function add(a, b)
    # println("I'm about to add $a + $b")
    c = a + b
    # println("c = $c")
    return c
end
add(a, b)
ctx = Ctx(pass=sliceprintlnpass, metadata = () -> nothing);

result = Cassette.overdub(ctx, add, a, b)

@info "calling the callback"
# callback()
for f in storage
    f()
end

end


text:
module Day7

export day7

using CombinedParsers
import CombinedParsers.Regexp: word, whitespace_maybe

function keepwords(str, n)
    join(split(str, " ")[1:n], " ")
end

function separate(str)
    if str[1:2] == "no"
        (0, "")
    else
        v = split(str, " ")
        (parse(Int, v[1]), join(v[2:3], " "))
    end
end

function parsebags(filename)
    bags = Dict{String, Vector{Tuple{Int, String}}}()
    for line in readlines(filename)
        v = split(line, "contain")
        outer = strip(keepwords(v[1],2))
        bags[outer] = []
        for content in split(v[2], ',')
            content = strip(content)
            push!(bags[outer], separate(content))
        end
    end
    bags
end

function parsebagsreverse(filename)
    bags = Dict{String, Vector{String}}()
    for line in readlines(filename)
        v = split(line, "contain")
        outer = keepwords(v[1],2)
        for content in split(v[2], ',')
            content = strip(content)
            inner = separate(content)[2]
            if !isempty(inner)
                push!(get!(bags, inner, Vector{String}()), outer)
            end
        end
    end
    bags
end

function day7(::Val{:a}, filename)
    target = "shiny gold"
    bags = parsebagsreverse(filename)
    good = Set{String}()
    totry = Set{String}([target])
    finished = Set{String}()
    while !isempty(totry)
        current = pop!(totry)
        push!(finished, current)
        for bag in get(bags, current, [])
            if !in(bag, finished)
                push!(totry, bag)
            end
            push!(good, bag)
        end
    end
    length(good)
end

function day7(::Val{:b}, filename)
    current = "shiny gold"
    bags = parsebags2(filename)
    totry = Set{Tuple{Int, String}}([(1, current)])
    count = 0
    while !isempty(totry)
        (n, current) = pop!(totry)
        count += n
        for (m, bag) in get(bags, current, [])
            push!(totry, (m*n, bag))
        end
    end
    println(count-1)
end

end