function foo{T}(ta::TimeArray{T,1}; prices=false, log_transform=false)
    #r = keyword_check(ta, prices=prices, log_transform=log_transform)
    r = keyword_check(ta, prices, log_transform)

    sum(r.values)
end

function bar(x)
    mean(x)
end

######## analyze ###################

function analyze(x;funcs=tradestats)
    for i in 1:length(funcs)
        @printf("The value for %s is %f", funcs[i], funcs[i](x))
        println("")
    end
end

######## annualized return ###################
### reference Bacon, Carl. Practical Portfolio Performance Measurement and Attribution. Wiley. 2004. p. 25

### @doc """
### # Annualized Returns
###
### # Keyword Argument defaults
###       prices = false
###       log_transform = false
###       method = "arithmetic" ("geometric" supported)
###       period = 252 (for daily)
###
### # Details
###
### # References
###   Bacon, Carl. Practical Portfolio Performance Measurement and Attribution. Wiley. 2004. p. 25
### """ ->

function annualized_return{T}(ta::TimeArray{T,1}; prices=false, log_transform=false, method="arithmetic", periods=252)
    r = keyword_check(ta, prices, log_transform)
    n = length(r)
    method == "arithmetic" ? (periods/n) * sum(r.values) :
    method == "geometric" ? prod(1+r.values) ^ (periods/n) - 1 :
    error("only arithmetic and geometric methods supported")
end

######## equity curve ########################

function equity{T}(ta::TimeArray{T,1}; prices=false)
    prices ?
    TimeArray(ta.timestamp, [1.0, expm1(cumsum(diff(log(ta.values)))) + 1], ["equity"]) :
    TimeArray(ta.timestamp, [expm1(cumsum(ta.values)) + 1], ["equity"])
end

######## from the oldmatrix.jl

# I think this might be incorrect
# it claims to be correct by treating the µ vector as a scalar for single vector case
#function ∑matrix(x::Vector)
function sigma(x::Vector)
    y = x * mean(x)
    y * y'
end

function Shapiro_Wilks{T<:Float64}(x::Vector{T})
    sx = sort(x)
    d = Truncated(Normal(mean(xs),1),minimum(xs), maximum(xs))
    m = sort(rand(d, length(x)))
    #V = ∑matrix(sx)
    V = sigma(sx)
    a = m' * inv(V) / sqrt(m' * inv(V) * inv(V) * m)
    y - x.-mean(x)

    # ∑ ai * xi and then ^ 2
    # so a cross product and then square it
    # a'x ^ 2
    num = a'x ^ 2

    # ∑ (xi - xbar)  ^ 2
    # so first get y = xi - xbar vector
    # y'y
    den = y'y
    num/den
end

######## from the matrix.jl

function ∑matrix(x::Vector, y::Vector)
    z = x .* y
    z .* z'
end

function shapiro{T<:Float64}(x::Vector{T})
    sx = sort(x)
    # d = Truncated(Normal(mean(xs),1),minimum(xs), maximum(xs))
    # m = sort(rand(d, length(x)))

    V = ∑matrix(sx)
    # a = m' * inv(V) / sqrt(m' * inv(V) * inv(V) * m)

    # ∑ ai * xi and then ^ 2
    # so a cross product and then square it
    # a'x ^ 2

    num = a'x ^ 2

    # ∑ (xi - xbar)  ^ 2
    # so first get y = xi - xbar vector
    # y'y

    y - x.-mean(x)
    den = y'y

    num/den
end

## _____ Shapiro Wilk fortran code___________________

######===========     Perform the Shapiro-Wilk test for normality.
######===========
######===========     The Shapiro-Wilk test tests the null hypothesis that the
######===========     data was drawn from a normal distribution.
######===========
######===========     Parameters
######===========     ----------
######===========     x : array_like
######===========         Array of sample data.
######===========     a : array_like, optional
######===========         Array of internal parameters used in the calculation.  If these
######===========         are not given, they will be computed internally.  If x has length
######===========         n, then a must have length n/2.
######===========     reta : bool, optional
######===========         Whether or not to return the internally computed a values.  The
######===========         default is False.
######===========
######===========     Returns
######===========     -------
######===========     W : float
######===========         The test statistic.
######===========     p-value : float
######===========         The p-value for the hypothesis test.
######===========     a : array_like, optional
######===========         If `reta` is True, then these are the internally computed "a"
######===========         values that may be passed into this function on future calls.
######===========
######===========     """
######===========     N = len(x)
######===========     if N < 3:
######===========         raise ValueError("Data must be at least length 3.")
######===========     if a is None:
######===========         a = zeros(N,'f')
######===========         init = 0
######===========     else:
######===========         if len(a) != N//2:
######===========             raise ValueError("len(a) must equal len(x)/2")
######===========         init = 1
######===========     y = sort(x)
######===========     a, w, pw, ifault = statlib.swilk(y, a[:N//2], init)
######===========     if ifault not in [0,2]:
######===========         warnings.warn(str(ifault))
######===========     if N > 5000:
######===========         warnings.warn("p-value may not be accurate for N > 5000.")
######===========     if reta:
######===========         return w, pw, a
######===========     else:
######===========         return w, pw

function swilk(x::Vector{Float64})

    fstats = dlopen(Pkg.dir("FinanceAnalytics//bin/swilk"))
    swilk_ = dlsym(fstats, :swilk_)

        #     SUBROUTINE SWILK (INIT, X, N, N1 , N2, A, W, PW, IFAULT)
        #     .. Scalar Arguments ..
        #      REAL               W, PW
        #      INTEGER            N, N1, N2, IFAULT
        #      LOGIC              INIT
        #     .. Array Arguments ..
        #      REAL               X(*), A(*)

    y      = sort(x)
    init   = false  # scipy default
    n      = length(y)  # needs some error checking for bounds
    n1     = 1   # this is a w.a.g. ~ changing this to 0 gets same ridiculous result
    n2     = ifloor(n/2) # scipy uses n//2
    w      = 0.0
    pw     = 1.0  # another w.a.g.
    ifault = 0
    a      = zeros(Float64, n2)

    ccall(swilk_,
          Float64,
          (Ptr{Bool}, Ptr{Float64}, Ptr{Int32}, Ptr{Int32}, Ptr{Int32}, Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Ptr{Int32}),
          &init, y, &n, &n1, &n2, a, &w, &pw, &ifault)
end

## _____ toy example below ___________________

function greet(n::Int)
    #greets   = dlopen(Pkg.dir("FinanceStats/bin/greetings"))
    greets   = dlopen("../bin/greetings")
    hello_   = dlsym(greets,:hello_)
    goodbye_ = dlsym(greets,:goodbye_)
    iseven(n)?
    ccall(hello_,Void,()) :
    ccall(goodbye_,Void,())
end

# function greet(n::Int)

#     # greets   = dlopen("../bin/greetings")
#     greets   = dlopen(Pkg.dir("FinanceStats/bin/greetings"))
# #    greets   = Pkg.dir("FinanceStats/bin/greetings")
# #    const greets   = "../bin/greetings"
#     hello_   = dlsym(greets,:hello_)
#     goodbye_ = dlsym(greets,:goodbye_)
#     iseven(n)?
# #    ccall((:hello_, greets) ,Void,()) :
# #    ccall((:goodbye_, greets),Void,())
#     ccall((:hello, greets) ,Void,()) :
#     ccall((:goodbye, greets),Void,())
# #    ccall((:hello_, "../bin/greetings") ,Void,()) :
# #    ccall((:goodbye_, greets),Void,())
# end

## _____ this works ___________________

### function greet(n::Int)
###     greets   = dlopen(Pkg.dir("FinanceStats/bin/greetings"))
###     hello_   = dlsym(greets,:hello_)
###     goodbye_ = dlsym(greets,:goodbye_)
###     iseven(n)?
###     ccall(hello_,Void,()) :
###     ccall(goodbye_,Void,())
### end


#=
Adapted from Higgs-Boson ML competition on Kaggle

=#

function ams(s,b)
  #=
  Calculate the Approximate Median Significance defined as:

  AMS = sqrt( 2 * { (s + b + b_r) * log[1+(s/(b+b_r))] - s} )

  where b_r = 10, b = background, s = signal

  =#
  br = 10.0
  radicand = 2 * ((s+b+br) * log(1.0 + s/(b+br)) - s)
  if radicand < 0
    println("radicand is negative. Exiting.")
  else
    return sqrt(radicand)
  end
end


#! /usr/bin/julia

# Rosetta Code, Hickerson series of almost integers

function makehickerson{T<:Real}(x::T)
    n = 0
    h = one(T)/2x
    function hickerson()
        n += 1
        h *= n/x
    end
end

function reporthickerson{T<:Real,U<:Integer}(a::T, nmax::U)
    h = makehickerson(a)
    hgm = makehickerson(prevfloat(a))
    hgp = makehickerson(nextfloat(a))

    println()
    print("Performing calculations using ", typeof(a))
    println(", which has ", precision(a), "-bit precision.")
    for i in 1:nmax
        x = h()
        xm = hgm()
        xp = hgp()
        y = ifloor(10x)
        ym = ifloor(10xm)
        yp = ifloor(10xp)
        println()
        println("Hickerson series result for n = ", i)
        println(@sprintf("    ->  %25.4f ", xm))
        println(@sprintf("    0>  %25.4f ", x))
        println(@sprintf("    +>  %25.4f ", xp))
        isprecok =
        isint =
        if ym == y == yp
            print("The precision is adequate, ")
            if  digits(y)[1] in [0, 9]
                println("and the result is an almost integer.")
            else
                println("but the result is not an almost integer.")
            end
        else
            println("The precision is inadequate for a definite result.")
        end
    end
end

a = log(big(2.0))
reporthickerson(a, 17)

a = log(2.0)
reporthickerson(a, 17)


include("GeometricCenter.jl")
include("MinDistance.jl")
include("PrincipalAxes.jl")
include(joinpath("RotationAngle","all.jl"))


function parse_positions(io)
    lines = readlines(io)
    return parse_ints_from_row(lines[1]; sep=",")
end

function fuelb(dist)
    return dist*(dist+1)/2
end

function consumed_fuel(positions, target; fuel_func=identity)
    return sum(fuel_func(abs(p-target)) for p in positions)
end

function median(values)
    svals = sort(values)
    n = length(svals)
    return svals[div(n, 2, RoundNearest)]
end

function day07a(positions)
    med = median(positions)
    return consumed_fuel(positions, med)
end

# Analytical solution to minimize_p sum_{x\in X} (x-p)(x-p+1)/2
# The minimizer shall be p = (2s+n)/(2n) where s = sum(X) and n = length(X).
# But it gives wrong result for the B part, and I do not know why.
# The minimizer is somewhere between 464 and 465, but the above computation returns 465.009...
# If the +n part is removed from the numerator, we have computation for the mean.
# But mean is a minimizer for sum x^2, which is not exactly what we are optimizing here...

function day07b_analytical(positions)
    f(p) = consumed_fuel(positions, p, fuel_func = fuelb)
    m = minimizer(positions)
    m1, m2 = Int(round(m, RoundDown)), Int(round(m, RoundUp))
    f1 = f(m1)
    f2 = f(m2)
    return convert(Int, min(f1, f2))
end

function minimizer(values)
    n = length(values)
    s = sum(values)
    return (2s + n) / (2n)
end

function day07b(positions)
    left, right = minimum(positions), maximum(positions)
    f(p) = consumed_fuel(positions, p, fuel_func = fuelb)
    popt = argmin(f, left:right)
    return convert(Int, f(popt))
end

function day07(io)
    positions = parse_positions(io)
    return (day07a(positions), day07b(positions))
end


# Author: Mathias Louboutin, mlouboutin3@gatech.edu
# Date: December 2020

using SlimOptim, LinearAlgebra, JOLI, TestImages, PyPlot
import TestImages: Gray

img = Float32.(Gray.(testimage("lighthouse.png")[1:2:end, 129:2:end-128]))

n = 256
k = 4
# Sparse in wavelet domain
W = joDWT(n, n; DDT=Float32, RDT=Float32)
# Or with curvelet ifi nstalled
# W = joCurvelet2D(128, 128; DDT=Float32, RDT=Float32)
A = vcat([joRomberg(n, n; DDT=Float32, RDT=Float32) for i=1:k]...)

# Make noisy data
imgn= img .+ .01f0*randn(Float32, size(img))
b = A*vec(imgn)

# setup bregamn
opt = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true)
opt2 = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true, spg=true)

sol = bregman(A, W, zeros(Float32, n*n), b, opt)
sol2 = bregman(A, W, zeros(Float32, n*n), b, opt2)

figure()
subplot(121)
plot(sol.ϕ_trace, label="std");plot(sol2.ϕ_trace, label="spg");
legend()
subplot(122)
loglog(sol.r_trace, label="std");loglog(sol2.r_trace, label="spg");
legend()

figure();
subplot(221)
imshow(img, cmap="Greys", vmin=-.5, vmax=.5)
title("True")
subplot(222)
imshow(reshape(b, n, k*n), cmap="Greys", vmin=-.5, vmax=.5)
title("Measurment")
subplot(223)
imshow(reshape(sol2.x, n, n), cmap="Greys", vmin=-.5, vmax=.5)
title("recovered")
subplot(224)
imshow(img - reshape(sol2.x, n, n), cmap="Greys", vmin=-.05, vmax=.05)
title("Difference x10")
tight_layout()


"""
    mean_overlap(a, y0, x0, x1, y1)

Mean overlap of an interval of length `a` and interval `[x0,x1]`,
when moving inside `[y0,y1]`.

Assumptions `a <= y1 - y0` and ` y0 <= x0 <= x1 <= y1`.

Return `result` with `min(x1 - x0, a) <= result <= a`.
"""
function mean_overlap(a::T, y0::T, x0::T, x1::T, y1::T) where T <: Real
    x0 = max(x0, y0)
    x1 = min(x1, y1)
    y0 <= x0 <= x1 <= y1 || throw(ArgumentError("violating ordering of boundaries"))
    a = min(a, y1 - y0)
    y10 = y1 - y0 - a
    if y10 <= (eps())*a
        return min(x1 - x0, a)
    end
    m11 = max(x0 - y0 - a, 0)
    m12 = max(min(x0+a, x1) - max(y0+a, x0), 0)
    d12 = (min(x0+a, x1) + max(y0+a, x0)) / 2 - x0
    m13 = max(min(x0+a, y1) - max(y0+a, x1), 0)
    m22 = max(x1 - x0 - a, 0)
    m23 = max(min(x1+a, y1) - max(x0+a, x1), 0)
    d23 = -(min(x1+a, y1) + max(x0+a, x1)) /2 + x1 + a
    m33 = max(y1 - x1 - a, 0)

    #println(m11 + m12 + m13 + m22 + m23 + m33 - y1 + y0 + a)
    #display([m11 m12 m13 m22 m23 m33;0 d12 x1 - x0 a d23 0])
    (m12*d12 + m13*(x1-x0) + m22*a + m23*d23) / y10
end

function sumeff(points, vmin, vmax, poly, pmin, pmax, xy, tol)
    pmi = pmin[xy] - tol
    pma = pmax[xy] + tol
    vmi = vmin[xy] - tol
    vma = vmax[xy] + tol
    sum(mean_overlap.(all_boxlengths(poly, xy) .+ 2tol, pmi, vmi, vma, pma))
end

function countmid(points, pmin, pmax, xy, tol)
    npoints = length(points)
    v = (vertex(points, k, xy) for k = 1:npoints)
    pmi = pmin[xy] - tol
    pma = pmax[xy] + tol
    low, mid, hi = sum([x < pmi, pmi <= x <= pma, x > pma] for x in v)
    mid
end

function estimate_effort(points, vmin, vmax, poly, pmin, pmax, tol)
    sumx = sumeff(points, vmin, vmax, poly, pmin, pmax, 1, tol)
    sumy = sumeff(points, vmin, vmax, poly, pmin, pmax, 2, tol)
    sumx0 = sumeff(points, vmin, vmax, poly, pmin, pmax, 1, 0.0)
    sumy0 = sumeff(points, vmin, vmax, poly, pmin, pmax, 2, 0.0)

    xmid = countmid(points, pmin, pmax, 1, tol)
    ymid = countmid(points, pmin, pmax, 2, tol)
    xmid0 = countmid(points, pmin, pmax, 1, 0.0)
    ymid0 = countmid(points, pmin, pmax, 2, 0.0)

    xmid * sumx, ymid * sumy, xmid0 * sumx0, ymid0 * sumy0
end


using Plots;
using Gadfly

W_fejer(omega,N) = 1/N * (sin(omega*N/2)/sin(omega/2))^2

N= 30
omega=range(-pi+1e-3,pi-1e-3,length=10000)
W0 = W_fejer.(1e-13,N)
plot(omega,W_fejer.(omega,N)./W0,yaxis=:log,label="",
xlabel="omega",ylabel="W_B(omega)/W(0)")
savefig("/home/thomas/atom-amd64/usr/share/atom/example/FejerKernel")


# Morphological open
""" For dim != 2, this function still creates a 2d circle.
"""
function kernelcircle(r, dim=2)
    n = 2 * ceil(Int, r) + 1
    A = centered(ones(Int32, n, n, ones(Int, dim-2)...))
    r2 = r^2
    for I in CartesianIndices(A)
        x, y = Tuple(I)
        if x^2 + y^2 > r2
            A[I] = 0
        end
    end
    A
end

function dilation(A::AbstractArray{T, D}, r, alg=ImageFiltering.Algorithm.FIR()) where {D, T}
    k = kernelcircle(r, D)
    imfilter(A, k, alg) .>= 0.5
end
function erosion(A::AbstractArray{T, D}, r, alg=ImageFiltering.Algorithm.FIR()) where {D, T}
    k = kernelcircle(r, D)
    imfilter(A, k, alg) .>= sum(k) - 0.5
end
function closing(A, r)
    A1 = dilation(A, r)
    erosion(A1, r)
end
function opening(A::AbstractArray{T, D}, r, alg) where {D, T}
    k = kernelcircle(r, D)
    A1 = imfilter(A, k, alg) .>= sum(k) - 0.5
    imfilter(A1, k, alg) .>= 0.5
end
function opening(A, r)
    opening(A, r, ImageFiltering.Algorithm.FIR())
end

function internalgradient(A::AbstractArray{T, D}) where {D, T}
    Bool.(A - erosion(A, 1))
end
function externalgradient(A::AbstractArray{T, D}) where {D, T}
    Bool.(dilation(A, 1) - A)
end
function gradient(A::AbstractArray{T, D}) where {D, T}
    Bool.(dilation(A, 1) - erosion(A, 1))
end


module ESS

using Statistics

export ess

function ess(X::Matrix{Float64}, cols::Vector{Int})
    p = length(cols)
    esses = zeros(p)
    for i = 1:p
        esses[i] = ess(X, cols[i])
    end
    return esses
end

function ess(X::Matrix{Float64})
    p = size(X, 2)
    return ess(X, collect(1:p))
end

function ess(X::Matrix{Float64}, col::Int)
    n = size(X, 1)
    ρ = 0.0
    for i = 1:(n - 3)
        p = lag_correlation(X, col, i)
        # @show i
        # @show p
        if p > 0.0
            ρ += p
        else
            break
        end
        # ρ += p
    end
    return n / (1 + 2 * ρ)
end

# function lag_correlation(X::Matrix{Float64}, col::Int, lag::Int)
#     n = size(X, 1)
#     x1 = X[1:(n - lag), col]
#     x2 = X[(lag + 1):n, col]
#     @assert length(x1) == length(x2)
#     ρ = cor(x1, x2)
#     return ρ
# end

function lag_correlation(X::Matrix{Float64}, col::Int, lag::Int)
    si = 0.0
    sj = 0.0
    ssi = 0.0
    ssj = 0.0
    sij = 0.0
    n = size(X, 1) - lag
    for i = 1:n
        j = i + lag
        x = X[i, col]
        y = X[j, col]

        si += x
        ssi += x * x
        sj += y
        ssj += y * y
        sij += x * y
    end

    ei = si / n
    ej = sj / n
    ei2 = ssi / n
    ej2 = ssj / n
    eij = sij / n

    sdi = sqrt(ei2 - ei * ei)
    sdj = sqrt(ej2 - ej * ej)
    covij = eij - ei * ej
    return covij / (sdi * sdj)
end

end


function cma_es_animated(f,x0;tol=1e-5,maxiter=1e3, animate=true,
       xrange=[-2., 3.], yrange=[-2.,6.])
  if animate
     # make a contour plot of the function we're minimizing. This is for
     # illustrating; you wouldn't have this normally
     x = range(xrange[1],xrange[2], length=100)
     y = range(yrange[1],yrange[2], length=100)
     c = contour(x,y,(x,y) -> log(f([x,y])) )
     anim = Animation()
   else
     anim = nothing
   end
    N=length(x0)
    λ=4+floor(3*log(N))
    m=x0
      σ=0.3
      μ=floor(λ/2)
      w=log(μ+0.5).-log.(1:μ)
      w=w/sum(w)
      μ_w=(sum(w)^2)/sum(w.^2)
      c_c=(4+μ_w/N)/(N+4+2*μ_w/N)
      c_σ=(μ_w+2)/(N+μ_w+5)
      c_1=2/((N+1.3)^2+μ_w)
      c_μ=min(1-c_1,
              2*(μ_w-2+1/μ_w)/((N+2)^2+μ_w) )
      damp_σ=1+c_σ+ 2*max(0.0,
                  sqrt( (μ_w-1)/(N+1) )-1    )
      p_c=zeros(N,1)
      p_σ=p_c
      B=I(N)
      D=ones(N,1)
      C=B*Diagonal(D.^2)*B'
      invsqrtC=B*Diagonal( D.^-1 )*B'
      eigeneval=0

      χ_N=sqrt(N)*(1-1/(4*N) +1/(21*N^2) )

      count=0
      normdiff=Inf
      N=length(x0)

      fx=zeros(Integer(λ))

      C=Diagonal(ones(N))
      equis=zeros(Integer(λ),N)

      function loop(equis,p_c,p_σ,c_1,c_μ,c_c,σ,C,m,
        B,D,invsqrtC,eigeneval)

          for count in 1:maxiter

          oldxbest=equis[1,:]
          for i in 1:Integer(λ)
            equis[i,:]=(σ.*B*(D.*randn(N,1))+m)'
            fx[i]=f(equis[i,:])
            #count=count+1
          end
          if animate
          # plot the search points
          p = deepcopy(c)
          scatter!(p, equis[:,1], equis[:,2], markercolor=:black,
          markeralpha=0.5, legend=false, xlims=xrange, ylims=yrange)
          end
          equis=equis[sortperm(fx),:]
          if animate
          # plot the best point so far
          scatter!(p, equis[1:Integer(μ),1], equis[1:Integer(μ),2],
           markercolor=:red, legend=false)
          end
          frame(anim)
          dm=(equis[1:Integer(μ),:]'-repeat(m,1,Integer(μ)))*w
          m=m+dm
          #dm=-dm
          p_σ=(1-c_σ)*p_σ+
                  ((sqrt(c_σ*(2-c_σ)*μ_w)*invsqrtC)*dm)/σ

          hsig=norm(p_σ)/sqrt(1-(1-c_σ)^(2*count/λ))χ_N<1.4+2/(N+1)

          p_c=(1-c_c)*p_c+(hsig*sqrt(c_c*(2-c_c)*μ)/σ) *dm

          aux=(1/σ).*(equis[1:Integer(μ),:]'-repeat(m,1,Integer(μ)) )
          C=(1-c_1-c_μ)*C+
                  c_1*(p_c*p_c'+(1-hsig)*c_c*(2-c_c)*C)+
                  c_μ*aux*Diagonal(w)*aux'
          σ=σ*exp( (c_σ/damp_σ)*(norm(p_σ)/χ_N-1) )

          if count-eigeneval>λ/(c_1+c_μ)/N/10
            eigeneval=count
            C=UpperTriangular(C)+UpperTriangular(C)'-Diagonal(C)
            B=eigvecs(C)
            D=eigvals(C).^(0.5)
          end
          invsqrtC=B'*Diagonal( D.^-1 )*B
          count=count+1
          normdiff=norm(equis[1,:]-oldxbest)
          #normdiff=norm(dm)
          if normdiff< tol || maximum(D)>1e7*minimum(D)
                  break
          end
          end
          return(f(equis[1,:]),equis[1,:],anim)
      end
      loop(equis,p_c,p_σ,c_1,c_μ,c_c,σ,C,m,B,D,invsqrtC,eigeneval)
end


using StaticArrays, LinearAlgebra, ForwardDiff, Plots

mutable struct Hopper{T}
    mb::T
    ml::T
    Jb::T
    Jl::T
    r::T
    μ::T
    g::T
end

kinematics(model, q) = @SVector [q[1] + q[3]*sin(q[5]), q[2] - q[3]*cos(q[5])]
# Dimensions
nq = 5 # configuration dim
nu = 2 # control dim
nc = 1 # number of contact points
nf = 2 # number of faces for friction cone pyramid

# Parameters
g = 9.81 # gravity
Δt = 0.1 # time step
μ = 0.5  # coefficient of friction
mb = 10. # body mass
ml = 1.  # leg mass
Jb = 2.5 # body inertia
Jl = 0.25 # leg inertia
r = 0.7 # nominal leg length
model = Hopper(mb,ml,Jb,Jl,r,μ,g)

# from "Dynamically Stable Legged Locomotion p. 144"

function gen_z(func,t,t_flight,t_stance)
    step = 0
    z = zero(t)

    for (i,tt) in enumerate(t)
        if tt > (step+0.5)*(t_flight + t_stance)
            step += 1
        end

        if step == 0
            z[i] = func(tt+0.5*t_flight)
        else
            z[i] = func(tt-((step-1+0.5)*t_flight + step*t_stance))
        end
    end
    return z
end
model.m
function raibert_trajectories(h::Hopper,ẋ,t_step,ρ,tf,T;compress=0.875)
    t_flight = (1-ρ)*t_step
    t_stance = ρ*t_step

    Jeff = h.Jb*h.Jl/(h.Jb+h.Jl)
    @show kh = ((2π/t_step)^2)*Jeff
    ωh = sqrt(kh/Jeff)
    z_min = compress*h.r

	@show kℓ = h.ml * (π / (ρ * t_step))^2.0
    @show dθ0 = ẋ/z_min

    θ_max = dθ0/ωh

    t = range(0,stop=tf,length=T)
    r_param = [1 1; cos(ωh*(t_flight*0.5+t_stance*0.5)) 1]\[h.r;z_min]

    @show z_max = 0.125*h.g*((1-ρ)^2)*t_step^2 + h.r*cos(dθ0/ωh*sin(ωh*t_flight*0.5))

    żlo = 0.5*t_flight*h.g
    zlo = z_max - 0.5/h.g*żlo^2
    function z_traj(i)
        zlo + żlo*i - 0.5*h.g*i^2
    end

    x = range(0,stop=ẋ*tf,length=T)
    z = gen_z(z_traj,t,t_flight,t_stance)
    r_traj = r_param[1]*cos.(ωh.*t) .+ r_param[2]
    θ = θ_max.*sin.(ωh.*t)
    ϕ = -Jl/Jb.*θ

    M_traj = kh * θ
    ṙ_traj = kℓ * (r_traj .- r)

    Q = [[x[t];z[t];r_traj[t]; ϕ[t];θ[t];] for t=1:T]
    U = [[M_traj[t];ṙ_traj[t]] for t=1:T]

    return Q, U, x, z, r_traj, ϕ, θ, M_traj, ṙ_traj, t
end

ẋ = 2
t_step = 0.5
ρ = 0.125
tf = 1.0
T = 1000
h = tf / (T - 1)
Q,U,_x,_z,_r,_θ,_ϕ,_M,_ṙ,_t = raibert_trajectories(model,ẋ,t_step,ρ,tf,T,compress=0.89)

plot(_t,_z,color=:red,width=2.,label="z")
plot!(_t,_r,color=:orange,label="r",width=2.)
plot!(_t,_θ,color=:yellow,label="theta",width=2.)
plot!(_t,_ϕ,color=:cyan,label="phi",width=2.)

plot(_t,_M)
plot!(_t,_ṙ)

include(joinpath(pwd(), "models/visualize.jl"))
vis = Visualizer()
render(vis)
visualize!(vis, model, Q, Δt = h)

# Visualization
function visualize!(vis, model::Hopper, q;
		Δt = 0.1, scenario = :vertical)

    r_foot = 0.05
    r_leg = 0.5 * r_foot

    setobject!(vis["body"], Sphere(Point3f0(0),
        convert(Float32, 0.1)),
        MeshPhongMaterial(color = RGBA(0, 1, 0, 1.0)))

    setobject!(vis["foot"], Sphere(Point3f0(0),
        convert(Float32, r_foot)),
        MeshPhongMaterial(color = RGBA(1.0, 165.0 / 255.0, 0, 1.0)))

    n_leg = 100
    for i = 1:n_leg
        setobject!(vis["leg$i"], Sphere(Point3f0(0),
            convert(Float32, r_leg)),
            MeshPhongMaterial(color = RGBA(0, 0, 0, 1.0)))
    end

    p_leg = [zeros(3) for i = 1:n_leg]
    anim = MeshCat.Animation(convert(Int, floor(1.0 / Δt)))

    for t = 1:length(q)
        p_body = [q[t][1], 0.0, q[t][2]]
        p_foot = [kinematics(model, q[t])[1], 0.0, kinematics(model, q[t])[2]]

        q_tmp = Array(copy(q[t]))
        r_range = range(0, stop = q[t][3], length = n_leg)
        for i = 1:n_leg
            q_tmp[3] = r_range[i]
            p_leg[i] = [kinematics(model, q_tmp)[1], 0.0, kinematics(model, q_tmp)[2]]
        end
        q_tmp[3] = q[t][3]
        p_foot = [kinematics(model, q_tmp)[1], 0.0, kinematics(model, q_tmp)[2]]

        z_shift = [0.0; 0.0; r_foot]

        MeshCat.atframe(anim, t) do
            settransform!(vis["body"], Translation(p_body + z_shift))
            settransform!(vis["foot"], Translation(p_foot + z_shift))

            for i = 1:n_leg
                settransform!(vis["leg$i"], Translation(p_leg[i] + z_shift))
            end
        end
    end

	# settransform!(vis["/Cameras/default"],
	# 	compose(Translation(0.0, 0.5, -1.0),LinearMap(RotZ(-pi / 2.0))))

    MeshCat.setanimation!(vis, anim)
end


"""
    init_input_layer(res_size::Int, in_size::Int, sigma::Float64)

Return a weighted input layer matrix, with random non-zero elements drawn from \$ [-\\text{sigma}, \\text{sigma}] \$, as described in [1].

[1] Lu, Zhixin, et al. "Reservoir observers: Model-free inference of unmeasured variables in chaotic systems." Chaos: An Interdisciplinary Journal of Nonlinear Science 27.4 (2017): 041102.
"""
function init_input_layer(res_size::Int,
        in_size::Int,
        sigma::Float64)

    W_in = zeros(Float64, res_size, in_size)
    q = floor(Int, res_size/in_size) #need to fix the reservoir input size. Check the constructor
    for i=1:in_size
        W_in[(i-1)*q+1 : (i)*q, i] = (2*sigma).*(rand(Float64, 1, q).-0.5)
    end
    return W_in

end

"""
    init_dense_input_layer(res_size::Int, in_size::Int, sigma::Float64)

Return a fully connected input layer matrix, with random non-zero elements drawn from \$ [-sigma, sigma] \$.
"""
function init_dense_input_layer(res_size::Int,
        in_size::Int,
        sigma::Float64)

    W_in = rand(Float64, res_size, in_size)
    W_in = 2.0 .*(W_in.-0.5)
    W_in = sigma .*W_in
    return W_in
end

"""
    init_sparse_input_layer(res_size::Int, in_size::Int, sigma::Float64, sparsity::Float64)

Return a sparsely connected input layer matrix, with random non-zero elements drawn from \$ [-sigma, sigma] \$ and given sparsity.
"""
function init_sparse_input_layer(res_size::Int,
        in_size::Int,
        sigma::Float64,
        sparsity::Float64)

    W_in = Matrix(sprand(Float64, res_size, in_size, sparsity))
    W_in = 2.0 .*(W_in.-0.5)
    replace!(W_in, -1.0=>0.0)
    W_in = sigma .*W_in
    return W_in
end

#from "minimum complexity echo state network" Rodan
"""
    min_complex_input(res_size::Int, in_size::Int, weight::Float64)

Return a fully connected input layer matrix with the same weights and sign drawn from a Bernoulli distribution, as described in [1].

[1] Rodan, Ali, and Peter Tino. "Minimum complexity echo state network." IEEE transactions on neural networks 22.1 (2010): 131-144.
"""
function min_complex_input(res_size::Int,
        in_size::Int,
        weight::Float64)

    W_in = Array{Float64}(undef, res_size, in_size)
    for i=1:res_size
        for j=1:in_size
            if rand(Bernoulli()) == true
                W_in[i, j] = weight
            else
                W_in[i, j] = -weight
            end
        end
    end
    return W_in
end

#from "minimum complexity echo state network" Rodan
#and "simple deterministically constructed cycle reservoirs with regular jumps" by Rodan and Tino

"""
    irrational_sign_input(res_size::Int, in_size::Int , weight::Float64 [, start::Int, irrational::Irrational])

Return a fully connected input layer matrix with the same weights and sign decided by the values of an irrational number, as described in [1] and [2].

[1] Rodan, Ali, and Peter Tino. "Minimum complexity echo state network." IEEE transactions on neural networks 22.1 (2010): 131-144.
[2] Rodan, Ali, and Peter Tiňo. "Simple deterministically constructed cycle reservoirs with regular jumps." Neural computation 24.7 (2012): 1822-1852.
"""
function irrational_sign_input(res_size::Int,
        in_size::Int,
        weight::Float64;
        start::Int = 1,
        irrational::Irrational = pi)

    setprecision(BigFloat, Int(ceil(log2(10)*(res_size*in_size+start+1))))
    ir_string = string(BigFloat(irrational)) |> collect
    deleteat!(ir_string, findall(x->x=='.', ir_string))
    ir_array = Array{Int}(undef, length(ir_string))
    W_in = Array{Float64}(undef, res_size, in_size)

    for i =1:length(ir_string)
        ir_array[i] = parse(Int, ir_string[i])
    end

    counter = start

    for i=1:res_size
        for j=1:in_size
            if ir_array[counter] < 5
                W_in[i, j] = -weight
            else
                W_in[i, j] = weight
            end
            counter += 1
        end
    end
    return W_in
end

"""
physics_informed_input(res_size::Int, in_size::Int, sigma::Float64, γ::Float64)

Return a weighted input layer matrix, with random non-zero elements drawn from \$ [-\\text{sigma}, \\text{sigma}] \$, where some γ
of reservoir nodes are connected exclusively to the raw inputs, and the rest to the outputs of the prior knowledge model , as described in [1].

[1] Jaideep Pathak et al. "Hybrid Forecasting of Chaotic Processes: Using Machine Learning in Conjunction with a Knowledge-Based Model" (2018)
"""
function physics_informed_input(res_size::Int,
        in_size::Int,
        sigma::Float64,
        γ::Float64,
        model_in_size::Int)

    state_size = in_size - model_in_size
    if state_size <= 0
        throw(DimensionMismatch("in_size must be greater than model_in_size"))
    end
    W_in = zeros(Float64, res_size, in_size)
    #Vector used to find res nodes not yet connected
    zero_connections = zeros(in_size)
    #Num of res nodes allotted for raw states
    num_for_state = floor(Int, res_size*γ)
    #Num of res nodes allotted for prior model input
    num_for_model = floor(Int, (res_size*(1-γ)))
    for i in 1:num_for_state
        #find res nodes with no connections
        idxs = findall(Bool[zero_connections == W_in[i,:] for i=1:size(W_in,1)])
        random_row_idx = idxs[rand(1:end)]
        random_clm_idx = range(1, state_size, step = 1)[rand(1:end)]
        W_in[random_row_idx,random_clm_idx] = rand(Uniform(-sigma, sigma))
    end

    for i in 1:num_for_model
        idxs = findall(Bool[zero_connections == W_in[i,:] for i=1:size(W_in,1)])
        random_row_idx = idxs[rand(1:end)]
        random_clm_idx = range(state_size+1, in_size, step = 1)[rand(1:end)]
        W_in[random_row_idx,random_clm_idx] = rand(Uniform(-sigma, sigma))
    end
    return W_in
end


import FastTransforms: FTPlan

abstract type LinearOperator{T} end

struct NonlinearOperator{F}
    N::F
end

struct SemiLinearPDE{D, T, LL, NN}
    L::LL
    N::NN
    U0::NTuple{D, Matrix{T}}
    function SemiLinearPDE{D, T, LL, NN}(L::LL, N::NN, U0::NTuple{D, Matrix{T}}) where {D, T, LL, NN}
        if D > 1
            @assert LL <: Tuple
            @assert NN <: Tuple
            @assert length(L) == D
            @assert length(N) == D
        end
        new{D, T, LL, NN}(L, N, U0)
    end
end

SemiLinearPDE(L, N, U0::NTuple{D, Matrix{T}}) where {D, T} = SemiLinearPDE{D, T, typeof(L), typeof(N)}(L, N, U0)
SemiLinearPDE(L, N, U0::Matrix{T}) where {T} = SemiLinearPDE(L, N, (U0,))

# ϵ^2 Δ
struct LaplaceBeltrami{T} <: LinearOperator{T}
    ϵ::T
end

# ϵ^2 ℒ_{δ}(α)
struct NonlocalLaplaceBeltrami{T} <: LinearOperator{T}
    ϵ::T
    α::T
    δ::T
end

function create_linear_operator(LB::LaplaceBeltrami{T}, m::Integer, n::Integer) where T
    L = zeros(T, m, 2n-1)
    ϵ = LB.ϵ
    @inbounds for i = 1:m
        L[i,1] = -ϵ^2*i*(i-1)
    end
    @inbounds for j = 1:n-1
        for i = 1:m-j
            L[i,2j] = L[i+j,1]
            L[i,2j+1] = L[i+j,1]
        end
    end
    L
end

function create_linear_operator(NLB::NonlocalLaplaceBeltrami{T}, m::Integer, n::Integer) where T
    L = zeros(T, m, 2n-1)
    λ = evaluate_lambda(m, NLB.α, NLB.δ)
    ϵ = NLB.ϵ
    @inbounds for i = 1:m
        L[i,1] = ϵ^2*λ[i]
    end
    @inbounds for j = 1:n-1
        for i = 1:m-j
            L[i,2j] = L[i+j,1]
            L[i,2j+1] = L[i+j,1]
        end
    end
    L
end

function evaluate_nonlinear_operator!(NL::NonlinearOperator, P::FTPlan{T, 2, FastTransforms.SPHERE}, PS::FTPlan{T, 2, FastTransforms.SPHERESYNTHESIS}, PA::FTPlan{T, 2, FastTransforms.SPHEREANALYSIS}, NU::Matrix{T}) where T
    # Compute spherical harmonic coefficients of a nonlinear operator N(u).

    # sph2fourier
    lmul!(P, NU)
    # Fourier to function values on sphere
    lmul!(PS, NU)
    NU .= NL.N.(NU)
    # Function values on sphere to Fourier
    lmul!(PA, NU)
    # fourier2sph
    ldiv!(P, NU)

    return NU
end

function evaluate_nonlinear_operator!(NL::Tuple{NonlinearOperator{F1}, NonlinearOperator{F2}}, P::FTPlan{T, 2, FastTransforms.SPHERE}, PS::FTPlan{T, 2, FastTransforms.SPHERESYNTHESIS}, PA::FTPlan{T, 2, FastTransforms.SPHEREANALYSIS}, NU::NTuple{2, Matrix{T}}, TEMP::Matrix{T}) where {T, F1, F2}
    # Compute spherical harmonic coefficients of a nonlinear operator N(u).

    # sph2fourier
    lmul!(P, NU[1])
    lmul!(P, NU[2])
    # Fourier to function values on sphere
    lmul!(PS, NU[1])
    lmul!(PS, NU[2])
    TEMP .= NU[1]
    NU[1] .= NL[1].N.(NU[1], NU[2])
    NU[2] .= NL[2].N.(TEMP, NU[2])
    # Function values on sphere to Fourier
    lmul!(PA, NU[1])
    lmul!(PA, NU[2])
    # fourier2sph
    ldiv!(P, NU[1])
    ldiv!(P, NU[2])

    return NU
end


module Parameters

export Param, setParameters

const BCDirichlet,BCRiemann = 1,2 # Constants for different boundary types

using TOML

# Parameters
struct Param
   nD::Int             # Dimension of the system
   LatticeType::Int    # DmQn lattice model
   Δx::Float64         # Discretized space interval
   Δt::Float64         # Discretized time interval
   α ::Float64         # Thermal diffusivity
   ω ::Float64         # Relaxation coefficient
   TotalTime::Float64  # Total simulation time
   nT::Int             # Total timesteps
   nI::Int             # Number of space intervals in x
   nJ::Int             # Number of space intervals in y

   Tᴸ::Float64     # Left boundary temperature
   Tᴿ::Float64     # Right boundary temperature
   Tᵀ::Float64     # Top boundary temperature
   Tᴮ::Float64     # Bottom boundary temperature

   BCtype::Int         # Type of boundary conditions

   function Param(nD::Int=2,LatticeType::Int=3,dx::Float64=1.0,dt::Float64=1.0,
      α::Float64=0.25,TotalTime::Float64=400.0,nI::Int=100,nJ::Int=100,
      Tleft::Float64=1.0,Tright::Float64=0.0,
      Ttop::Float64=0.0,Tbot::Float64=0.0,
      BCtype::Int=1)
      @assert(0 ≤ TotalTime, "Simulation time must be positive!")

      new(nD,LatticeType,dx,dt,α,inv(nD*dt*α/dx^2 + 0.5),TotalTime,
         Int(TotalTime/dt),nI,nJ,Tleft,Tright,Ttop,Tbot,BCtype)
   end
end

function setParameters()
   paramIn = TOML.parsefile("PARAM.toml")
   nD = paramIn["Parameters"]["nD"]
   LatticeType = paramIn["Parameters"]["LatticeType"]
   dx = paramIn["Parameters"]["dx"]
   dt = paramIn["Parameters"]["dt"]
   α = paramIn["Parameters"]["α"]
   TotalTime = paramIn["Parameters"]["TotalTime"]
   nI = paramIn["Parameters"]["nI"]
   nJ = paramIn["Parameters"]["nJ"]
   Tleft = paramIn["Parameters"]["Tleft"]
   Tright = paramIn["Parameters"]["Tright"]
   Ttop = paramIn["Parameters"]["Ttop"]
   Tbot = paramIn["Parameters"]["Tbot"]
   BCtype = paramIn["Parameters"]["BCtype"]

   if LatticeType == "D2Q4"
      LT = 1
   elseif LatticeType == "D2Q5"
      LT = 2
   elseif LatticeType == "D2Q9"
      LT = 3
   end

   if BCtype == "Dirichlet"
      BC = 1
   elseif BCtype == "Riemann"
      BC = 2
   end

   param = Param(nD,LT,dx,dt,α,TotalTime,nI,nJ,Tleft,Tright,Ttop,Tbot,BC)

end

end


# @doc doc"""
# lag{Float64}(x::Array{Float64}, n::Int64=1)
#
# Lag an array by `n` steps.
#
# - If `n` == 0, then `x` is returned.
# - If `n` > 0, then rows of `x` are shifted downwards `n` times.
# - If `n` < 0, then rows of `x` are shifted upwards `n` times.
# """ ->
# function lag{Float64}(x::Array{Float64}, n::Int64=1)
#     @assert abs(n)<size(x,1) "Argument n out of bounds."
#     if n == 0
#         return x
#     elseif n > 0
#         out = zeros(x)
#         out[1:n,:] = NaN
#         out[n+1:end,:] = x[1:end-n,:]
#         return out
#     elseif n < 0
#         out = zeros(x)
#         out[end+n+1:end,:] = NaN
#         out[1:end+n,:] = x[1-n:end,:]
#         return out
#     end
# end

# @doc doc"""
# Lag an array by `n` steps.
#
# - If `n` == 0, then `x` is returned.
# - If `n` > 0, then rows of `x` are shifted downwards `n` times.
# - If `n` < 0, then rows of `x` are shifted upwards `n` times.
# """ ->
# function lag{Float64}(x::Array{Float64}, n::Int64=1)
#     @assert abs(n)<size(x,1) "Argument n out of bounds."
#     if n == 0
#         return x
#     elseif n > 0
#         out = zeros(x)
#         out[1:n,:] = NaN
#         out[n+1:end,:] = x[1:end-n,:]
#         return out
#     elseif n < 0
#         out = zeros(x)
#         out[end+n+1:end,:] = NaN
#         out[1:end+n,:] = x[1-n:end,:]
#         return out
#     end
# end

@doc doc"""
Lagged differencing
""" ->
function diffn{Float64}(x::Vector{Float64}, n::Int64=1)
    @assert n<size(x,1) && n>0 "Argument n out of bounds."
    out = zeros(x)
    out[1:n] = NaN
    @inbounds for i=n+1:size(x,1)
        out[i] = x[i] - x[i-n]
    end
    return out
end

@doc doc"""
Lagged differencing
""" ->
function diffn{Float64}(X::Array{Float64,2}, n::Int64=1)
    @assert n<size(X,1) && n>0 "Argument n out of bounds."
    out = zeros(X)
    @inbounds for j = 1:size(X,2)
        out[:,j] = diffn(X[:,j], n)
    end
    return out
end

@doc doc"""
Compute the mode of an arbitrary array

(Adapted from StatsBase: https://raw.githubusercontent.com/JuliaStats/StatsBase.jl/master/src/scalarstats.jl)
""" ->
function mode{Float64}(a::AbstractArray{Float64})
    isempty(a) && error("mode: input array cannot be empty.")
    cnts = Dict{Float64,Int64}()
    # first element
    mc = 1
    mv = a[1]
    cnts[mv] = 1
    # find the mode along with table construction
    @inbounds for i = 2 : length(a)
        x = a[i]
        if haskey(cnts, x)
            c = (cnts[x] += 1)
            if c > mc
                mc = c
                mv = x
            end
        else
            cnts[x] = 1
            # in this case: c = 1, and thus c > mc won't happen
        end
    end
    return mv
end

@doc doc"""
Compute a running or rolling arithmetic mean of an array.
""" ->
function runmean{Float64}(x::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    out[1:n-1] = NaN
    if cumulative
        fi = 1.0:size(x,1)
        @inbounds for i = n:size(x,1)
            out[i] = sum(x[1:i])/fi[i]
        end
    else
        @inbounds for i = n:size(x,1)
            out[i] = mean(x[i-n+1:i])
        end
    end
    return out
end

@doc doc"""
Compute a running or rolling summation of an array.
""" ->
function runsum{Float64}(x::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    if cumulative
        out = cumsum(x)
        out[1:n-1] = NaN
    else
        out = zeros(x)
        out[1:n-1] = NaN
        @inbounds for i = n:size(x,1)
            out[i] = sum(x[i-n+1:i])
        end
    end
	return out
end

@doc doc"""
Welles Wilder summation of an array
""" ->
function wilder_sum{Float64}(x::Vector{Float64}, n::Int64=10)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    nf = float(n)  # type stability -- all arithmetic done on floats
    out = zeros(x)
    out[1] = x[1]
    @inbounds for i = 2:size(x,1)
        out[i] = x[i] + out[i-1]*(nf-1.0)/nf
    end
    return out
end

@doc doc"""
Compute the running or rolling mean absolute deviation of an array
""" ->
function runmad{Float64}(x::Vector{Float64}, n::Int64=10, cumulative::Bool=true; fun::Function=median)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    out[1:n-1] = NaN
    center = 0.0
    if cumulative
        fi = collect(1.0:size(x,1))
        @inbounds for i = n:size(x,1)
            center = fun(x[1:i])
            out[i] = sum(abs(x[1:i]-center)) / fi[i]
        end
    else
        fn = float(n)
        @inbounds for i = n:size(x,1)
            center = fun(x[i-n+1:i])
            out[i] = sum(abs(x[i-n+1:i]-center)) / fn
        end
    end
    return out
end

@doc doc"""
Compute the running or rolling variance of an array
""" ->
function runvar{Float64}(x::Vector{Float64}, n::Int64=10, cumulative=true)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    out[1:n-1] = NaN
    if cumulative
        @inbounds for i = n:size(x,1)
            out[i] = var(x[1:i])
        end
    else
        @inbounds for i = n:size(x,1)
            out[i] = var(x[i-n+1:i])
        end
    end
    return out
end

@doc doc"""
Compute the running or rolling standard deviation of an array
""" ->
function runsd{Float64}(x::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    return sqrt(runvar(x, n, cumulative))
end

@doc doc"""
Compute the running or rolling covariance of two arrays
""" ->
function runcov{Float64}(x::Vector{Float64}, y::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    @assert length(x) == length(y) "Dimension mismatch: length of `x` not equal to length of `y`."
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    out[1:n-1] = NaN
    if cumulative
        @inbounds for i = n:length(x)
            out[i] = cov(x[1:i], y[1:i])
        end
    else
        @inbounds for i = n:length(x)
            out[i] = cov(x[i-n+1:i], y[i-n+1:i])
        end
    end
    return out
end

@doc doc"""
Compute the running or rolling correlation of two arrays
""" ->
function runcor{Float64}(x::Vector{Float64}, y::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    @assert length(x) == length(y) "Dimension mismatch: length of `x` not equal to length of `y`."
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    out[1:n-1] = NaN
    if cumulative
        @inbounds for i = n:length(x)
            out[i] = cor(x[1:i], y[1:i])
        end
    else
        @inbounds for i = n:length(x)
            out[i] = cor(x[i-n+1:i], y[i-n+1:i])
        end
    end
    return out
end

@doc doc"""
Compute the running or rolling maximum of an array.
""" ->
function runmax{Float64}(x::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    if cumulative
        out[n] = maximum(x[1:n])
        @inbounds for i = n+1:size(x,1)
            out[i] = max(out[i-1], x[i])
        end
    else
        @inbounds for i = n:size(x,1)
            out[i] = maximum(x[i-n+1:i])
        end
    end
    out[1:n-1] = NaN
    return out
end

@doc doc"""
Compute the running or rolling minimum of an array.
""" ->
function runmin{Float64}(x::Vector{Float64}, n::Int64=10, cumulative::Bool=true)
    @assert n<size(x,1) && n>0 "Argument n is out of bounds."
    out = zeros(x)
    if cumulative
        out[n] = minimum(x[1:n])
        @inbounds for i = n+1:size(x,1)
            out[i] = min(out[i-1], x[i])
        end
    else
        @inbounds for i = n:size(x,1)
            out[i] = minimum(x[i-n+1:i])
        end
    end
    out[1:n-1] = NaN
    return out
end


# This file is based on the PolarFact package by weijianzhang
# License is MIT

export polar, PolarDecomposition

"""
    polar(A; algorithm::Symbol, maxiter, tol, verbose)

Compute the polar decomposition of the matrix `A`.

Returns a [`PolarDecomposition`](@ref).

Arguments:

`algorithm` (default: `:newton`) may be one of:
- ``:newton``: scaled Newton's method
- ``:qdwh``: the QR-based Dynamically weighted Halley (QDWH) method
- ``:halley``: Halley's method
- ``:schulz``: the Newton Schulz method
- ``:hybrid``: a hybrid Newton method
- ``:svd``: the SVD method

`maxiter` defaults to 100; `tol` defaults to `∛eps(t)`; `verbose` defaults to `false`.
"""
function polar end

"""
    PolarDecomposition <: Factorization

Matrix factorization type of the polar decomposition of a matrix ``A``.
This is the return type of [`polar(_)`](@ref), the corresponding matrix factorization function.

Every ``m-by-n`` matrix ``A`` has a polar decomposition

```math
A = U H
```

where the ``m-by-n`` matrix ``U`` has orthonormal columns if ``m>n``
or orthonormal rows if ``m<n`` and the ``n-by-n`` matrix ``H`` is
Hermitian positive semidefinite. For a square matrix ``A``, ``H`` is
unique. If in addition, ``A`` is nonsingular, then ``H`` is positive
definite and ``U`` is unique.

Iterating the decomposition produces the components ``U`` and ``H``.

"""
struct PolarDecomposition{T,M<:AbstractArray{T},Mh<:AbstractArray{T}} <: Factorization{T}
    U::M
    H::Mh
    niters::Integer
    converged::Bool

    function PolarDecomposition{T,M,Mh}(U, H,
                    niter::Integer,
                    converged::Bool) where {T,M<:AbstractArray{T},Mh<:AbstractArray{T}}
        require_one_based_indexing(U, H)
        size(U, 2) == size(H, 1) ||
               throw(DimensionMismatch("Inner dimension of U and H mismatch."))
        new{T,M,Mh}(U, H, niter, converged)
    end
end
function PolarDecomposition{T}(U::AbstractArray{T}, H::AbstractArray{T}, niter=1, converged=true) where {T}
    PolarDecomposition{T,typeof(U),typeof(H)}(U,H,niter,converged)
end

Base.iterate(P::PolarDecomposition) = (P.U, Val(:U))
Base.iterate(P::PolarDecomposition, ::Val{:U}) = (P.H, Val(:done))
Base.iterate(P::PolarDecomposition, ::Val{:done}) = nothing

module Polar
using LinearAlgebra

using ..MatrixFactorizations: PolarDecomposition

using Printf
# common types or functions

# the objective type

struct Objective{T}
    absolute::T  # absolute error
    relative::T  # relative error

    function Objective(absolute::T, relative::T) where {T <: Real}
        absolute >= 0 || error("absolute must be non-negative.")
        relative >= 0 || error("relative must be non-negative.")

        new{T}(absolute, relative)
    end
end

function evaluate_objv(preU::Matrix{T}, U::Matrix{T}) where {T}
    rel = opnorm(preU - U, Inf) / opnorm(preU, Inf)
    abs = opnorm(I - U'*U, Inf)
    return Objective(rel, abs)
end

abstract type PolarUpdater end

abstract type PolarAlg end

# common algorithm skeleton for iterative updating methods

function common_iter!(updater::PolarUpdater,
                         X::Matrix{T},
                         U::Matrix{T},
                         H::Matrix{T},
                         maxiter::Int,
                         verbose::Bool,
                         tol) where {T}

    preU = Array{T}(undef, size(X))
    copyto!(U, X)
    converged = false
    t = 0
    if verbose
        @printf("%-5s    %-13s    %-13s\n", "Iter.", "Rel. err.",  "Obj.")
    end

    while !converged && t < maxiter
        t += 1
        copyto!(preU, U)
        update_U!(updater, U)

        # determine convergence
        diff = norm(preU - U)
        if diff < tol
            converged = true
        end

        # display infomation
        if verbose
            objv = evaluate_objv(preU, U)
            @printf("%5d    %13.6e    %13.6e\n",
                    t, objv.absolute, objv.relative)
        end
    end

    # compute H
    mul!(H, U', X)
    H = (1/T(2)) * (H + H')
    return PolarDecomposition{T}(U, H, t, converged)
end

# Scaling iterative algorithm
function common_iter_scal!(updater::PolarUpdater,
                              X::Matrix{T},
                              U::Matrix{T},
                              H::Matrix{T},
                              maxiter::Int,
                              verbose::Bool,
                              tol) where {T}

    preU = Array{T}(undef, size(X))
    copyto!(U, X)
    converged = false
    t = 0
    if verbose
        @printf("%-5s    %-13s    %-13s\n", "Iter.", "Rel. err.",  "Obj.")
    end

    while !converged && t < maxiter
        t += 1
        copyto!(preU, U)
        update_U!(updater, U)

        # determine convergence
        diff = norm(preU - U)
        if diff < tol
            converged = true
        end

        # determine scaling
        reldiff = diff/norm(U) # relative error
        if updater.scale && (reldiff < updater.scale_tol)
            updater.scale = false
        end

        # display infomation
        if verbose
            objv = evaluate_objv(preU, U)
            @printf("%5d    %13.6e    %13.6e\n",
                    t, objv.absolute, objv.relative)
        end
    end

    # compute H
    mul!(H, U', X)
    H = (1/T(2)) * (H + H')
    return PolarDecomposition{T}(U, H, t, converged)
end

# Hybrid iteration algorithm
function common_iter_hybr!(updater1::PolarUpdater,
                              updater2::PolarUpdater,
                              X::Matrix{T},
                              U::Matrix{T},
                              H::Matrix{T},
                              maxiter::Int,
                              verbose::Bool,
                              tol,
                              theta) where {T} # theta is the switch parameter

    preU = Array{T}(undef, size(X))
    copyto!(U, X)
    converged = false
    switched = false
    t = 0
    if verbose
        @printf("%-5s    %-13s    %-13s\n", "Iter.", "Rel. err.",  "Obj.")
    end

    while !converged && t < maxiter
        t += 1
        copyto!(preU, U)

        if switched
            update_U!(updater2, U)
        else
            obj = opnorm(I - U'*U, 1)
            if obj > theta # theta is the switch parameter
                update_U!(updater1, U)
            else
                switched = true
                update_U!(updater2, U)
            end
        end

        # determine convergence
        diff = norm(preU - U)
        if diff < tol
            converged = true
        end

        # display infomation
        if verbose
            objv = evaluate_objv(preU, U)
            @printf("%5d    %13.6e    %13.6e\n",
                    t, objv.absolute, objv.relative)
        end
    end

    # compute H
    mul!(H, U', X)
    H = (1/T(2)) * (H + H')
    return PolarDecomposition{T}(U, H, t, converged)
end

"""
    Polar.NewtonAlg{T} <: Polar.PolarAlg

Newton's method for polar decomposition

Reference:
Nicholas J. Higham, Computing the Polar Decomposition ---with Applications,
SIAM J. Sci. Statist. Comput. Vol. 7, Num 4 (1986) pp. 1160-1174.
"""
mutable struct NewtonAlg{T,RT<:Real} <: PolarAlg
    maxiter::Int        # maximum number of iterations.
    scale::Bool         # whether to scale Newton iteration.
    verbose::Bool       # whether to show procedural information
    tol::RT        # tolerance for convergence
    scale_tol::RT  # tolerance for acceleration scaling

    function NewtonAlg{T}( ;maxiter::Integer=100,
                       verbose::Bool=false,
                       scale::Bool=true,
                       tol::RT=cbrt(eps(real(T))),
                       scale_tol::Real=eps(real(T))^(1/4)) where {T,RT<:Real}
        maxiter > 1 || error("maxiter must be greater than 1.")
        tol > 0 || error("tol must be positive.")
        scale_tol > 0 || error("scale_tol must be positive.")

        new{T,RT}(maxiter,
            scale,
            verbose,
            tol,
            RT(scale_tol))
    end
end

function solve!(alg::NewtonAlg{T},
                   X::Matrix{T}, U::Matrix{T}, H::Matrix{T}) where {T}
    common_iter_scal!(NewtonUpdater(alg.scale, alg.scale_tol), X, U, H, alg.maxiter, alg.verbose, alg.tol)
end

mutable struct NewtonUpdater{T} <: PolarUpdater
    scale::Bool
    scale_tol::T
end

function update_U!(upd::NewtonUpdater, U::Matrix{T}) where {T}
    scale = upd.scale
    Uinv = Array{T}(undef, size(U))
    Uinvt = Array{T}(undef, size(U))
    copyto!(Uinv, inv(U))

    # 1, Inf-norm scaling
    if scale
        g = (opnorm(Uinv,1) * opnorm(Uinv, Inf) / (opnorm(U,1) * opnorm(U, Inf)) )^(1/4)
    else
        g = one(T)
    end
    adjoint!(Uinvt, Uinv)
    copyto!(U, (g * U + Uinvt /g) / convert(T, 2))

end

"""
     Polar.NewtonSchulzAlg{T} <: Polar.PolarAlg

 Newton-Schulz algorithm for polar decomposition

 This method can only apply to matrix ``A`` such that ``norm(A) < sqrt(3)``.

Reference:
[3] Günther Schulz, Iterative Berechnung der reziproken Matrix, Z. Angew.
Math. Mech.,13:57-59, (1933) pp. 114, 181.
"""
mutable struct NewtonSchulzAlg{T,RT} <: PolarAlg
    maxiter::Int     # maximum number of iterations.
    verbose::Bool    # whether to show procedural information
    tol::RT     # convergence tolerance.

    function NewtonSchulzAlg{T}(; maxiter::Integer=100,
                             verbose::Bool=false,
                             tol::RT=cbrt(eps(real(T)))) where {T,RT<:Real}
        maxiter > 1 || error("maxiter must be greater than 1.")
        tol > 0 || error("tol must be positive.")

        new{T,RT}(Int(maxiter),
            verbose,
            tol)
    end
end

function solve!(alg::NewtonSchulzAlg,
                   X::Matrix{T}, U::Matrix{T}, H::Matrix{T}) where {T}

    # Newton Schulz converge quadratically if norm(X) < sqrt(3)

    # Revisor's note: this test makes the implementation pointless,
    # since opnorm does an SVD.
    # opnorm(X) < convert(real(T), sqrt(3)) || throw(ArgumentError("The norm of the input matrix must be smaller than sqrt(3)."))

    common_iter!(NewtonSchulzUpdater(), X, U, H, alg.maxiter, alg.verbose, alg.tol)
end

struct NewtonSchulzUpdater <: PolarUpdater end

function update_U!(upd::NewtonSchulzUpdater, U::Matrix{T}) where {T}
    UtU = Array{T}(undef, size(U))
    mul!(UtU, adjoint(U), U)
    copyto!(U, 0.5*U*(3*I - UtU))
end

#
# Compute Polar Decomposition via SVD
#

struct SVDAlg <: PolarAlg end

function solve!(alg::SVDAlg,
                X::Matrix{T}, U::Matrix{T}, H::Matrix{T}) where {T}

    F = svd(X, full = false)
    PQt = Array{T}(undef, size(U))
    mul!(PQt, F.U, adjoint(F.V))
    copyto!(U, PQt)
    copyto!(H, F.V * Diagonal(F.S) * F.Vt)
    H = (1/T(2)) * (H + H')

    return PolarDecomposition{T}(U, H, 1, true)
end

"""
    Polar.HalleyAlg{T} <: Polar.PolarAlg

Halley's method for the polar decomposition

Reference:
Y. Nakatsukasa, Z. Bai and F. Gygi, Optimizing Halley's iteration
for computing the matrix polar decomposition, SIAM, J. Mat. Anal.
Vol. 31, Num 5 (2010) pp. 2700-2720

"""
mutable struct HalleyAlg{T,RT} <: PolarAlg
    maxiter::Int
    verbose::Bool
    tol::RT

    function HalleyAlg{T}( ;maxiter::Integer=100,
                       verbose::Bool=false,
                       tol::RT = cbrt(eps(T))) where {T,RT<:Real}
        maxiter > 1 || error("maxiter must be greater than 1.")
        tol > 0 || error("tol must be positive.")

        new{T,RT}(maxiter,
            verbose,
            tol)
    end
end

function solve!(alg::HalleyAlg{T},
                X::Matrix{T}, U::Matrix{T}, H::Matrix{T}) where {T}
    common_iter!(HalleyUpdater(), X, U, H, alg.maxiter, alg.verbose, alg.tol)
end

struct HalleyUpdater <: PolarUpdater end

function update_U!(upd::HalleyUpdater, U::Matrix{T}) where {T}
    UtU = Array{T}(undef, size(U))
    mul!(UtU, adjoint(U), U)
    copyto!(U, U * (3*I + UtU)* inv(I + 3*UtU))
end

"""
    Polar.QDWHAlg{T} <: Polar.PolarAlg

QR-based Dynamically Weighted Halley (QDWH) algorithm for polar decomposition

Reference: Optimizing Halley's iteration for computing the matrix
           polar decomposition, Yuji Nakatsukasa, Zhaojun Bai and
           Francois Gygi, SIAM, J. Mat. Anal. Vol. 31, Num 5 (2010)
           pp. 2700-2720

Limitations: 1. the QDWH method should support `m > n` matrix itself in the future.
             2. the computing of `alpha` and `L` in `solve!` can be improved by using
                norm and condition number estimate, which were
                not available in Julia when written.
"""
mutable struct QDWHAlg{T,RT} <: PolarAlg
    maxiter::Int
    verbose::Bool
    piv::Bool       # whether to pivot
    tol::RT

    function QDWHAlg{T}( ;maxiter::Integer=100,
                     verbose::Bool=false,
                     piv::Bool=true,
                     tol::RT=cbrt(eps(T))) where {T,RT<:Real}
        maxiter > 1 || error("maxiter must be greater than 1.")
        tol > 0 || error("tol must be positive.")

        new{T,RT}(maxiter,
            verbose,
            piv,
            tol)
    end
end

function solve!(alg::QDWHAlg,
                X::Matrix{T}, U::Matrix{T}, H::Matrix{T}) where {T}
    # alpha is an estimate of the largest singular value of the
    # original matrix
    X_temp = Array{T}(undef, size(X))
    copyto!(X_temp, X)

    n = size(X_temp, 1)
    alpha = norm(X_temp)
    for i in length(X_temp)
        X_temp[i] /= alpha # form X0
    end

    # L is a lower bound for the smallest singular value of X0
    smin_est = opnorm(X_temp, 1)/cond(X_temp, 1)
    L  = smin_est/convert(T, sqrt(n))

    common_iter!(QDWHUpdater(alg.piv, L), X, U, H, alg.maxiter, alg.verbose, alg.tol)
end

mutable struct QDWHUpdater{T} <: PolarUpdater
    piv::Bool   # whether to pivot QR factorization
    L::T  # a lower bound for the smallest singluar value of each update matrix U
end

if VERSION < v"1.7-"
    ColumnNorm() = Val(true)
end

function update_U!(upd::QDWHUpdater, U::Matrix{T}) where {T}
    piv = upd.piv
    L = upd.L
    m, n = size(U)
    B = Array{T}(undef, m+n, n)
    Q1 = Array{T}(undef, n, n)
    Q2 = Array{T}(undef, n, n)
    # Compute paramters L, a, b, c
    L2 = L^2
    dd = try
        (4 * (1 - L2)/L2^2)^(1/3)
        catch
        (complex(4 * (1 -L2)/L2^2, 0))^(1/3)
    end
    sqd = sqrt(1+dd)
    a = sqd + 0.5 * sqrt(8 - 4 * dd + 8 * (2 - L2)/(L2 * sqd))
    a = real(a)
    b = (a - 1)^2 / 4
    c = a + b - 1

    # update L
    upd.L = L * (a + b * L2)/(1 + c * L2)

    copyto!(B, [sqrt(c)*U; Matrix(one(T)*I,n,n)])
    if piv
        F = qr(B, ColumnNorm())
    else
        F = qr(B)
    end
    copyto!(Q1, Matrix(F.Q)[1:m, :])
    copyto!(Q2, Matrix(F.Q)[m+1:end, :])
    copyto!(U, b / c * U + (a - b / c) / sqrt(c) * Q1 * Q2')

end

"""
    Polar.NewtonHybridAlg{T} <: Polar.PolarAlg

Hybrid Newton and Newton-Schulz algorithm for polar decomposition

Reference:
Nicholas J. Higham and Robert S. Schreiber, Fast Polar Decomposition
of an arbitrary matrix, SIAM, J. Sci. Statist. Comput. Vol. 11, No. 4
(1990) pp. 648-655.
"""
mutable struct NewtonHybridAlg{T,RT<:Real} <: PolarAlg
    maxiter::Int    # maximum number of iterations.
    verbose::Bool   # whether to show procedural information.
    tol::RT    # convergence tolerance
    theta::RT  # switch parameter

    function NewtonHybridAlg{T}( ; maxiter::Integer=100,
                             verbose::Bool=false,
                             tol::RT=cbrt(eps(real(T))),
                             theta::RT=convert(real(T), 0.6)) where {T,RT<:Real}
        maxiter > 1 || error("maxiter must  be greater than 1.")
        tol > 0 || error("tol must be positive.")
        theta > 0 || error("theta must be positive.")

        new{T,RT}(maxiter,
            verbose,
            tol,
            theta)
    end
end

function solve!(alg::NewtonHybridAlg,
                X::Matrix{T}, U::Matrix{T}, H::Matrix{T}) where {T}
    common_iter_hybr!(NewtonUpdater(true, eps(real(T))^(1/4)), NewtonSchulzUpdater(), X, U, H,
                      alg.maxiter, alg.verbose, alg.tol, alg.theta)
end

isiterative(alg::PolarAlg) = true
isiterative(alg::SVDAlg) = false

end # submodule Polar

function polar(A::AbstractMatrix{T};
                   alg::Symbol=:newton,
                   maxiter::Integer=100,
                   tol::Real = cbrt(eps(real(T))),
                   verbose::Bool=false) where {T}

    # choose algorithm
    algorithm =
       alg == :newton ? Polar.NewtonAlg{T}(; maxiter=maxiter, tol=tol, verbose=verbose) :
       alg == :qdwh ?  Polar.QDWHAlg{T}(; maxiter=maxiter, tol=tol, verbose=verbose) :
       alg == :halley ? Polar.HalleyAlg{T}(; maxiter=maxiter, tol=tol, verbose=verbose) :
       alg == :svd ? Polar.SVDAlg() :
       alg == :schulz ? Polar.NewtonSchulzAlg{T}(; maxiter=maxiter, tol=tol, verbose=verbose) :
       alg == :hybrid ? Polar.NewtonHybridAlg{T}(; maxiter=maxiter, tol=tol, verbose=verbose) :
       error("Invalid algorithm.")
    polar(A, algorithm)
end

"""
    polar(::AbstractMatrix, ::Polar.PolarAlg)
"""
function polar(A::AbstractMatrix{T}, algorithm::Polar.PolarAlg) where {T}
    # Initialization: if m > n, do QR factorization
    m, n = size(A)
    mm = m
    if m > n
        if Polar.isiterative(algorithm)
            m = n
            F = qr(A)
            A = F.R
        end
    elseif m < n
        throw(ArgumentError("The row dimension of the input matrix must be
              greater or equal to column dimension."))
    end
    U = Array{T}(undef, m, n)
    H = Array{T}(undef, n, n)
    # solve for polar factors
    r = Polar.solve!(algorithm, A, U, H)
    if mm > m
        return PolarDecomposition{T}(F.Q * r.U, r.H, r.niters, r.converged)
    end
    return r
end