text:
# convert padding etc. size to an Int array of the right dimension
function psize(p, x)
  nd = ndims(x)-2
  if isa(p,Number)
    ntuple(_->Int(p), nd)
  elseif length(p)==nd
    tuple(p...)
  else
    throw(DimensionMismatch("psize: $p $nd"))
  end
end

# Type system-level information about convolution dimensions. Critical for things like
# im2col_2d!() to generate efficient code.
struct ConvDims{img, kernel, channels, stride, padding, dilation, flipkernel} end
img_size(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = I

# Calculate the output dimensions of this convolution
function output_size(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F}
    O_w = div(I[1] + P[1] + P[2] - (K[1] - 1) * D[1] - 1, S[1]) + 1
    O_h = div(I[2] + P[3] + P[4] - (K[1] - 1) * D[1] - 1, S[1]) + 1
    return (O_w, O_h)
end
kernel_size(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = K
img_channels(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = C
stride(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = S
padding(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = P
dilation(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = D
flipkernel(c::ConvDims{I,K,C,S,P,D,F}) where {I, K, C, S, P, D, F} = F

function im2col_2d!(img::AbstractArray{T,3}, col::AbstractArray{T,2}, cdims::ConvDims) where T
  width, height = img_size(cdims)
  kernel_w, kernel_h = kernel_size(cdims)
  channels = img_channels(cdims)
  pad_w_lo, pad_w_hi, pad_h_lo, pad_h_hi = padding(cdims)
  dil_w, dil_h = dilation(cdims)
  stride_w, stride_h = stride(cdims)
  width_col, height_col = output_size(cdims)

  if flipkernel(cdims)
    flipk = (w, h) -> (kernel_w - w + 1, kernel_h - h + 1)
  else
    flipk = (w, h) -> (w, h)
  end

  # Reshape col for easy access.
  col_reshaped = reshape(col, (width_col, height_col, kernel_w, kernel_h, channels))

  # Let us first calculate the number of rows/columns within which we must zero out some
  # portion of the image patches we're copying over.  Note the subtractions on the `_hi`
  # variants are due to us needing to account for padding that is completely ignored due
  # to stride/dilation/kernel size combinations.
  spill_w_lo = ceil(Int, pad_w_lo/stride_w)
  spill_w_hi = width_col - div(width + pad_w_lo - (kernel_w - 1)*dil_w, stride_w)
  spill_h_lo = ceil(Int, pad_h_lo/stride_h)
  spill_h_hi = height_col - div(height + pad_h_lo - (kernel_h - 1)*dil_h, stride_h)
  spill_w_hi_abs = width_col - spill_w_hi + 1
  spill_h_hi_abs = height_col - spill_h_hi + 1

  # First, a helper function to project from output (w, h) to input (input_w, input_h)
  project(idx, stride, pad) = (idx - 1)*stride - pad + 1

  # These are the regions we're going to have to run with cognizance of padding
  padded_regions = (
    (1:width_col,                           1:spill_h_lo),
    (1:spill_w_lo,             (spill_h_lo+1):(spill_h_hi_abs-1)),
    (spill_w_hi_abs:width_col, (spill_h_lo+1):(spill_h_hi_abs-1)),
    (1:width_col,              spill_h_hi_abs:height_col),
  )

  # We begin by copying the central region of the image which requires no padding at all.
  # Eliminating the branches of the fully generalized version below gives us a nice
  # speedup on the majority of the data.
  for c in 1:channels
    for kh in 1:kernel_h
      for kw in 1:kernel_w
        for h in (spill_h_lo+1):(height_col - spill_h_hi)
          input_kh = project(h, stride_h, pad_h_lo) + (kh - 1)*dil_h

          @inbounds for w in (spill_w_lo+1):(width_col - spill_w_hi)
            input_kw = project(w, stride_w, pad_w_lo) + (kw - 1)*dil_w
            col_reshaped[w, h, flipk(kw, kh)..., c] = img[input_kw, input_kh, c]
          end
        end
      end
    end
  end

  # For each "padded region", we run the fully general version
  for (w_region, h_region) in padded_regions
    for c in 1:channels
      for kh in 1:kernel_h
        for kw in 1:kernel_w
          @inbounds for h in h_region
            input_kh = project(h, stride_h, pad_h_lo) + (kh - 1)*dil_h

            # If this column is off the edge, then deal with the entire thing
            # in one fell swoop, like a ravenous flock of crows.  CAW CAW.
            if input_kh <= 0 || input_kh > height
              for w in w_region
                col_reshaped[w, h, flipk(kw, kh)..., c] = zero(eltype(col_reshaped))
              end
              continue
            end

            @inbounds for w in w_region
              input_kw = project(w, stride_w, pad_w_lo) + (kw - 1)*dil_w

              # If this pixel is off the edge of the map, clear it out.
              if input_kw <= 0 || input_kw > width
                col_reshaped[w, h, flipk(kw, kh)..., c] = zero(eltype(col_reshaped))
                continue
              end

              # Copy the data over
              col_reshaped[w, h, flipk(kw, kh)..., c] = img[input_kw, input_kh, c]
            end
          end
        end
      end
    end
  end
end

function col2im_2d!(col::AbstractArray{T,2}, img::AbstractArray{T,3}, width::Int, height::Int,
  channels::Int, kernel_w::Int, kernel_h::Int, pad_w::Int, pad_h::Int, stride_w::Int,
  stride_h::Int, dil_w::Int, dil_h::Int, mode::Int) where T

  height_col = div(height + 2pad_h - (kernel_h - 1) * dil_h - 1, stride_h) + 1
  width_col = div(width + 2pad_w - (kernel_w - 1) * dil_w - 1, stride_w) + 1
  channels_col = channels * kernel_h * kernel_w

  fill!(img, 0)
  #pragma omp parallel for
  for c = 1:channels_col
    w_offset = (c - 1) % kernel_w
    h_offset = div(c - 1,  kernel_w) % kernel_h
    c_im = div(c - 1, kernel_h * kernel_w)
    if mode == 0
      w_offset = kernel_w - 1 - w_offset
      h_offset = kernel_h - 1 - h_offset
    end
    for h = 1:height_col, w = 1:width_col
      h_pad = (h - 1) * stride_h - pad_h + h_offset * dil_h
      w_pad = (w - 1) * stride_w - pad_w + w_offset * dil_w
      if h_pad >= 0 && h_pad < height && w_pad >= 0 && w_pad < width
        cval::T = col[((c - 1) * height_col + h - 1) * width_col + w]
        img[(c_im * height + h_pad) * width + w_pad + 1] += cval
      end
    end
  end
end

function im2col_3d!(img::AbstractArray{T,4}, col::AbstractArray{T,2}, width::Int, height::Int, depth::Int,
  channels::Int, kernel_w::Int, kernel_h::Int, kernel_d::Int, pad_w::Int, pad_h::Int, pad_d::Int,
  stride_w::Int, stride_h::Int, stride_d::Int, dil_w::Int, dil_h::Int, dil_d::Int, mode::Int) where T

  height_col = div(height + 2pad_h - (kernel_h - 1) * dil_h - 1, stride_h) + 1
  width_col = div(width + 2pad_w - (kernel_w - 1) * dil_w - 1, stride_w) + 1
  depth_col = div(depth + 2pad_d - (kernel_d - 1) * dil_d - 1, stride_d) + 1
  channels_col = channels * kernel_h * kernel_w * kernel_d

  #pragma omp parallel for
  for c = 1:channels_col
    w_offset = (c - 1) % kernel_w
    h_offset = div(c - 1, kernel_w) % kernel_h
    d_offset = div(c - 1, kernel_w * kernel_h) % kernel_d
    c_im = div(c - 1, kernel_w * kernel_h * kernel_d)
    if mode == 0
      w_offset = kernel_w - 1 - w_offset
      h_offset = kernel_h - 1 - h_offset
      d_offset = kernel_d - 1 - d_offset
    end
    for d = 1:depth_col, h = 1:height_col, w = 1:width_col
      d_pad = (d - 1) * stride_d - pad_d + d_offset * dil_d
      h_pad = (h - 1) * stride_h - pad_h + h_offset * dil_h
      w_pad = (w - 1) * stride_w - pad_w + w_offset * dil_w
      if d_pad >= 0 && d_pad < depth && h_pad >= 0 && h_pad < height &&
        w_pad >= 0 && w_pad < width
        col[(((c - 1) * depth_col + d - 1) * height_col + h - 1) * width_col + w] =
    	    img[((c_im * depth + d_pad) * height + h_pad) * width + w_pad + 1]
    	else
    	  col[(((c - 1) * depth_col + d - 1) * height_col + h - 1) * width_col + w] = 0
      end
    end
  end
end

function col2im_3d!(col::AbstractArray{T,2}, img::AbstractArray{T,4}, width::Int, height::Int,
  depth::Int, channels::Int, kernel_w::Int, kernel_h::Int, kernel_d::Int,
  pad_w::Int, pad_h::Int, pad_d::Int, stride_w::Int, stride_h::Int, stride_d::Int,
  dil_w::Int, dil_h::Int, dil_d::Int, mode::Int) where T

  height_col = div(height + 2pad_h - (kernel_h - 1) * dil_h - 1, stride_h) + 1
  width_col = div(width + 2pad_w - (kernel_w - 1) * dil_w - 1, stride_w) + 1
  depth_col = div(depth + 2pad_d - (kernel_d - 1) * dil_d - 1, stride_d) + 1
  channels_col = channels * kernel_h * kernel_w * kernel_d

  fill!(img, 0)
  #pragma omp parallel for
  for c = 1:channels_col
    w_offset = (c - 1) % kernel_w;
    h_offset = div(c - 1, kernel_w) % kernel_h
    d_offset = div(c - 1, kernel_w * kernel_h) % kernel_d
    c_im = div(c - 1, kernel_h * kernel_w * kernel_d)

    if mode == 0
      w_offset = kernel_w - 1 - w_offset
      h_offset = kernel_h - 1 - h_offset
      d_offset = kernel_d - 1 - d_offset
    end

    for d = 1:depth_col, h = 1:height_col, w = 1:width_col
      d_pad = (d - 1) * stride_d - pad_d + d_offset * dil_d
    	h_pad = (h - 1) * stride_h - pad_h + h_offset * dil_h
    	w_pad = (w - 1) * stride_w - pad_w + w_offset * dil_w
    	if h_pad >= 0 && h_pad < height && w_pad >= 0 && w_pad < width &&
        d_pad >= 0 && d_pad < depth
    	  cval::T = col[(((c - 1) * depth_col + d - 1) * height_col + h - 1) * width_col + w]
    	  iidx = ((c_im * depth + d_pad) * height + h_pad) * width + w_pad + 1
              #pragma omp atomic
    	  img[iidx] += cval
    	end
    end
  end
end

function dilation_dims(w, dilation = 1)
  N = ndims(w)
  dims_w = size(w)
  dil = psize(dilation, w)
  ntuple(N) do i
    if i < N - 1
      (dims_w[i] - 1) * dil[i] + 1
    else
      dims_w[i]
    end
  end
end

function im2col_dims(w,y)
    N = ndims(y)
    r,c = 1,1
    for i=1:N-2
        r *= size(y,i)
        c *= size(w,i)
    end
    c *= size(w,N-1)
    return (r, c)
end

function im2col_dims(w::NTuple{4, Int}, y)
    N = ndims(y)
    r,c = 1,1
    for i=1:N-2
        r *= size(y,i)
        c *= w[i]
    end
    c *= w[N-1]
    return (r, c)
end

function depthwiseconv2d!(y::AbstractArray{T,4}, x::AbstractArray{T,4}, w::AbstractArray{T,4};
                  padding = 0, stride = 1, mode = 0, alpha = T(1)) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,Cm,Cw = size(w) # Cm = Channel Multiplier
    @assert Cx == Cw DimensionMismatch()
    Wy,Hy,Cy,Ny = size(y) # Cy = Cw * Cm
    dims_w = (Ww,Hw,Cw,Cm*Cw)
    x2dims = im2col_dims(dims_w,y)
    x2 = similar(x, x2dims)
    (p1,p2) = psize(padding,x)
    (s1,s2) = psize(stride,x)
    M,N,K,Y = Wy*Hy,Cm,Ww*Hw,Wy*Hy*Cm
    yidx = 1
    @inbounds for i in 1:Nx
        im2col2d!(dims_w, x, x2, i, p1, p2, s1, s2, mode)
        @inbounds for j in 1:Cx
            gemm!('N','N',M,N,K,alpha,pointer(x2,(j-1)*M*K+1),pointer(w,(j-1)*K*N+1),T(0),pointer(y,yidx))
            yidx += Y
        end
    end
    return y
end

function depthwiseconv2d_grad_w!(dw::AbstractArray{T,4}, x::AbstractArray{T,4}, w::AbstractArray{T,4}, dy::AbstractArray{T,4};
        padding=0, stride=1, mode=0, alpha=1) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,Cm,Cw = size(w) # Cm = Channel Multiplier
    @assert Cx == Cw DimensionMismatch()
    Wy,Hy,Cy,Ny = size(dy) # Cy = Cw * Cm
    @assert Cy == Cw * Cm DimensionMismatch()
    dims_w = (Ww,Hw,Cw,Cm*Cw)
    x2dims = im2col_dims(dims_w,dy)
    x2 = similar(x, x2dims)
    (p1,p2) = psize(padding,x)
    (s1,s2) = psize(stride,x)
    M,N,K,Y,W = Ww*Hw,Cm,Wy*Hy,Wy*Hy*Cm*Cx,Ww*Hw*Cm
    alpha,beta = T(alpha),T(1)
    dyidx = 1
    @inbounds for i in 1:Nx
        im2col2d!(dims_w, x, x2, i, p1, p2, s1, s2, mode)
        dwidx = 1
        @inbounds for j in 1:Cx
            gemm!('T','N',M,N,K,alpha,pointer(x2,(j-1)*M*K+1),pointer(dy,dyidx+(j-1)*K*N),beta,pointer(dw,dwidx))
            dwidx += W
        end
        dyidx += Y
    end
    return dw
end

function depthwiseconv2d_grad_x!(dx::AbstractArray{T,4}, x::AbstractArray{T,4}, w::AbstractArray{T,4}, dy::AbstractArray{T,4};
                   padding=0, stride=1, mode=0, alpha=1) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,Cm,Cw = size(w) # Cm = Channel Multiplier
    @assert Cx == Cw DimensionMismatch()
    Wy,Hy,Cy,Ny = size(dy) # Cy = Cw * Cm
    @assert Cy == Cw * Cm DimensionMismatch()
    dims_w = (Ww,Hw,Cw,Cm*Cw)
    x2dims = im2col_dims(dims_w,dy)
    x2 = similar(x, x2dims)
    M,N,K,Y,W = Wy*Hy,Ww*Hw,Cm,Wy*Hy*Cm*Cx,Ww*Hw*Cm
    alpha,beta = T(alpha),T(0)
    (p1,p2) = psize(padding,x)
    (s1,s2) = psize(stride,x)
    dyidx = 1
    @inbounds for i in 1:Nx
        @inbounds for j in 1:Cx
            gemm!('N','T',M,N,K,alpha,pointer(dy,dyidx+(j-1)*K*M),pointer(w,(j-1)*K*N+1),beta,pointer(x2,(j-1)*M*N+1))
        end
        col2im2d!(dims_w,dx,x2,i,p1,p2,s1,s2,mode)
        dyidx += Y
    end
    return dx
end

function conv2d!(y::AbstractArray{T,4}, x::AbstractArray{T,4}, w::AbstractArray{T,4},
                 cdims::ConvDims; alpha=T(1)) where T
    Wx, Hx = img_size(cdims)
    Ww, Hw = kernel_size(cdims)
    Wy, Hy = output_size(cdims)
    Cx = img_channels(cdims)
    M, N, K, Y = Wy*Hy, size(y,3), prod(size(w)[1:3]), prod(size(y)[1:3])

    x2 = similar(x, im2col_dims(w, y))
    @inbounds for n in 1:size(x,4)
        im2col_2d!(view(x, :, :, :, n), x2, cdims)
        gemm!('N','N',M,N,K,alpha,pointer(x2),pointer(w),T(0),pointer(y,(n - 1)*Y + 1))
    end
    return y
end

function conv2d!(y::AbstractArray{T,4}, x::AbstractArray{T,4}, w::AbstractArray{T,4};
               padding=0, stride=1, dilation=1, mode=0, alpha=T(1)) where T
    if mode != 0 && mode != 1
        throw(ArgumentError("conv2d only supports mode=0 or 1."))
    end
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,C1,C2 = size(w)

    # Check that the number of channels in `x` matches the number of channels in each
    # kernel of `w`.  IF it doesn't, throw a DimensionMismatch()
    if Cx != C1
        throw(DimensionMismatch())
    end
    (p1,p2) = psize(padding,x)
    (s1,s2) = psize(stride,x)
    (d1,d2) = psize(dilation, x)

    cdims = ConvDims{(Wx,Hx),(Ww,Hw),Cx,(s1,s2),(p1,p1,p2,p2),(d1,d2), mode == 0}()
    return conv2d!(y, x, w, cdims; alpha=alpha)
end

function conv2d_grad_w!(dw::AbstractArray{T,4}, x::AbstractArray{T,4}, dy::AbstractArray{T,4};
                   padding=0, stride=1, dilation=1, mode=0, alpha=1) where T
    # dw = x'*dy
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,C1,C2 = size(dw)
    Wy,Hy,Cy,Ny = size(dy)
    # if mode != 0 && mode != 1; throw(ArgumentError("conv2d only supports mode=0 or 1.")); end
    @assert Cx==C1 && Cy==C2 && Ny==Nx
    x2dims = im2col_dims(dw,dy)
    x2 = similar(x, x2dims)
    # op(A) is an m-by-k matrix, op(B) is a k-by-n matrix, C is an m-by-n matrix.
    Y,M,N,K = Wy*Hy*Cy,Ww*Hw*Cx,Cy,Wy*Hy
    alpha,beta = T(alpha),T(1)
    (p1,p2) = psize(padding,x)
    (s1,s2) = psize(stride,x)
    (d1,d2) = psize(dilation,x)
    dyi = 1
    @inbounds for n in 1:Nx
        im2col2d!(dw, x, x2, n, p1, p2, s1, s2, d1, d2, mode)
        gemm!('T','N',M,N,K,alpha,pointer(x2),pointer(dy,dyi),beta,pointer(dw))
        dyi += Y
    end
    return dw
end

function conv2d_grad_x!(dx::AbstractArray{T,4}, w::AbstractArray{T,4}, dy::AbstractArray{T,4};
                   padding=0, stride=1, dilation=1, mode=0, alpha=1) where T
    # dx = dy*w'
    Wx,Hx,Cx,Nx = size(dx)
    Ww,Hw,C1,C2 = size(w)
    Wy,Hy,Cy,Ny = size(dy)
    # if mode != 0 && mode != 1; throw(ArgumentError("conv2d only supports mode=0 or 1.")); end
    @assert Cx==C1 && Cy==C2 && Ny==Nx
    x2dims = im2col_dims(w,dy)
    x2 = similar(dx, x2dims)
    # op(A) is an m-by-k matrix, op(B) is a k-by-n matrix, C is an m-by-n matrix.
    Y,M,N,K = Wy*Hy*Cy,Wy*Hy,Ww*Hw*Cx,Cy
    alpha,beta = T(alpha),T(0)
    (p1,p2) = psize(padding,dx)
    (s1,s2) = psize(stride,dx)
    (d1,d2) = psize(dilation,dx)
    dyi = 1
    @inbounds for n in 1:Nx
        gemm!('N','T',M,N,K,alpha,pointer(dy,dyi),pointer(w),beta,pointer(x2))
        col2im2d!(w,dx,x2,n,p1,p2,s1,s2,d1,d2,mode)
        dyi += Y
    end
    return dx
end

function im2col2d!(w::NTuple{4,Int}, x::AbstractArray{T,4}, x2::AbstractArray{T,2},
                 n::Int, p1::Int, p2::Int, s1::Int, s2::Int, mode::Int) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,C1,C2 = w
    xn = view(x, :, :, :, n)
    cdims = ConvDims{(Wx,Hx),(Ww,Hw),Cx,(s1,s2),(p1,p1,p2,p2),(1,1), mode == 0}()
    im2col_2d!(xn,x2,cdims)
    return x2
end

function im2col2d!(w::AbstractArray{T,4}, x::AbstractArray{T,4}, x2::AbstractArray{T,2},
                 n::Int, p1::Int, p2::Int, s1::Int, s2::Int, d1::Int, d2::Int, mode::Int) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,C1,C2 = size(w)
    xn = view(x, :, :, :, n)
    cdims = ConvDims{(Wx,Hx),(Ww,Hw),Cx,(s1,s2),(p1,p1,p2,p2),(d1,d2), mode == 0}()
    im2col_2d!(xn,x2,cdims)
    return x2
end

function col2im2d!(w::NTuple{4,Int}, x::AbstractArray{T,4}, x2::AbstractArray{T,2},
                 n::Int, p1::Int, p2::Int, s1::Int, s2::Int, mode::Int) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,C1,C2 = w
    xn = view(x, :, :, :, n)
    col2im_2d!(x2,xn,Wx,Hx,Cx,Ww,Hw,p1,p2,s1,s2,1,1,mode)
    return x
end

function col2im2d!(w::AbstractArray{T,4}, x::AbstractArray{T,4}, x2::AbstractArray{T,2},
                 n::Int, p1::Int, p2::Int, s1::Int, s2::Int, d1::Int, d2::Int, mode::Int) where T
    Wx,Hx,Cx,Nx = size(x)
    Ww,Hw,C1,C2 = size(w)
    xn = view(x, :, :, :, n)
    col2im_2d!(x2,xn,Wx,Hx,Cx,Ww,Hw,p1,p2,s1,s2,d1,d2,mode)
    return x
end

function conv3d!(y::AbstractArray{T,5}, x::AbstractArray{T,5}, w::AbstractArray{T,5};
               padding=0, stride=1, dilation = 1, mode=0, alpha=T(1)) where T
    if mode != 0 && mode != 1; throw(ArgumentError("conv3d only supports mode=0 or 1.")); end
    Wx,Hx,Dx,Cx,Nx = size(x)
    Ww,Hw,Dw,C1,C2 = size(w)
    if Cx!=C1; throw(DimensionMismatch()); end
    Wy,Hy,Dy,Cy,Ny = size(y)
    # @assert Cy==C2 && Ny==Nx
    x2dims = im2col_dims(w,y)
    x2 = similar(x, x2dims)
    (p1,p2,p3) = psize(padding,x)
    (s1,s2,s3) = psize(stride,x)
    (d1,d2,d3) = psize(dilation,x)
    M,N,K,Y = Wy*Hy*Dy,Cy,Ww*Hw*Dw*Cx,Wy*Hy*Dy*Cy
    yidx = 1
    W = reshape(w, (size(w, 1),:,C1,C2))
    @inbounds for n in 1:Nx
        im2col3d!(w, x, x2, n, p1, p2, p3, s1, s2, s3, d1, d2, d3, mode)
        gemm!('N','N',M,N,K,alpha,pointer(x2),pointer(W),T(0),pointer(y,yidx))
        yidx += Y
    end
    return y
end

function conv3d_grad_w!(dw::AbstractArray{T,5}, x::AbstractArray{T,5}, dy::AbstractArray{T,5};
                   padding=0, stride=1, dilation = 1, mode=0, alpha=1) where T
    # dw = x'*dy
    Wx,Hx,Dx,Cx,Nx = size(x)
    Ww,Hw,Dw,C1,C2 = size(dw)
    Wy,Hy,Dy,Cy,Ny = size(dy)
    # if mode != 0 && mode != 1; throw(ArgumentError("conv2d only supports mode=0 or 1.")); end
    @assert Cx==C1 && Cy==C2 && Ny==Nx
    x2dims = im2col_dims(dw,dy)
    x2 = similar(x, x2dims)
    # op(A) is an m-by-k matrix, op(B) is a k-by-n matrix, C is an m-by-n matrix.
    Y,M,N,K = Wy*Hy*Dy*Cy,Ww*Hw*Dw*Cx,Cy,Wy*Hy*Dy
    alpha,beta = T(alpha),T(1)
    (p1,p2,p3) = psize(padding,x)
    (s1,s2,s3) = psize(stride,x)
    (d1,d2,d3) = psize(dilation,x)
    dyi = 1
    @inbounds for n in 1:Nx
        im2col3d!(dw, x, x2, n, p1, p2, p3, s1, s2, s3, d1, d2, d3, mode)
        gemm!('T','N',M,N,K,alpha,pointer(x2),pointer(dy,dyi),beta,pointer(dw))
        dyi += Y
    end
    return dw
end

function conv3d_grad_x!(dx::AbstractArray{T,5}, w::AbstractArray{T,5}, dy::AbstractArray{T,5};
                   padding=0, stride=1, dilation = 1, mode=0, alpha=1) where T
    # dx = dy*w'
    Wx,Hx,Dx,Cx,Nx = size(dx)
    Ww,Hw,Dw,C1,C2 = size(w)
    Wy,Hy,Dy,Cy,Ny = size(dy)
    # if mode != 0 && mode != 1; throw(ArgumentError("conv2d only supports mode=0 or 1.")); end
    @assert Cx==C1 && Cy==C2 && Ny==Nx
    x2dims = im2col_dims(w,dy)
    x2 = similar(dx, x2dims)
    # op(A) is an m-by-k matrix, op(B) is a k-by-n matrix, C is an m-by-n matrix.
    Y,M,N,K = Wy*Hy*Dy*Cy,Wy*Hy*Dy,Ww*Hw*Dw*Cx,Cy
    alpha,beta = T(alpha),T(0)
    (p1,p2,p3) = psize(padding,dx)
    (s1,s2,s3) = psize(stride,dx)
    (d1,d2,d3) = psize(dilation,dx)
    dyi = 1
    @inbounds for n in 1:Nx
        gemm!('N','T',M,N,K,alpha,pointer(dy,dyi),pointer(w),beta,pointer(x2))
        col2im3d!(w,dx,x2,n,p1,p2,p3,s1,s2,s3,d1,d2,d3,mode)
        dyi += Y
    end
    return dx
end

function im2col3d!(w::AbstractArray{T,5}, x::AbstractArray{T,5}, x2::AbstractArray{T,2},
                 n::Int, p1::Int, p2::Int, p3::Int, s1::Int, s2::Int,
                 s3::Int, d1::Int, d2::Int, d3::Int, mode::Int) where T
    Wx,Hx,Dx,Cx,Nx = size(x)
    Ww,Hw,Dw,C1,C2 = size(w)
    xn = view(x, :, :, :, :, n)
    im2col_3d!(xn,x2,Wx,Hx,Dx,Cx,Ww,Hw,Dw,p1,p2,p3,s1,s2,s3,d1,d2,d3,mode)
    return x2
end

function col2im3d!(w::AbstractArray{T,5}, x::AbstractArray{T,5}, x2::AbstractArray{T,2},
                 n::Int, p1::Int, p2::Int, p3::Int, s1::Int, s2::Int,
                 s3::Int, d1::Int, d2::Int, d3::Int, mode::Int) where T
    Wx,Hx,Dx,Cx,Nx = size(x)
    Ww,Hw,Dw,C1,C2 = size(w)
    xn = view(x, :, :, :, :, n)
    col2im_3d!(x2,xn,Wx,Hx,Dx,Cx,Ww,Hw,Dw,p1,p2,p3,s1,s2,s3,d1,d2,d3,mode)
    return x
end


text:
__precompile__(false)

module LoggerManager

using Dates

using ..TimeManager

export LOGGER_OUT
export LOGGER_ERROR

#export printf

#export print
#export @printf
#export println
#export info
#export warn
#export error

LOGGER_OUT = "out.log"
LOGGER_ERROR = "error.log"

function __init__()
  if !isdir("logs") mkpath("logs") end
  global LOGGER_OUT = "logs/out-"*string(Dates.today())*".log"
  global LOGGER_ERROR = "logs/error-"*string(Dates.today())*".log"
	open(LOGGER_OUT, "w+")
	open(LOGGER_ERROR, "w+")
end

begin print(xs...;stream=stdout) = open(f -> (Base.print(f, xs...); Base.print(stream, xs...)), LOGGER_OUT, "a+") end
macro printf(stream=:STDOUT,xs...) open(f -> (:(Base.@printf(f, $xs...)); :(Base.@printf(stream, $xs...))),LOGGER_OUT, "a+") end
begin println(xs...;stream=stdout) = open(f -> (Base.println(f, TimeManager.programTimeStr(), " ", xs...); Base.println(stream, xs...)), LOGGER_OUT, "a+") end

begin info(xs...) = open(f -> (Base.println(f, TimeManager.programTimeStr(), " INFO: ", xs...); Base.println("INFO: ",xs...;stream=stdout)), LOGGER_OUT, "a+") end
begin error(xs...) = open(f -> (Base.println(f, TimeManager.programTimeStr()," ERROR: ",xs...); error(stderr,xs...)), LOGGER_ERROR, "a+") end
begin warn(xs...) = open(f -> (Base.println(f, TimeManager.programTimeStr(), " WARNING: ",xs...); Base.println("WARNING: ",xs...;stream=stderr)), LOGGER_ERROR, "a+") end
begin debug(xs...) = open(f -> (Base.println(f, TimeManager.programTimeStr(), " DEBUG: ",xs...); Base.println("DEBUG: ",xs...;stream=stderr)), LOGGER_ERROR, "a+") end

function logException(ex::Exception, title="")
	time=TimeManager.programTimeStr()
  m=msg(:Exception, title, "", "See '$LOGGER_ERROR' for more info.")
	open(f -> println(f, m), LOGGER_OUT, "a+")
	open(function(f)
			Base.print(f, msg(:Exception, title, "", ":"))
			Base.showerror(f, ex, catch_backtrace())
			Base.println(f, "\n----------")
	end, LOGGER_ERROR, "a+")

  Base.println(stderr, "\x1b[91m", m, "\x1b[0m")
  Base.print("Program will close in 3 sec")
  for i=1:3 Libc.systemsleep(1); Base.print(".") end
  Base.println()
  exit()
end

log(title, f::Function, args...) = try; f(args...); catch err; logException(err,title); end

function msg(args... ;time=true, name="", mode="", title="", lineBreak=true)
  m=""
  if time m *= TimeManager.programTimeStr()*" " end
  if name!="" m *= string("(",name,") ") end
  if mode!="" m *= string("[",mode,"] ") end
  if title!="" m *= string("'",title,"' ") end
  m *= string(args...)
  if lineBreak m *= "\n" end
  m
end

end #LoggerManager

ext: jl

max_stars_repo_name: Gilga/JuliaOpenGL

max_issues_repo_name: Gilga/JuliaOpenGL

max_forks_repo_name: Gilga/JuliaOpenGL


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

module ForceLatestCompatibleVersionTests

import ..Pkg # ensure we are using the correct Pkg
import ..Utils
using Test

get_exception_message(ex::Pkg.Resolve.ResolverError) = ex.msg

function get_exception_and_message(f::Function)
    ex = try
        f()
    catch ex
        ex
    end
    msg = get_exception_message(ex)
    return ex, msg
end

const exception_type_1 = Pkg.Resolve.ResolverError
const message_1 = "Unsatisfiable requirements detected for package"
const message_2 = "Dependency does not have a [compat] entry"

const test_package_parent_dir = joinpath(
    @__DIR__,
    "test_packages",
    "force_latest_compatible_version",
)

@testset "check_force_latest_compatible_version" begin
    @testset "get_earliest_backwards_compatible_version" begin
        @test Pkg.Operations.get_earliest_backwards_compatible_version(v"1.2.3") == v"1.0.0"
        @test Pkg.Operations.get_earliest_backwards_compatible_version(v"0.2.3") == v"0.2.0"
        @test Pkg.Operations.get_earliest_backwards_compatible_version(v"0.0.3") == v"0.0.3"
    end

    @testset "`force_latest_compatible_version` kwarg to `Pkg.test`" begin
        @testset "OldOnly1: `SomePkg = \"=0.1.0\"`" begin
            mktempdir() do tmp_dir
                test_package = joinpath(tmp_dir, "OldOnly1")
                cp(joinpath(test_package_parent_dir, "OldOnly1"), test_package; force = true)
                Utils.isolate(loaded_depot = true) do
                    Pkg.activate(test_package)
                    Pkg.instantiate()
                    Pkg.build()

                    for force_latest_compatible_version in [false, true]
                        @testset "default value of `allow_earlier_backwards_compatible_versions`" begin
                            @test(
                                Pkg.test(;
                                    force_latest_compatible_version,
                                ) == nothing
                            )
                        end

                        @testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
                            for allow_earlier_backwards_compatible_versions in [false, true]
                                @test(
                                    Pkg.test(;
                                        force_latest_compatible_version,
                                        allow_earlier_backwards_compatible_versions,
                                    ) == nothing
                                )
                            end
                        end
                    end
                end
            end
        end

        @testset "OldOnly2: `SomePkg = \"0.1\"`" begin
            mktempdir() do tmp_dir
                test_package = joinpath(tmp_dir, "OldOnly2")
                cp(joinpath(test_package_parent_dir, "OldOnly2"), test_package; force = true)
                Utils.isolate(loaded_depot = true) do
                    Pkg.activate(test_package)
                    Pkg.instantiate()
                    Pkg.build()

                    @testset "default value of `allow_earlier_backwards_compatible_versions`" begin
                        for force_latest_compatible_version in [false, true]
                            @test(
                                Pkg.test(;
                                    force_latest_compatible_version,
                                ) == nothing
                            )
                        end
                    end

                    @testset "`allow_earlier_backwards_compatible_versions` = false" begin
                        @test(
                            Pkg.test(;
                                force_latest_compatible_version = false,
                                allow_earlier_backwards_compatible_versions = false,
                            ) == nothing
                        )
                        @test_throws(
                            exception_type_1,
                            Pkg.test(;
                                force_latest_compatible_version = true,
                                allow_earlier_backwards_compatible_versions = false,
                            ),
                        )
                        let
                            f = function ()
                                Pkg.test(;
                                    force_latest_compatible_version = true,
                                    allow_earlier_backwards_compatible_versions = false,
                                )
                            end
                            ex, msg = get_exception_and_message(f)
                            @test ex isa exception_type_1
                            @test occursin(message_1, msg)
                        end
                    end

                    @testset "`allow_earlier_backwards_compatible_versions` = true" begin
                        for force_latest_compatible_version in [false, true]
                            @test(
                                Pkg.test(;
                                    force_latest_compatible_version,
                                    allow_earlier_backwards_compatible_versions = true,
                                ) == nothing
                            )
                        end
                    end
                end
            end
        end

        @testset "BothOldAndNew: `SomePkg = \"0.1, 0.2\"`" begin
            mktempdir() do tmp_dir
                test_package = joinpath(tmp_dir, "BothOldAndNew")
                cp(joinpath(test_package_parent_dir, "BothOldAndNew"), test_package; force = true)
                Utils.isolate(loaded_depot = true) do
                    Pkg.activate(test_package)
                    Pkg.instantiate()
                    Pkg.build()

                    @testset "default value of `allow_earlier_backwards_compatible_versions`" begin
                        @test(
                            Pkg.test(;
                                force_latest_compatible_version = false,
                            ) == nothing
                        )
                        @test_throws(
                            exception_type_1,
                            Pkg.test(;
                                force_latest_compatible_version = true,
                            ),
                        )
                        let
                            f = function ()
                                Pkg.test(;
                                    force_latest_compatible_version = true,
                                )
                            end
                            ex, msg = get_exception_and_message(f)
                            @test ex isa exception_type_1
                            @test occursin(message_1, msg)
                        end
                    end

                    @testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
                        for allow_earlier_backwards_compatible_versions in [false, true]
                            @test(
                                Pkg.test(;
                                    force_latest_compatible_version = false,
                                    allow_earlier_backwards_compatible_versions,
                                ) == nothing
                            )
                            @test_throws(
                                exception_type_1,
                                Pkg.test(;
                                    force_latest_compatible_version = true,
                                    allow_earlier_backwards_compatible_versions,
                                ),
                            )
                            let
                                f = function ()
                                    Pkg.test(;
                                        force_latest_compatible_version = true,
                                        allow_earlier_backwards_compatible_versions,
                                    )
                                end
                                ex, msg = get_exception_and_message(f)
                                @test ex isa exception_type_1
                                @test occursin(message_1, msg)
                            end
                        end
                    end
                end
            end
        end

        @testset "NewOnly: `SomePkg = \"0.2\"`" begin
            mktempdir() do tmp_dir
                test_package = joinpath(tmp_dir, "NewOnly")
                cp(joinpath(test_package_parent_dir, "NewOnly"), test_package; force = true)
                Utils.isolate(loaded_depot = true) do
                    Pkg.activate(test_package)
                    Pkg.instantiate()
                    Pkg.build()

                    for force_latest_compatible_version in [false, true]
                        @testset "default value of `allow_earlier_backwards_compatible_versions`" begin
                            @test_throws(
                                exception_type_1,
                                Pkg.test(;
                                    force_latest_compatible_version,
                                ),
                            )
                            let
                                f = function ()
                                    Pkg.test(;
                                        force_latest_compatible_version,
                                    )
                                end
                                ex, msg = get_exception_and_message(f)
                                @test ex isa exception_type_1
                                @test occursin(message_1, msg)
                            end
                        end

                        @testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
                            for allow_earlier_backwards_compatible_versions in [false, true]
                                @test_throws(
                                    exception_type_1,
                                    Pkg.test(;
                                        force_latest_compatible_version,
                                        allow_earlier_backwards_compatible_versions,
                                    ),
                                )
                                let
                                    f = function ()
                                        Pkg.test(;
                                            force_latest_compatible_version,
                                            allow_earlier_backwards_compatible_versions,
                                        )
                                    end
                                    ex, msg = get_exception_and_message(f)
                                    @test ex isa exception_type_1
                                    @test occursin(message_1, msg)
                                end
                            end
                        end
                    end
                end
            end
        end

        @testset "DirectDepWithoutCompatEntry" begin
            mktempdir() do tmp_dir
                test_package = joinpath(tmp_dir, "DirectDepWithoutCompatEntry")
                cp(joinpath(test_package_parent_dir, "DirectDepWithoutCompatEntry"), test_package; force = true)

                # Because this test involves instantiating a project that has
                # a direct dependency that does not have a `[compat]` entry,
                # it is possible that future commits to the General registry
                # will break this test. Therefore, we intentionally run this
                # test using a fixed version of the General registry.
                registry_url = "https://github.com/JuliaRegistries/General.git"
                registry_commit = "982ab1618f87a7dc69c3617e124a6ad83a22b1c5"
                Utils.isolate_and_pin_registry(; registry_url, registry_commit) do
                    Pkg.activate(test_package)
                    Pkg.instantiate()
                    Pkg.build()

                    @testset "force_latest_compatible_version = false" begin
                        @testset "default value of `allow_earlier_backwards_compatible_versions`" begin
                            @test(
                                Pkg.test(;
                                    force_latest_compatible_version = false,
                                ) == nothing
                            )
                        end

                        @testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
                            for allow_earlier_backwards_compatible_versions in [false, true]
                                @test(
                                    Pkg.test(;
                                        force_latest_compatible_version = false,
                                        allow_earlier_backwards_compatible_versions,
                                    ) == nothing
                                )
                            end
                        end
                    end

                    @testset "force_latest_compatible_version = true" begin
                        @testset "default value of `allow_earlier_backwards_compatible_versions`" begin
                            @test(
                                Pkg.test(;
                                    force_latest_compatible_version = true,
                                ) == nothing
                            )
                            @test_logs(
                                (:warn, message_2),
                                match_mode=:any,
                                Pkg.test(;
                                    force_latest_compatible_version = true,
                                ),
                            )
                        end

                        @testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
                            for allow_earlier_backwards_compatible_versions in [false, true]
                                @test(
                                    Pkg.test(;
                                        force_latest_compatible_version = true,
                                        allow_earlier_backwards_compatible_versions,
                                    ) == nothing
                                )
                                @test_logs(
                                    (:warn, message_2),
                                    match_mode=:any,
                                    Pkg.test(;
                                        force_latest_compatible_version = true,
                                        allow_earlier_backwards_compatible_versions,
                                    ),
                                )
                            end
                        end
                    end
                end
            end
        end
    end
end

end # module


text:
# Undergraduate Student: Arturo Burgos
# Professor: João Rodrigo Andrade
# Federal University of Uberlândia - UFU, Fluid Mechanics Laboratory - MFLab, Block 5P, Uberlândia, MG, Brazil

# Fourth exercise: Solving a Linear System --> ax = b

using Printf # Just the same as import a module
using Statistics
using LinearAlgebra # Import this module to use the norm function
using CPUTime

n = 324
k = Int32(sqrt(324))

# Here I set the A matrix

a = rand([0,0],(n,n))

for i = 1:n
  for j = 1:n
    if i==j

      a[i,j] = -4

      elseif i == (j-3) || i == (j+3)

        a[i,j] = 1

      elseif (mod(i,3) != 0 && j == (i+1))

        a[i,j] = 1

      elseif (mod(i,3) != 1 && j == (i-1))

        a[i,j] = 1

      end
  end
end

# Here I set the b matrix

b = ones(n)

for i = 1:k
  if  i < k

    b[i] = -50

  else

    b[i] = -150

  end
end

for i = k+1:n-k
  if i%k != 0

    b[i] = 0

  else

    b[i] = -100

  end
end

for i = n-k+1:n
  if  i < n

    b[i] = -50

  else

    b[i] = -150

  end
end

println("\n")

println("The B matrix is: ")
println(b)

R = zeros(n)

function  linearsystem(a,b,x_k1)
  # Initial x_k and x_k1 value
  x_k = zeros(n)

  x_k1 = ones(n)

  # Here I set the tolerance

  e = 1e-9

  # Here I set the iterations
  ite = 0

  # Here I set the error based in the Infinite norm
  #erro = (x_k1 - x_k)/x_k1; # ---> Why the relative error has the same result?
  global erro = norm((x_k1 - x_k),Inf)

  while erro>e
    for i = 1:n

      global x_k1[i] = b[i]

      for j = 1:n
        if j != i

          global x_k1[i] =  x_k1[i] - a[i,j]*x_k[j]

        end
      end

      global x_k1[i] = x_k1[i]/a[i,i]

    end

    global erro = norm((x_k1 - x_k),Inf)
    global x_k[:] = x_k1[:]

    ite = ite + 1

  end

  return x_k1

end

R = linearsystem(a,b,R);
println("\n\nThe R Matrix is: \n\n",R)
println("\n\n")

@time @CPUtime linearsystem(a,b,R)

max_stars_repo_path: Linear System/Julia/linear_system.jl

max_stars_repo_name: arturofburgos/Comparing-Languages

max_issues_repo_path: Linear System/Julia/linear_system.jl

max_forks_repo_path: Linear System/Julia/linear_system.jl


text:
using GeometryTypes
using ComputerVision

𝐦 = [Point(1,2), Point(1,2)]
𝐦′ = [Point(3,4), Point(3,4)]
c = Correspondence((𝐦,𝐦′))

c[2]

length(c)


text:
# Copyright (c) 2017: Miles Lubin and contributors
# Copyright (c) 2017: Google Inc.
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

module TestConstraintIndicatorActiveOnFalse

using Test

using MathOptInterface
const MOI = MathOptInterface

function runtests()
    for name in names(@__MODULE__; all = true)
        if startswith("$(name)", "test_")
            @testset "$(name)" begin
                getfield(@__MODULE__, name)()
            end
        end
    end
    return
end

include("../utilities.jl")

"""
    test_indicator_activate_on_zero()

Linear problem with indicator constraint, similar to indicator1_test with
reversed z1.
```
max  2x1 + 3x2
s.t. x1 + x2 <= 10
     z1 == 0 ==> x2 <= 8
     z2 == 1 ==> x2 + x1/5 <= 9
     (1-z1) + z2 >= 1 <=> z2 - z1 >= 0
```
"""
function test_indicator_activate_on_zero()
    model = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}())
    x1 = MOI.add_variable(model)
    x2 = MOI.add_variable(model)
    z1 = MOI.add_variable(model)
    z2 = MOI.add_variable(model)
    vc1 = MOI.add_constraint(model, z1, MOI.ZeroOne())
    @test vc1.value == z1.value
    vc2 = MOI.add_constraint(model, z2, MOI.ZeroOne())
    @test vc2.value == z2.value
    f1 = MOI.VectorAffineFunction(
        [
            MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, z1)),
            MOI.VectorAffineTerm(2, MOI.ScalarAffineTerm(1.0, x2)),
        ],
        [0.0, 0.0],
    )
    iset1 = MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(MOI.LessThan(8.0))
    BT = MOI.Bridges.Constraint.concrete_bridge_type(
        MOI.Bridges.Constraint.IndicatorActiveOnFalseBridge{Float64},
        typeof(f1),
        typeof(iset1),
    )
    BT2 = MOI.Bridges.Constraint.concrete_bridge_type(
        MOI.Bridges.Constraint.IndicatorActiveOnFalseBridge,
        typeof(f1),
        typeof(iset1),
    )
    bridge = MOI.Bridges.Constraint.bridge_constraint(BT, model, f1, iset1)
    @test BT === BT2
    @test bridge isa BT
    z1comp = bridge.variable
    @test MOI.get(model, MOI.ConstraintFunction(), bridge.zero_one_cons) ==
          z1comp
    @test MOI.get(model, MOI.ConstraintSet(), bridge.disjunction_cons) ==
          MOI.EqualTo(1.0)
    disjunction_cons =
        MOI.get(model, MOI.ConstraintFunction(), bridge.disjunction_cons)
    for t in disjunction_cons.terms
        @test t.variable == z1 || t.variable == z1comp
        @test t.coefficient ≈ 1.0
    end
end

end  # module

TestConstraintIndicatorActiveOnFalse.runtests()

max_stars_repo_name: JuliaOpt/MathOptInterface

max_issues_repo_name: JuliaOpt/MathOptInterface

max_forks_repo_name: JuliaOpt/MathOptInterface


text:
## some functions for showing Exp objects

function offsetString(ofs::Rational{Int})
	v1, v2 = divrem(ofs,1//1)
	if v2 < 0 ; v1 -= 1//1 ; v2 += 1//1 ; end
	s = @sprintf "ofs = %2d + %s, " v1 "$v2" ; s = rpad(s,18)
	s
end

function Base.show(io::IO, x::Exp)
	print(io,"Exp:    dur = $(x.dur)")
	for a in x.as ; print(io,"\n  $a") ; end
end

function Base.show(io::IO, a::Note)
	print(io,"Note:   ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "dur = %s, " "$(a.dur)" ; s = rpad(s,14)
	print(io,s)
	s = @sprintf "itv = %2d, " a.itv ; s = rpad(s,11)
	print(io,s)
	s = @sprintf "ocv = %d, " a.ocv ; s = rpad(s,10)
	print(io,s)
	s = @sprintf "vel = %.2f,  " a.vel
	print(io,s)
	print(io,"sca = $(a.sca.name)")
end

function Base.show(io::IO, a::BankSel)
	print(io,"Blank-Select:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "bank = %d" a.bank
	print(io,s)
end

function Base.show(io::IO, a::ProgSel)
	print(io,"Program-Select:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "prog = %d" a.prog
	print(io,s)
end

function Base.show(io::IO, a::VolSet)
	print(io,"Volume-Set:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "vol = %.2f" a.vol
	print(io,s)
end

function Base.show(io::IO, a::PitchWheel)
	print(io,"Pitch-Wheel:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "val = %.3f" (a.val-2^13)/(2^13)
	print(io,s)
end

function Base.show(io::IO, a::ChanAfter)
	print(io,"Channel-Aftertouch:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "val = %.2f" a.val/127
	print(io,s)
end

function Base.show(io::IO, a::Control7)
	print(io,"Control-7bit:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "num = %d,  " a.num
	print(io,s)
	s = @sprintf "val = %d" a.val
	print(io,s)
end

function Base.show(io::IO, a::Control14)
	print(io,"Control-14bit:  ")
	s = @sprintf "ch%d, " a.cha ; s = rpad(s,7)
	print(io,s)
	vs = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "num = %d,  " a.num
	print(io,s)
	s = @sprintf "val = %d" a.val
	print(io,s)
end

function Base.show(io::IO, a::Marker)
	print(io,"Marker:  ")
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "name = \"%s\"" a.name
	print(io,s)
end

function Base.show(io::IO, a::TimeSignature)
	print(io,"Time-Signature:  ")
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "num = %d,  " a.num
	print(io,s)
	s = @sprintf "den = %d,  " a.den
	print(io,s)
	s = @sprintf "clocks = %d,  " a.clocks
	print(io,s)
	s = @sprintf "sub = %d" a.sub
	print(io,s)
end

function Base.show(io::IO, a::Tempo)
	print(io,"Tempo-Set:  ")
	s = offsetString(a.ofs)
	print(io,s)
	s = @sprintf "bpm = %d" a.bpm
	print(io,s)
end


text:
using Random

mutable struct Citizen <: AbstractAgent
    id::Int
    pos::Dims{2}
    stabilized::Bool
    opinion::Array{Int,1}
    prev_opinion::Array{Int,1}
end

"""
    opinion(;dims=(10, 10), nopinions=3, levels_per_opinion=4)

Same as in [Opinion spread](@ref).
"""
function opinion(; dims = (10, 10), nopinions = 3, levels_per_opinion = 4)
    space = GridSpace(dims)
    properties = Dict(:nopinions => nopinions)
    model = AgentBasedModel(
        Citizen,
        space,
        scheduler = random_activation,
        properties = properties,
    )
    for pos in positions(model)
        add_agent!(
            pos,
            model,
            false,
            rand(1:levels_per_opinion, nopinions),
            rand(1:levels_per_opinion, nopinions),
        )
    end
    return model, opinion_agent_step!, dummystep
end

function adopt!(agent, model)
    neighbor = rand(collect(nearby_ids(agent, model)))
    matches = model[neighbor].opinion .== agent.opinion
    nmatches = count(matches)
    # Adopt a different opinion w/ calculated probability
    if nmatches < model.nopinions && rand() < nmatches / model.nopinions
        switchId = rand(findall(x -> x == false, matches))
        agent.opinion[switchId] = model[neighbor].opinion[switchId]
    end
end

function update_prev_opinion!(agent, model)
    for i in 1:(model.nopinions)
        agent.prev_opinion[i] = agent.opinion[i]
    end
end

function is_stabilized!(agent, model)
    if agent.prev_opinion == agent.opinion
        agent.stabilized = true
    else
        agent.stabilized = false
    end
end

function opinion_agent_step!(agent, model)
    update_prev_opinion!(agent, model)
    adopt!(agent, model)
    is_stabilized!(agent, model)
end


text:
# ---
# title: 1010. Pairs of Songs With Total Durations Divisible by 60
# id: problem1010
# author: Indigo
# date: 2021-01-30
# difficulty: Medium
# categories: Array
# link: <https://leetcode.com/problems/pairs-of-songs-with-total-durations-divisible-by-60/description/>
# hidden: true
# ---
#
# You are given a list of songs where the ith song has a duration of `time[i]`
# seconds.
#
# Return _the number of pairs of songs for which their total duration in seconds
# is divisible by_ `60`. Formally, we want the number of indices `i`, `j` such
# that `i < j` with `(time[i] + time[j]) % 60 == 0`.
#
#
#
# **Example 1:**
#
#
#
#     Input: time = [30,20,150,100,40]
#     Output: 3
#     Explanation: Three pairs have a total duration divisible by 60:
#     (time[0] = 30, time[2] = 150): total duration 180
#     (time[1] = 20, time[3] = 100): total duration 120
#     (time[1] = 20, time[4] = 40): total duration 60
#
#
# **Example 2:**
#
#
#
#     Input: time = [60,60,60]
#     Output: 3
#     Explanation: All three pairs have a total duration of 120, which is divisible by 60.
#
#
#
#
# **Constraints:**
#
#   * `1 <= time.length <= 6 * 104`
#   * `1 <= time[i] <= 500`
#
#
## @lc code=start
using LeetCode

function num_pairs_divisible_by60(time::Vector{Int})
    res = 0
    dct = fill(0, 60)
    for t in time
        (t % 60 == 0) ? (dct[60] += 1) : (dct[t % 60] += 1)
    end
    for i in 1:29
        res += dct[i] * dct[60 - i]
    end
    res += sum(1:(dct[30] - 1)) + sum(1:(dct[60] - 1))
    return res
end
## @lc code=end


text:
# arithmetic.jl

# Addition, substraction and other functions
for TM in tupleTMs
    @eval begin
        tmdata(f::$TM) = (f.x0, f.dom)

        zero(a::$TM) = $TM(zero(a.pol), zero(a.rem), a.x0, a.dom)
        one(a::$TM) = $TM(one(a.pol), zero(a.rem), a.x0, a.dom)

        # iszero(a::$TM) = iszero(a.pol) && iszero(zero(a.rem))

        findfirst(a::$TM) = findfirst(a.pol)

        ==(a::$TM, b::$TM) =
            a.pol == b.pol && a.rem == b.rem && a.x0 == b.x0 && a.dom == b.dom

        # Addition
        +(a::$TM) = $TM(a.pol, a.rem, a.x0, a.dom)

        function +(a::$TM, b::$TM)
            a, b = fixorder(a, b)
            return $TM(a.pol+b.pol, a.rem+b.rem, a.x0, a.dom)
        end

        +(a::$TM, b::T) where {T<:NumberNotSeries} = $TM(a.pol+b, a.rem, a.x0, a.dom)

        +(b::T, a::$TM) where {T<:NumberNotSeries} = $TM(b+a.pol, a.rem, a.x0, a.dom)

        # Substraction
        -(a::$TM) = $TM(-a.pol, -a.rem, a.x0, a.dom)

        function -(a::$TM, b::$TM)
            a, b = fixorder(a, b)
            return $TM(a.pol-b.pol, a.rem-b.rem, a.x0, a.dom)
        end

        -(a::$TM, b::T) where {T<:NumberNotSeries} = $TM(a.pol-b, a.rem, a.x0, a.dom)

        -(b::T, a::$TM) where {T<:NumberNotSeries} = $TM(b-a.pol, -a.rem, a.x0, a.dom)

        # Basic division
        function basediv(a::$TM, b::$TM)
            # invb = rpa(x->inv(x), b)
            invb = inv(b)
            return a * invb
        end

        # Multiplication by numbers
        *(a::$TM, b::T) where {T<:NumberNotSeries} = $TM(a.pol*b, b*a.rem, a.x0, a.dom)

        *(b::T, a::$TM) where {T<:NumberNotSeries} = $TM(a.pol*b, b*a.rem, a.x0, a.dom)

        # Multiplication
        function *(a::$TM, b::$TM)
            @assert tmdata(a) == tmdata(b)
            a_order = get_order(a)
            b_order = get_order(b)
            rnegl_order = a_order + b_order
            aux = a.dom - a.x0

            # Returned polynomial
            res = a.pol * b.pol
            order = res.order

            # Remainder of the product
            if $TM == TaylorModel1

                # Remaing terms of the product as reduced Taylor1 (factored polynomial)
                rnegl = Taylor1(zero(res[0]), rnegl_order)
                for k in order+1:rnegl_order
                    @inbounds for i = 0:k
                        (i > a_order || k-i > b_order) && continue
                        rnegl[k] += a.pol[i] * b.pol[k-i]
                    end
                end

                # Bound for the neglected part of the product of polynomials
                Δnegl = rnegl(aux)
                Δ = remainder_product(a, b, aux, Δnegl)
            else

                # Remaing terms of the product as reduced Taylor1 (factored polynomial)
                rnegl = Taylor1(zero(res[0]), rnegl_order-order)
                for k in order+1:rnegl_order
                    @inbounds for i = 0:k
                        (i > a_order || k-i > b_order) && continue
                        rnegl[k-order-1] += a.pol[i] * b.pol[k-i]
                    end
                end
                Δnegl = rnegl(aux)
                Δ = remainder_product(a, b, aux, Δnegl, order)
            end

            return $TM(res, Δ, a.x0, a.dom)
        end

        # Division by numbers
        /(a::$TM, b::T) where {T<:NumberNotSeries} = a * inv(b)

        /(b::T, a::$TM) where {T<:NumberNotSeries} = b * inv(a)

        # Power
        function ^(a::$TM, r::Number)
            r == zero(r) && return one(a)
            r == 1 && return a
            r == 2 && return a*a
            return rpa(x->x^r, a)
        end

        function ^(a::$TM, n::Integer)
            n == 0 && return one(a)
            n == 1 && return a
            n == 2 && return a*a
            return rpa(x->x^n, a)
        end
    end
end

# Remainder of the product
function remainder_product(a, b, aux, Δnegl)
    Δa = a.pol(aux)
    Δb = b.pol(aux)
    Δ = Δnegl + Δb * a.rem + Δa * b.rem + a.rem * b.rem
    return Δ
end
function remainder_product(a::TaylorModel1{TaylorN{T}, S},
                           b::TaylorModel1{TaylorN{T}, S},
                           auxT, Δnegl) where {T, S}
    N = get_numvars()
    # An N-dimensional symmetrical IntervalBox is assumed
    # to bound the TaylorN part
    auxQ = IntervalBox(-1 .. 1, Val(N))
    Δ = remainder_product(a, b, auxT, auxQ, Δnegl)
    return Δ
end
function remainder_product(a::TaylorModel1{TaylorN{T}, S},
                           b::TaylorModel1{TaylorN{T}, S},
                           auxT, auxQ, Δnegl) where {T, S}
    Δa = a.pol(auxT)(auxQ)
    Δb = b.pol(auxT)(auxQ)
    Δ = Δnegl(auxQ) + Δb * a.rem + Δa * b.rem + a.rem * b.rem
    return Δ
end
function remainder_product(a::TaylorModel1{TaylorModelN{N,T,S},S},
        b::TaylorModel1{TaylorModelN{N,T,S},S}, aux, Δnegl) where {N,T,S}
    Δa = a.pol(aux)
    Δb = b.pol(aux)
    Δ = Δnegl + Δb * a.rem + Δa * b.rem + a.rem * b.rem

    # Evaluate at the TMN centered domain
    auxN = a[0].dom - a[0].x0
    ΔN = Δ(auxN)
    return ΔN
end
function remainder_product(a::RTaylorModel1, b::RTaylorModel1, aux, Δnegl, order)
    Δa = a.pol(aux)
    Δb = b.pol(aux)
    V = aux^(order+1)
    Δ = Δnegl + Δb * a.rem + Δa * b.rem + a.rem * b.rem * V
    return Δ
end

# Division
function /(a::TaylorModel1, b::TaylorModel1)
    @assert tmdata(a) == tmdata(b)
    return basediv(a, b)
end

function /(a::RTaylorModel1, b::RTaylorModel1)
    @assert tmdata(a) == tmdata(b)

    # DetermineRootOrderUpperBound seems equivalent (optimized?) to `findfirst`
    bk = findfirst(b)
    bk ≤ 0 && return basediv(a, b)

    # In 2.3.12, `a` and `b` are now extended in order by `bk`,
    # but how can we do this without knowing the explicit function
    # that created them?
    #
    # Below we reduce the original order by bk.
    #
    order = get_order(a)
    ared = truncate_taylormodel(
        RTaylorModel1(Taylor1(a.pol.coeffs[bk+1:order+1]), a.rem, a.x0, a.dom), order-bk)
    order = get_order(b)
    bred = truncate_taylormodel(
        RTaylorModel1(Taylor1(b.pol.coeffs[bk+1:order+1]), b.rem, b.x0, b.dom), order-bk)

    return basediv( ared, bred )
end

"""
    truncate_taylormodel(a::RTaylorModel1, m::Integer)

Truncates `a::RTaylorModel1` to order `m`.

"""
function truncate_taylormodel(a::RTaylorModel1, m::Integer)
    order = get_order(a)
    m ≥ order && return a

    apol = Taylor1(copy(a.pol.coeffs[1:m+1]))
    bpol = Taylor1(copy(a.pol.coeffs))
    aux = a.dom - a.x0
    Δnegl = bound_truncation(RTaylorModel1, bpol, aux, m)
    Δ = Δnegl + a.rem * (aux)^(order-m)
    return RTaylorModel1( apol, Δ, a.x0, a.dom )
end

# Same as above, for TaylorModelN
tmdata(f::TaylorModelN) = (f.x0, f.dom)
zero(a::TaylorModelN) = TaylorModelN(zero(a.pol), zero(a.rem), a.x0, a.dom)
one(a::TaylorModelN) = TaylorModelN(one(a.pol), zero(a.rem), a.x0, a.dom)

# iszero(a::TaylorModelN) = iszero(a.pol) && iszero(zero(a.rem))

findfirst(a::TaylorModelN) = findfirst(a.pol)

==(a::TaylorModelN, b::TaylorModelN) =
    a.pol == b.pol && a.rem == b.rem && a.x0 == b.x0 && a.dom == b.dom

# Addition and substraction
for op in (:+, :-)
    @eval begin
        $(op)(a::TaylorModelN) = TaylorModelN($(op)(a.pol), $(op)(a.rem), a.x0, a.dom)

        function $(op)(a::TaylorModelN, b::TaylorModelN)
            a, b = fixorder(a, b)
            return TaylorModelN($(op)(a.pol,b.pol), $(op)(a.rem,b.rem), a.x0, a.dom)
        end

        $(op)(a::TaylorModelN, b::T) where {T<:NumberNotSeries} =
            TaylorModelN($(op)(a.pol, b), a.rem, a.x0, a.dom)

        $(op)(b::T, a::TaylorModelN) where {T<:NumberNotSeries} =
            TaylorModelN($(op)(b, a.pol), $(op)(a.rem), a.x0, a.dom)
    end
end

# Multiplication
function *(a::TaylorModelN, b::TaylorModelN)
    @assert tmdata(a) == tmdata(b)
    a_order = get_order(a)
    b_order = get_order(b)
    rnegl_order = a_order + b_order
    @assert rnegl_order ≤ get_order()
    aux = a.dom - a.x0

    # Returned polynomial
    res = a.pol * b.pol
    order = res.order

    # Remaing terms of the product
    vv = Array{HomogeneousPolynomial{eltype(res)}}(undef, rnegl_order-order)
    suma = Array{promote_type(eltype(res), eltype(a.dom))}(undef, rnegl_order-order)
    for k in order+1:rnegl_order
        vv[k-order] = HomogeneousPolynomial(zero(eltype(res)), k)
        @inbounds for i = 0:k
            (i > a_order || k-i > b_order) && continue
            TaylorSeries.mul!(vv[k-order], a.pol[i], b.pol[k-i])
        end
        suma[k-order] = vv[k-order](aux)
    end

    # Bound for the neglected part of the product of polynomials
    Δnegl = sum( sort!(suma, by=abs2) )
    Δ = remainder_product(a, b, aux, Δnegl)

    return TaylorModelN(res, Δ, a.x0, a.dom)
end

# Multiplication by numbers
function *(b::T, a::TaylorModelN) where {T<:NumberNotSeries}
    pol = a.pol * b
    rem = b * a.rem
    return TaylorModelN(pol, rem, a.x0, a.dom)
end

*(a::TaylorModelN, b::T) where {T<:NumberNotSeries} = b * a

# Basic division
function basediv(a::TaylorModelN, b::TaylorModelN)
    invb = rpa(x->inv(x), b)
    return a * invb
end
# /(a::TaylorModelN, b::TaylorModelN) = basediv(a, b)

# Division by numbers
/(a::TaylorModelN, b::T) where {T<:Number} = a * inv(b)
/(b::T, a::TaylorModelN) where {T<:NumberNotSeries} = b * inv(a)

# Power
function ^(a::TaylorModelN{N,T,S}, r::Number) where {N,T,S}
    r == 0 && return one(a)
    r == 1 && return a
    r == 2 && return a*a
    return rpa(x->x^r, a)
end
function ^(a::TaylorModelN{N,T,S}, n::Integer) where {N,T,S}
    n == 0 && return one(a)
    n == 1 && return a
    n == 2 && return a*a
    return rpa(x->x^n, a)
end


text:
using Distributed
# start the following on the command line:
# julia -p n  # starts n processes on the local machine
# julia -p 8  # starts REPL with 8 workers
workers()
# 8-element Array{Int64,1}:
#   2
#   3
#   4
#   5
#   ⋮
#   8
#   9

# iterate over workers:
for pid in workers()
	# do something with pid
end
myid() # 1
addprocs(5)
# 5-element Array{Any,1}:
#  10
#  11
#  12
#  13
#  14
nprocs() # 14
rmprocs(3) #> Task (done) @0x0000000012f8f0f0
# worker with id 3 is removed

# cluster of computers:
# julia --machinefile machines driver.jl
# Run processes specified in driver.jl on hosts listed in machines

# primitive operations with processes:
r1 = remotecall(x -> x^2, 2, 1000) #> Future(2, 1, 15, nothing)
fetch(r1) #> 1000000

remotecall_fetch(sin, 5, 2pi) # -2.4492935982947064e-16

r2 = @spawnat 4 sqrt(2) #> Future(4, 1, 18, nothing)
# lets worker 4 calculate sqrt(2)
fetch(r2)  #> 1.4142135623730951
r = [@spawnat w sqrt(5) for w in workers()]
# or in a freshly started REPL:
# r = [@spawnat i sqrt(5) for i=1:length(workers())]
# 12-element Array{Future,1}:
#  Future(2, 1, 20, nothing)
#  Future(4, 1, 21, nothing)
#  Future(5, 1, 22, nothing)
#  Future(6, 1, 23, nothing)
#  Future(7, 1, 24, nothing)
#  Future(8, 1, 25, nothing)
#  Future(9, 1, 26, nothing)
#  Future(10, 1, 27, nothing)
#  Future(11, 1, 28, nothing)
#  Future(12, 1, 29, nothing)
#  Future(13, 1, 30, nothing)
#  Future(14, 1, 31, nothing)
r3 = @spawn sqrt(5) #> Future(2, 1, 32, nothing)
fetch(r3) #> 2.23606797749979

# using @everywhere to make functions available to all workers:
@everywhere w = 8
@everywhere println(myid())
# 1
#       From worker 14:   14
#       From worker 8:    8
#       From worker 10:   10
#       From worker 7:    7
#       From worker 2:    2
#       From worker 4:    4
#       From worker 12:   12
#       From worker 9:    9
#       From worker 11:   11
#       From worker 6:    6
#       From worker 13:   13
#       From worker 5:    5

x = 5 #> 5
@everywhere println(x) #> 5
#   # exception on worker 2: ERROR: UndefVarError: x not defined ...
#        ...and 11 more exception(s)

@everywhere include("defs.jl")
@everywhere function fib(n)
	if (n < 2)
		return n
	else
		return fib(n-1) + fib(n-2)
	end
end

r2 =  @spawnat 2 fib(10)
# Future(2, 1, 70, nothing)
fetch(r2)
# 55

include("functions.jl")

# broadcast data to all workers:
d = "Julia"
for pid in workers()
   remotecall(x -> (global d; d = x; nothing), pid, d)
end


text:
module API

using DocStringExtensions

using ..Configuration
import ..Delayed
import ..Graph
import ..Resolve
import ..Trials

export ask
export fmin
export provide_recommendation
export tell!

"""
$(TYPEDSIGNATURES)
Provides a suggestion based on random search to generate hyperparameter values.
Also can generate trials to be evaluated from Dict of points.
"""
function ask(space::Dict{Symbol, T} where T)::Trials.Trial

    vals = Trials.ValsDict()
    hyperparams = Resolve.node(space, vals)

    return Trials.Trial(hyperparams, vals, Inf)
end
"""
$(TYPEDSIGNATURES)
Provides a suggestion based on tree-parzen estimation
"""
function ask(space::Dict{Symbol, T} where T, trials::Vector{Trials.Trial}, config::Config)::Trials.Trial

    # Run a few initial random jobs before doing tree-parzen.
    if length(trials) < config.random_trials
        return ask(space)
    end

    # Get results from previous iterations and resolve posterior distributions
    vals, hyperparams = Resolve.node(space, trials, config)

    return Trials.Trial(hyperparams, vals, Inf)
end

"""
$(TYPEDSIGNATURES)
Stores the evaluation of a trial and composes a trial history
"""
function tell!(trials::Vector{Trials.Trial}, trial::Trials.Trial, loss::Float64)::Nothing
    tell!(trial, loss)
    push!(trials, trial)

    return nothing
end
"""
$(TYPEDSIGNATURES)

Stores the evaluation of a trial only
"""
function tell!(trial::Trials.Trial, loss::Float64)::Nothing
    trial.loss = loss
    return nothing
end

"""
$(TYPEDSIGNATURES)
Gets us the "best" parameter combination
"""
function provide_recommendation(trials::Vector{Trials.Trial})
    losses = [trial.loss for trial in trials]

    return trials[argmin(losses)].hyperparams
end

check_rval(rval::Float64, fn_symbol::Symbol) = nothing
function check_rval(rval::Any, fn_symbol::Symbol)
    throw(TypeError(
        fn_symbol, "The function you submitted to TreeParzen didn't return a Float",
        Float64, rval
    ))
end

#### Below functions are to support fmin
"""
$(TYPEDSIGNATURES)

Apply the `hyperparams` to the `fn` and check that the result is `Float64`.
"""
function evaluate_hyperparams(fn::Function, hyperparams::Dict{Symbol, T} where T)::Float64

    rval = try
        fn(hyperparams)
    catch error
        throw(ArgumentError(string(
            "The function called ", fn, " that you submitted to fmin could not be run.",
            " The error message was: ", error)
        ))
    end
    check_rval(rval, Symbol(fn))

    return rval
end

"""
$(TYPEDSIGNATURES)

# Arguments

- `points` : A list of trials chosen by the user to be evaluated before optimisation.
    The list can be empty.
- `fn` : The user's function to be minimised.
- `space` : A `Dict` containing objects from the `TreeParzen.HP` module that describe the
    space of hyperparameters that TreeParzen is allowed to search.
- `N` : Number of trials to perform.
- `config` : A `Config` object.
- `logging_interval` : At what interval the update of trials should be logged. The default
    value is `-1`, where only the final total will be logged. If the parameter is specified
    you get interval log statements of iterations.

Example:

```julia
    API.run(
        Trials.Trial[], x -> x[:loss],
        space,
        1000,
        config;
        logging_interval = 100
    )
```
   Output:
    [ Info: fmin: 100 / 1000 trials carried out
    [ Info: fmin: 200 / 1000 trials carried out
    [ Info: fmin: 300 / 1000 trials carried out
    [ Info: fmin: 400 / 1000 trials carried out
    [ Info: fmin: 500 / 1000 trials carried out
    [ Info: fmin: 600 / 1000 trials carried out
    [ Info: fmin: 700 / 1000 trials carried out
    [ Info: fmin: 800 / 1000 trials carried out
    [ Info: fmin: 900 / 1000 trials carried out
    [ Info: fmin: 1000 / 1000 trials carried out

"""
function run(
    points::Vector{Trials.Trial}, fn::Function, space::Dict{Symbol, T} where T, N::Int,
    config::Config;
    logging_interval::Int = -1,
)::Vector{Trials.Trial}

    trials = Trials.Trial[]

    if logging_interval == -1
        logging_interval = N
    end

    if logging_interval > N
        @warn("The logging_interval ($logging_interval) given is higher than total number of requested trials ($N)")
    end

    if length(trials) > 0
        @info("fmin: $(length(trials)) / $(N) trials carried out")
    end

    # If any points to evaluate have been added, do them first
    for point in points
        loss = evaluate_hyperparams(fn, point.hyperparams)
        API.tell!(trials, point, loss)
    end

    for i in (length(trials) + 1):N
        new_trial = API.ask(space, trials, config)
        loss = evaluate_hyperparams(fn, new_trial.hyperparams)
        API.tell!(trials, new_trial, loss)
        if (i % logging_interval) == 0
            @info("fmin: $(i) / $(N) trials carried out")
        end
    end

    return trials
end

"""
$(TYPEDSIGNATURES)

Find the set of hyperparameters that return the lowest value from the submitted function.

# Arguments

- `fn` : The function that you want to evaluate. Note that the function needs to accept a
    single `Dict` and return a `Float64`. If your function does not accept a `Dict` you will
    need to create a wrapping function that translates the content of the `Dict` to the
    parameters your function needs. Similarly if you want to maximise your function instead
    of minimise it, your wrapping function will need to invert the output.
    See the TreeParzen user guide for examples.
- `space` : A `Dict` containing objects from the `TreeParzen.HP` module that describe the
    space of hyperparameters that TreeParzen is allowed to search.
- `N` : Number of trials to perform.
- `points` : A list of trials chosen by the user to be evaluated before optimisation. The
    list can be empty. Example:
        `[Dict(:x => 0.0, :y => 0.0), Dict(:x => 1.0, :y => 2.0)]`

## Optional keyword arguments

- `threshold::Float64` : A value between `0` and `1`, which controls the probability threshold at
    which expected improvement criteria is modeled.
- `linear_forgetting::Int` : A positive value which controls the number of historic points which
    are used for probabilistic modelling, and older points beyond this are linearly
    de-weighted.
- `draws::Int` : A positive value which controls the number of samples to draw when making a
    recommendation for next optimisation candidate.
- `random_trials::Int` : A positive value which controls the number of trials of randomly
    generated candidate points before TreeParzen optimisation is used.
- `prior_weight::Float64` : A value between `0` and `1`, which controls importance of user specified
    probabilistic parameters vs the history of trials.
- `logging_interval::Int` : An integer specifying after how many iterations should a progress
    statement be logged out. Default, -1, will log only upon completion.
"""
function fmin(
    fn::Function, space::Dict{Symbol, T} where T, N::Int, points::Vector{Trials.Trial};
    threshold::Float64 = 0.25, linear_forgetting::Int = 25, draws::Int = 24,
    random_trials::Int = 20, prior_weight::Float64 = 1.0, logging_interval::Int = -1,
)
    Graph.checkspace(space)

    config = Config(threshold, linear_forgetting, draws, random_trials, prior_weight)

    # Evaluate the trials
    trials = run(points, fn, space, N, config; logging_interval = logging_interval)

    @info("Successfully completed fmin ")

    return API.provide_recommendation(trials)
end
function fmin(
    fn::Function, space::Dict{Symbol, T} where T, N::Int,
    points::Vector{<: Dict{Symbol, T} where T}; kwargs...
)
    if N < length(points)
        throw(ArgumentError(string(
            "You have asked for fewer steps than the number of points to evaluate: N ", N,
            "points ", length(points)
        )))
    end

    return fmin(fn, space, N, API.ask.(points); kwargs...)
end
function fmin(fn::Function, space::Dict{Symbol, T} where T, N::Int; kwargs...)
    return fmin(fn, space, N, Trials.Trial[]; kwargs...)
end

end  # module API


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

using QuasiArrays, Test

@testset "arrayops" begin
    @testset "basics" begin
        @test length(QuasiVector([1, 2, 3],0:0.5:1)) == 3
        @test count(!iszero, QuasiVector([1, 2, 3],0:0.5:1)) == 3

        let a = QuasiVector(fill(1., 4),0:0.5:1.5), b = a+a, c = a-a, d = a+a+a
            @test b[0.0] === 2. && b[0.5] === 2. && b[1.0] === 2. && b[1.5] === 2.
            @test c[0.0] === 0. && c[0.5] === 0. && c[1.0] === 0. && c[1.5] === 0.
            @test d[0.0] === 3. && d[0.5] === 3. && d[1.0] === 3. && d[1.5] === 3.
        end

        v = QuasiVector([1,2,3], 0:0.5:1)
        @test isequal(1 .+ v, QuasiVector([2,3,4], 0:0.5:1))
        @test isequal(v .+ 1, QuasiVector([2,3,4], 0:0.5:1))
        @test isequal(1 .- v, QuasiVector([0,-1,-2], 0:0.5:1))
        @test isequal(v .- 1, QuasiVector([0,1,2], 0:0.5:1))

        @test isequal(5*v, QuasiVector([5,10,15], 0:0.5:1))
        @test isequal(v*5, QuasiVector([5,10,15], 0:0.5:1))
        @test isequal(1 ./ v, QuasiVector([1.0,0.5,1/3], 0:0.5:1))
        @test isequal(v/5, QuasiVector([0.2,0.4,0.6], 0:0.5:1))

        @test isequal(2 .% v, QuasiVector([0,0,2], 0:0.5:1))
        @test isequal(v .% 2, QuasiVector([1,0,1], 0:0.5:1))
        @test isequal(2 .÷ v, QuasiVector([2,1,0], 0:0.5:1))
        @test isequal(v .÷ 2, QuasiVector([0,1,1], 0:0.5:1))
        @test isequal(-2 .% v, QuasiVector([0,0,-2], 0:0.5:1))
        @test isequal(-2 .÷ v, QuasiVector([-2,-1,0], 0:0.5:1))

        a = QuasiArray(fill(1.,2,2),(0:0.5:0.5,1:0.5:1.5))
        b = similar(a)
        @test axes(b) === axes(a)
        @test b.axes === a.axes
        copyto!(b,a)
        @test b == a

        a[0.0,1.0] = 1
        a[0.0,1.5] = 2
        a[0.5,1.0] = 3
        a[0.5,1.5] = 4
        b = copy(a')
        @test a[0.0,1.0] == 1. && a[0.0,1.5] == 2. && a[0.5,1.0] == 3. && a[0.5,1.5] == 4.
        @test b[1.0,0.0] == 1. && b[1.5,0.0] == 2. && b[1.0,0.5] == 3. && b[1.5,0.5] == 4.
        @test a[[0.0,0.5], [1.0,1.5]] == [1 2; 3 4]
        V = view(a, [0.0,0.5], [1.0,1.5])
        V[1,1] = 2
        @test a[0.0,1.0] == V[1,1] == V[CartesianIndex(1,1)] == 2
        V .= 1
        @test a.parent == fill(1,2,2)
        view(a, [0.0,0.5], [1.0,1.5]) .= 2
        @test a.parent == fill(2,2,2)
        a[[0.0 0.5], [1.0 1.5]] .= 1
        @test a.parent == fill(1.,2,2)
        a[[0.0 0.5], 1] .= 0
        @test a[0.0,1.0] == 0. && a[0.0,1.5] == 1. && a[0.5,1.0] == 0. && a[0.5,1.5] == 1.
        a[:, [1.0 1.5]] .= 2
        @test parent(a) == fill(2.,2,2)
    end

    @testset "construction" begin
        @test typeof(QuasiVector{Int}(undef, (1:3,))) == QuasiVector{Int,Tuple{UnitRange{Int}}}
        @test typeof(QuasiVector(undef, 1:3)) == QuasiVector{Any,Tuple{UnitRange{Int}}}
        @test typeof(QuasiMatrix{Int}(undef, 1:2,1:3)) == QuasiMatrix{Int,NTuple{2,UnitRange{Int}}}
        @test typeof(QuasiMatrix(undef, 1:2,1:3)) == QuasiMatrix{Any,NTuple{2,UnitRange{Int}}}

        @test size(QuasiVector{Int}(undef, 1:3)) == (3,)
        @test size(QuasiVector(undef, 1:3)) == (3,)
        @test size(QuasiMatrix{Int}(undef, 1:2,1:3)) == (2,3)
        @test size(QuasiMatrix(undef, 1:2,1:3)) == (2,3)

        @test_throws MethodError QuasiMatrix()
        @test_throws MethodError QuasiMatrix{Int}()
        @test_throws MethodError QuasiArray{Int,3}()
    end

    @testset "end" begin
        X = QuasiArray([ i+2j for i=1:5, j=1:5 ],(0:0.5:2,0:0.5:2))
        @test X[end,end] == 15
        @test_broken X[end]     == 15  # linear index
        @test X[0.5,  end] == 12
        @test X[end,  0.5] == 9
        @test X[end-0.5,0.5] == 8
        Y = ([2, 1, 4, 3] .- 1) ./ 2
        @test X[Y[end],0.0] == 5
        @test X[end,Y[end]] == 11
    end
end


text:
export loadCanonicalFG_Kaess, loadCanonicalFG_TestSymbolic, loadCanonicalFG_CaesarRing1D

"""
    $SIGNATURES

Canonical example from literature, Kaess, et al.: ISAM2, IJRR, 2011.

Notes
- Paper variable ordering: p = [:l1;:l2;:x1;:x2;:x3]
"""
function loadCanonicalFG_Kaess(;graphinit::Bool=false)
  fg = initfg()

  addVariable!(fg,:x1, ContinuousScalar)
  addFactor!(fg, [:x1;], Prior(Normal()), autoinit=graphinit)

  addVariable!(fg,:x2, ContinuousScalar)
  addFactor!(fg,[:x1, :x2], LinearConditional(Normal()), autoinit=graphinit)

  addVariable!(fg, :x3, ContinuousScalar)
  addFactor!(fg,[:x2,:x3],LinearConditional(Normal()), autoinit=graphinit)

  addVariable!(fg, :l1, ContinuousScalar)
  addFactor!(fg, [:x1,:l1], LinearConditional(Normal()) , autoinit=graphinit)
  addFactor!(fg, [:x2,:l1], LinearConditional(Normal()) , autoinit=graphinit)

  addVariable!(fg, :l2, ContinuousScalar)
  addFactor!(fg, [:x3,:l2], LinearConditional(Normal()), autoinit=graphinit)

  return fg
end

"""
    $SIGNATURES

Canonical example introduced by Borglab.

Notes
- Known variable ordering: p = [:x1; :l3; :l1; :x5; :x2; :l2; :x4; :x3]
"""
function loadCanonicalFG_TestSymbolic(;graphinit::Bool=false)
  fg = initfg()

  addVariable!(fg, :x1, ContinuousScalar)
  addVariable!(fg, :x2, ContinuousScalar)
  addVariable!(fg, :x3, ContinuousScalar)
  addVariable!(fg, :x4, ContinuousScalar)
  addVariable!(fg, :x5, ContinuousScalar)
  addVariable!(fg, :l1, ContinuousScalar)
  addVariable!(fg, :l2, ContinuousScalar)
  addVariable!(fg, :l3, ContinuousScalar)

  addFactor!(fg, [:x1;:l1], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x1;:x2], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x2;:l1], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x2;:x3], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x3;:x4], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x4;:l2], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x4;:x5], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:l2;:x5], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x4;:l3], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x5;:l3], LinearConditional(Normal()), autoinit=graphinit)

  return fg
end

"""
    $SIGNATURES

Canonical example introduced originally as Caesar Hex Example.

Notes
- Paper variable ordering: p = [:x0;:x2;:x4;:x6;:x1;:l1;:x5;:x3;]
"""
function loadCanonicalFG_CaesarRing1D(;graphinit::Bool=false)

  fg = initfg()

  addVariable!(fg, :x0, ContinuousScalar)
  addVariable!(fg, :x1, ContinuousScalar)
  addVariable!(fg, :x2, ContinuousScalar)
  addVariable!(fg, :x3, ContinuousScalar)
  addVariable!(fg, :x4, ContinuousScalar)
  addVariable!(fg, :x5, ContinuousScalar)
  addVariable!(fg, :x6, ContinuousScalar)

  addFactor!(fg, [:x0], Prior(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x0;:x1], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x1;:x2], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x2;:x3], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x3;:x4], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x4;:x5], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x5;:x6], LinearConditional(Normal()), autoinit=graphinit)

  addVariable!(fg, :l1, ContinuousScalar)
  addFactor!(fg, [:x0;:l1], LinearConditional(Normal()), autoinit=graphinit)
  addFactor!(fg, [:x6;:l1], LinearConditional(Normal()), autoinit=graphinit)

  return fg
end


text:
export logpdf

function logpdf(m::JointDistribution,x, method=logpdf)
    return method(m.model, m.args, x)
end

@gg function logpdf(_m::Model, _args, _data)
    type2model(_m) |> sourceLogpdf() |> loadvals(_args, _data)
end

function sourceLogpdf()
    function(_m::Model)
        proc(_m, st :: Assign)     = :($(st.x) = $(st.rhs))
        proc(_m, st :: Sample)     = :(_ℓ += logpdf($(st.rhs), $(st.x)))
        proc(_m, st :: Return)     = nothing
        proc(_m, st :: LineNumber) = nothing

        wrap(kernel) = @q begin
            _ℓ = 0.0
            $kernel
            return _ℓ
        end

        buildSource(_m, proc, wrap) |> flatten
    end
end


text:
module TestTimematr

using Base.Test
using DataArrays
using DataFrames
using Dates

using TimeData

println("Running timematr tests")

#######################################
## reconstruction from core elements ##
#######################################

## create instance
vals = rand(30, 4)
dats = Date[Date(2013, 7, ii) for ii=1:30]
nams = [:A, :B, :C, :D]
df = composeDataFrame(vals, nams)

tm = TimeData.Timematr(df, dats)
tm = TimeData.Timematr(vals, nams, [31:60])
tm = TimeData.Timematr(vals, nams, dats)

## reconstructing Timematr instance from core elements
tm = TimeData.Timematr(vals, nams, dats)
vals2 = TimeData.core(tm)
nams2 = TimeData.names(tm)
dats2 = TimeData.idx(tm)
tm2 = TimeData.Timematr(vals2, nams2, dats2)
@test TimeData.isequal(tm, tm2)

## core function should not return Array{Real, 2}
vals1 = [4000, 4001, 4002]
vals2 = [1., 2., 3.]
df = DataFrame(a = vals1, b = vals2)
tm = TimeData.Timematr(df)
@test isa(TimeData.core(tm), Array{Float64})

end


text:
using Reproject
using Test
using Conda, PyCall
using FITSIO, WCS
using SHA: sha256

ENV["PYTHON"]=""
Conda.add_channel("astropy")
Conda.add("reproject")
rp = pyimport("reproject")
astropy = pyimport("astropy")

include("parsers.jl")
include("utils.jl")
include("core.jl")


text:
### Todo Item
import Escher: view, update

# Model

immutable TodoItem <: Component
    done::Bool
    desc::AbstractString
end

const emptyitem = TodoItem(false, "")

# View

function view(i::TodoItem)
    actions = collector(:todoitem)

    ui = hbox(
        intent(Check, checkbox(i.done)) >>> actions,
        hskip(1em),
        intent(EditDesc, textinput(i.desc)) >>> actions
    ) |> packacross(center)

    intent(actions, ui)
end

# Update

immutable Check <: Action
    state::Bool
end

immutable EditDesc <: Action
    desc::AbstractString
end

update(x::TodoItem, a::Check) = TodoItem(a.state, x.desc)
update(x::TodoItem, a::EditDesc) = TodoItem(x.done, a.desc)


text:
"abstract Hyperparameters"
abstract AbstractHyperparam;

"Abstract Model Data Object"
abstract AbstractModelData;

"Abstract Model Buffer Object"
abstract AbstractModelBuffer;