using Meshing
using Test
using GeometryTypes
using ForwardDiff
using Profile
using Statistics: mean
using LinearAlgebra: dot, norm

@testset "meshing" begin
    @testset "surface nets" begin
        sdf_sphere = SignedDistanceField(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.))) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end
        sdf_torus = SignedDistanceField(HyperRectangle(Vec(-2,-2,-2.),Vec(4,4,4.)), 0.05) do v
            (sqrt(v[1]^2+v[2]^2)-0.5)^2 + v[3]^2 - 0.25
        end

        snf_torus = HomogenousMesh(HyperRectangle(Vec(-2,-2,-2.),Vec(4,4,4.)), (81,81,81), NaiveSurfaceNets()) do v
            (sqrt(v[1]^2+v[2]^2)-0.5)^2 + v[3]^2 - 0.25
        end

        snf_sphere = HomogenousMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), (21,21,21), NaiveSurfaceNets()) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        # test convience constructors
        HomogenousMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), NaiveSurfaceNets()) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end
        HomogenousMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), NaiveSurfaceNets(), size=(5,5,5)) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        sphere = HomogenousMesh(sdf_sphere, NaiveSurfaceNets())
        torus = HomogenousMesh(sdf_torus, NaiveSurfaceNets())
        @test length(vertices(sphere)) == 1832
        @test length(vertices(torus)) == 5532
        @test length(faces(sphere)) == 1830
        @test length(faces(torus)) == 5532

        @test length(vertices(sphere)) == length(vertices(snf_sphere))
        @test length(vertices(torus)) == length(vertices(snf_torus))
        @test length(faces(sphere)) == length(faces(snf_sphere))
        @test length(faces(torus)) == length(faces(snf_torus))
    end

    @testset "noisy spheres" begin
        # Produce a level set function that is a noisy version of the distance from
        # the origin (such that level sets are noisy spheres).
        #
        # The noise should exercise marching tetrahedra's ability to produce a water-
        # tight surface in all cases (unlike standard marching cubes).
        #
        N = 10
        sigma = 1.0
        distance = Float32[ sqrt(Float32(i*i+j*j+k*k)) for i = -N:N, j = -N:N, k = -N:N ]
        distance = distance + sigma*rand(2*N+1,2*N+1,2*N+1)

        # Extract an isosurface.
        #
        lambda = N-2*sigma # isovalue

        msh = HomogenousMesh(distance, MarchingTetrahedra(lambda))

        s2 = SignedDistanceField(HyperRectangle(Vec(0,0,0.),Vec(1,1,1.))) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        if "--profile" in ARGS
            HomogenousMesh(s2, MarchingTetrahedra())
            Profile.clear()
            @profile HomogenousMesh(s2, MarchingTetrahedra())
            #ProfileView.view()
        end

        msh = HomogenousMesh(s2, MarchingTetrahedra())
        @test length(vertices(msh)) == 973
        @test length(faces(msh)) == 1830
    end

    @testset "vertex interpolation" begin
        @test Meshing.vertex_interp(0, Vec(0,0,0), Vec(0,1,0), -1, 1) == Vec(0,0.5,0)
        @test Meshing.vertex_interp(-1, Vec(0,0,0), Vec(0,1,0), -1, 1) == Vec(0,0,0)
        @test Meshing.vertex_interp(1, Vec(0,0,0), Vec(0,1,0), -1, 1) == Vec(0,1,0)
    end

    @testset "marching cubes" begin

        algo = MarchingCubes()

        sdf = SignedDistanceField(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.))) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        mf = SimpleMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)),algo, size=(21,21,21)) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        mfrv = SimpleMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)),MarchingCubes(reduceverts=false), size=(21,21,21)) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        # convience constructors
        SimpleMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), MarchingCubes()) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end
        SimpleMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), MarchingCubes(), size=(5,6,7)) do v
            sqrt(sum(dot(v,v))) - 1 # sphere
        end

        @test length(vertices(mfrv)) == 10968
        m = SimpleMesh(sdf,algo)
        m2 = SimpleMesh(sdf,algo)
        @test length(vertices(m)) == 7320
        @test length(faces(m)) == 3656
        @test length(faces(mf)) == length(faces(mfrv))
        @test m == m2
        @test length(vertices(m)) == length(vertices(mf))
        @test length(faces(m)) == length(faces(mf))
    end

    @testset "respect origin" begin
        # verify that when we construct a mesh, that mesh:
        #   a) respects the origin of the SDF
        #   b) is correctly spaced so that symmetric functions create symmetric meshes
        f = x -> norm(x)
        bounds = HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2))
        resolution = 0.1
        sdf = SignedDistanceField(f, bounds, resolution)

        for algorithm in (MarchingCubes(0.5), MarchingTetrahedra(0.5))
            mesh = @inferred GLNormalMesh(sdf, algorithm)
            # should be centered on the origin
            @test mean(vertices(mesh)) ≈ [0, 0, 0] atol=0.15*resolution
            # and should be symmetric about the origin
            @test maximum(vertices(mesh)) ≈ [0.5, 0.5, 0.5]
            @test minimum(vertices(mesh)) ≈ [-0.5, -0.5, -0.5]
        end

        # Test functional Meshing
        mesh = @inferred GLNormalMesh(f, HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2)), (21,21,21), MarchingCubes(0.5))
        # should be centered on the origin
        @test mean(vertices(mesh)) ≈ [0, 0, 0] atol=0.15*resolution
        # and should be symmetric about the origin
        @test maximum(vertices(mesh)) ≈ [0.5, 0.5, 0.5]
        @test minimum(vertices(mesh)) ≈ [-0.5, -0.5, -0.5]

        # Naive Surface Nets has no accuracy guarantee, and is a weighted sum
        # so a larger tolerance is needed for this one. In addition,
        # quad -> triangle conversion is not functioning correctly
        # see: https://github.com/JuliaGeometry/GeometryTypes.jl/issues/169
        mesh = @inferred GLNormalMesh(sdf, NaiveSurfaceNets(0.5))
        # should be centered on the origin
        @test mean(vertices(mesh)) ≈ [0, 0, 0] atol=0.15*resolution
        # and should be symmetric about the origin
        @test maximum(vertices(mesh)) ≈ [0.5, 0.5, 0.5] atol=0.2
        @test minimum(vertices(mesh)) ≈ [-0.5, -0.5, -0.5] atol=0.2
    end

    @testset "AbstractMeshingAlgorithm interface" begin
        f = x -> norm(x) - 0.5
        bounds = HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2))
        resolution = 0.1
        sdf = SignedDistanceField(f, bounds, resolution)

        @testset "marching cubes" begin
            @test_nowarn GLNormalMesh(sdf, MarchingCubes())
            @inferred GLNormalMesh(sdf, MarchingCubes())
        end

        @testset "marching tetrahedra" begin
            @test_nowarn GLNormalMesh(sdf, MarchingTetrahedra())
            @inferred GLNormalMesh(sdf, MarchingTetrahedra())
            @test_nowarn GLNormalMesh(sdf.data, MarchingTetrahedra(0.5))
            @inferred GLNormalMesh(sdf.data, MarchingTetrahedra(0.5))
        end
        @testset "naive surface nets" begin
            @test_nowarn GLNormalMesh(sdf, NaiveSurfaceNets())
            @inferred GLNormalMesh(sdf, NaiveSurfaceNets())
        end
    end

    @testset "mixed types" begin
        # https://github.com/JuliaGeometry/Meshing.jl/issues/9
        samples = randn(10, 10, 10)
        m1 = HomogenousMesh(SignedDistanceField(
            HyperRectangle(Vec(0,0,0), Vec(1, 1, 1)),
            samples), MarchingTetrahedra())
        m2 = HomogenousMesh(SignedDistanceField(
            HyperRectangle(Vec(0,0,0), Vec(1, 1, 1)),
            Float32.(samples)), MarchingTetrahedra())
        @test all(isapprox.(vertices(m1), vertices(m2), rtol=1e-6))
        @test all(faces(m1) == faces(m2))

        @testset "forward diff" begin
            # Demonstrate that we can even take derivatives through the meshing algorithm
            f = x -> norm(x)
            bounds = HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2))
            resolution = 0.1
            sdf = SignedDistanceField(f, bounds, resolution)

            function surface_distance_from_origin(isovalue)
                # Compute the mean distance of each vertex in the isosurface
                # mesh from the origin. This should return a value equal to the
                # isovalue and should have a derivative of 1.0 w.r.t. the
                # isovalue. This function is just meant to serve as an example
                # of some quantity you might want to differentiate in a mesh,
                # and has the benefit for testing of having a trivial expected
                # solution.
                mesh = HomogenousMesh(sdf, MarchingTetrahedra(isovalue))
                mean(norm.(vertices(mesh)))
            end

            isoval = 0.85
            @test surface_distance_from_origin(isoval) ≈ isoval atol=1e-2
            @test ForwardDiff.derivative(surface_distance_from_origin, isoval) ≈ 1 atol=1e-2
        end

        @testset "type stability" begin
            # verify that we don't lose type stability just by mixing arguments
            # of different types
            data = randn(5, 5, 5)
            iso = 0.2
            eps = 1e-4
            @inferred(Meshing.marchingTetrahedra(data, iso, eps, Int))
            @inferred(Meshing.marchingTetrahedra(Float32.(data), Float64(iso), Float16(eps), Int32))
            @inferred(Meshing.marchingTetrahedra(Float64.(data), Float32(iso), Float64(eps), Int64))
        end
        @testset "Float16" begin
            sdf_torus = SignedDistanceField(HyperRectangle(Vec{3,Float16}(-2,-2,-2.),
                                                           Vec{3,Float16}(4,4,4.)),
                                            0.1, Float16) do v
                (sqrt(v[1]^2+v[2]^2)-0.5)^2 + v[3]^2 - 0.25
            end
            @test typeof(HomogenousMesh(sdf_torus,NaiveSurfaceNets())) ==
                         PlainMesh{Float16,Face{4,Int}}
            m2 = HomogenousMesh(sdf_torus,MarchingTetrahedra())
            m3 = HomogenousMesh(sdf_torus,MarchingCubes())
        end
    end
end


using Gadfly
using TSne

"""
Normalize `A` columns, so that the mean and standard deviation
of each column are 0 and 1, resp.
"""
function rescale(A, dim::Integer=1)
    res = A .- mean(A, dim)
    res ./= map!(x -> x > 0.0 ? x : 1.0, std(A, dim))
    res
end

if length(ARGS)==0
    println("usage:\n\tjulia demo.jl iris\n\tjulia demo.jl mnist")
    exit(0)
end

if ARGS[1] == "iris"
    using RDatasets
    println("Using Iris dataset.")
    iris = dataset("datasets","iris")
    X = convert(Matrix{Float64}, iris[:, 1:4])
    labels = iris[:, 5]
    plotname = "iris"
    initial_dims = -1
    iterations = 1500
    perplexity = 15
elseif ARGS[1] == "mnist"
    using MLDatasets
    println("Using MNIST dataset.")
    X, labels = MNIST.traindata(Float64);
    npts = min(2500, size(X, 2), size(labels))
    labels = labels[1:npts]
    X = rescale(X[:, 1:npts]')
    plotname = "mnist"
    initial_dims = 50
    iterations = 1000
    perplexity = 20
else
    error("Unknown dataset \"", ARGS[1], "\"")
end

println("X dimensions are: ", size(X))
Y = tsne(X, 2, initial_dims, iterations, perplexity)
println("Y dimensions are: ", size(Y))

writecsv(plotname*"_tsne_out.csv", Y)
open("labels.txt", "w") do io
    writedlm(io, labels)
end

theplot = plot(x=Y[:,1], y=Y[:,2], color=labels)
draw(PDF(plotname*".pdf", 4inch, 3inch), theplot)
#draw(SVG(plotname*".svg", 4inch, 3inch), theplot)


export TR_generic

# One dimensionnal trust region algorithm.
# We approximate our function h by a 2nd degree polynomial (Taylor) q.
# If q is a good approximation of h then we move towards the minimizer of q
# Otherwise we reduce our Trust region interval (Δ) until we have a good
# approximation.
# q(d) = h(t) + h'(t) * d + 0.5 * h''(t) * d²
#
# Specific key word arguments
# eps1 :: Float64. Below that treshold we don't move and reduce Δ.
#                  Treshold to determines if q is a bad approximation of h
# eps2 :: Float64. Over that treshold we augment the size of Δ
#                  Treshold to determines if q is a very good approximation of h
# red :: Float64. Rate at which we reduce Δ if we have a bad approximation
# aug :: Float64. Rate at which we augment Δ if we have a bad approximation
# Δ :: Float64. Size of the trust region at the begining of the algorithm.

function TR_generic(h :: AbstractNLPModel,
                    nlpstop :: AbstractStopping;
                    verbose :: Bool = true,
                    eps1 :: Float64 = 0.2,
                    eps2 :: Float64 = 0.8,
                    red:: Float64 = 0.5,
                    aug :: Float64 = 2.0,
                    Δ :: Float64 = 1.0,
                    direction :: Symbol = :Nwt)
    Δ = [Δ]
    (length(h.meta.x0) > 1) && warn("Not a 1-D problem ")
    t = h.meta.x0; iter = 0; # Establish starting point and iteration counter.
    fₖ = obj(h, t); f₀ = copy(fₖ)
    gₖ = grad(h, t); g₀ = copy(gₖ)

    # H denotes the (approximation of the) second derivative
    if direction == :Sec || direction == :SecA
      H = [1.0]
    else
      H = hess(h, t)
    end

    OK = update_and_start!(nlpstop, x = t, fx = fₖ, gx = gₖ, g0 = g₀, Hx = H)

    verbose && @printf(" iter  t         gₖ          Δ        pred         ared\n")
    verbose && @printf(" %4d %7.2e  %7.2e  %7.2e \n", iter, t[1], gₖ[1], Δ[1])

    # We loop until we have a minimizer or we have reached the maximum number of
    # iterations.
    while !OK
        dN = -gₖ ./ H;                 # (approximation of the) Newton direction
        d = TR_step_computation(H, gₖ, dN, Δ) # determines the right direction
                                              # depending on our trust region

        # Numerical reduction computation
        ftestTR = obj(h, t + d)        # Value of h and h' at t + d
        gtestTR = grad(h, t + d)

        # We check to see if we have a good approximation of h using the ratio
        # of the actual reduction and the predicted reduction.
        (pred, ared, ratio) =
            pred_ared_computation(gₖ, fₖ, H, d, ftestTR, gtestTR)

        if (ratio .< eps1) &&  (true in (abs.(d) .== Δ))
            # Bad approximation of h. Reduction of the size of the trust region
            Δ = red * Δ
            add_stop!(nlpstop.meta)
            # printstyled("on a reduit delta \n", color = :red)
        else
            # Good approximation we move towards the minimizer of q
            (t, fₖ, gₖ, H) =
                step_computation(direction, h, t, d, gₖ, fₖ, ftestTR, gtestTR)

            if ratio > eps2
                # Very good approximation of h. We increase our trust region.
                Δ = aug * Δ
            end
            OK = update_and_stop!(nlpstop, x = t, fx = fₖ, gx = gₖ, Hx = H)
            # @show OK
        end

        iter += 1
        verbose && @printf(" %4d %7.2e  %7.2e  %7.2e %7.2e %7.2e\n",
                           iter, t[1], gₖ[1], Δ[1], pred, ared)
    end

    optimal = nlpstop.meta.optimal
    # @show optimal
    return optimal, nlpstop
end


# List of macros that generate functions to interpolate data that contain hard-coded data
macro generateInputSine(mean, amplitude, freq, offset)
  quote
    function dummy(t)
      $mean + $amplitude*sin($freq*t + $offset)
    end
  end
end

macro generateInputStep(tstep,i0,i1)
  quote
    function dummy(t)
      if (t <= $tstep)
        $i0
      else
        $i1
      end
    end
  end
end

macro generateInputSlowStep(t0,t1,i0,i1)
  quote
    function dummy(t)
      if (t <= $t0)
        $i0
      else if ((t > $t0) & (t <= $t1))
        $i0 + (t - $t0)/($t1 - $t0)$i1
      else
        $i1
      end
    end
  end
end


using Revise

using Devito

configuration!("log-level", "DEBUG")
configuration!("language", "openmp")
configuration!("mpi", false)

x = SpaceDimension(name="x", spacing=Constant(name="h_x", value=5.0))
z = SpaceDimension(name="z", spacing=Constant(name="h_z", value=5.0))

function ricker!(src, f, _t, t₀)
    t = reshape(_t, size(src))
    src .= (1.0 .- 2.0 * (pi * f * (t .- t₀)).^2) .* exp.(-(pi * f * (t .- t₀)).^2)
end

grid = Grid(
    dimensions = (z,x),
    shape = (501,251),
    origin = (0.0,0.0),
    extent = (2500.0,1250.0),
    dtype = Float32)

b = Devito.Function(name="b", grid=grid, space_order=8)
v = Devito.Function(name="v", grid=grid, space_order=8)
q = Devito.Function(name="woverq", grid=grid, space_order=8)

b_data = data(b)
v_data = data(v)
q_data = data(q)

b_data .= 1
v_data .= 1.5
q_data .= 1/1000

time_range = 0.0f0:0.5f0:1000.0f0

p = TimeFunction(name="p", grid=grid, time_order=2, space_order=8)
z,x,t = dimensions(p)

src = SparseTimeFunction(name="src", grid=grid, npoint=1, nt=length(time_range))
src_coords = coordinates(src)
src_coords .= [625.0; 5.0]
src_data = data(src)
ricker!(src_data, 0.01, collect(time_range), 125)
src_term = inject(src; field=forward(p), expr=src*spacing(t)^2*v^2/b)

nz,nx,δz,δx = size(grid)...,spacing(grid)...
rec = SparseTimeFunction(name="rec", grid=grid, npoint=nx, nt=length(time_range))
rec_coords = coordinates(rec)
rec_coords[1,:] .= δx*(0:nx-1)
rec_coords[2,:] .= 10.0

rec_term = interpolate(rec, expr=p)

g1(field) = dx(field,x0=x+spacing(x)/2)
g3(field) = dz(field,x0=z+spacing(z)/2)
g1_tilde(field) = dx(field,x0=x-spacing(x)/2)
g3_tilde(field) = dz(field,x0=z-spacing(z)/2)

update_p = spacing(t)^2 * v^2 / b *
    (g1_tilde(b * g1(p)) + g3_tilde(b * g3(p))) +
    (2 - spacing(t) * q) * p +
    (spacing(t) * q - 1) * backward(p)

stencil_p = Eq(forward(p), update_p)

dt = step(time_range)
smap = spacing_map(grid)
smap[spacing(t)] = dt

op = Operator([stencil_p, src_term, rec_term], subs=smap, name="OpExampleIso")

summary = apply(op)
@show summary

_p = data(p)
d = data(rec)

using PyPlot

_p = data(p)

d = data(rec)

figure();imshow(_p[:,:,1])
display(gcf())

figure();imshow(d, aspect="auto",cmap="gray",clim=.1*[-1,1]*maximum(abs,d))
display(gcf())


if !isdefined(Main, :CustomStencil)
    include("../module/CustomStencil.jl")
    using Main.CustomStencil
end
include("misc.jl")
include("cpu_stencils.jl")

## Stencil Definition
coefs = Float64.([1; 0.5; 0.25; 0.125; 0.06])

nv_star_stencil = @def_stencil_expression -c[0]D[x,y,z] + @sum(i, 1, 4, c[i]*(
            D[x+i,y,z] + D[x,y+i,z] + D[x,y,z+i] +
            D[x-i,y,z] + D[x,y-i,z] + D[x,y,z-i]))

star_stencil = @def_stencil_expression -v*c[0]D[x,y,z] + v*@sum(i, 1, 4, c[i]*(
            D[x+i,y,z] + D[x,y+i,z] + D[x,y,z+i] +
            D[x-i,y,z] + D[x,y-i,z] + D[x,y,z-i]))

v_def = CreateStencilDefinition(star_stencil, coefs)
nv_def = CreateStencilDefinition(nv_star_stencil, coefs)
st_inst = NewStencilInstance(v_def, nv_def)

## Input Data size Definition
radius = 4
nx = 5
ny = 5
nz = 5
dx = 1<<(nx)
dy = 1<<(ny)
dz = 1<<(nz)

t_steps = 4
## GPU Implementation
original_data = CreateData(dx,dy,dz)
data = PadData(radius, original_data)
vsq = ones(Float32, dx, dy, dz)
vsq[10:15,:,:] .= 0.1
dout = ApplyStencil(st_inst, data, t_steps,vsq=vsq)
gpu_out = Array(dout)

## CPU Implementation
cpu_out =  cpu_star_stencil_v_nv(original_data, vsq, radius, coefs, t_steps)

## Check Result
mean_er = sum(abs.(cpu_out .- gpu_out))./(reduce(*, size(gpu_out)))
@assert(sum(abs.(cpu_out .- gpu_out))./(reduce(*, size(gpu_out))) < 0.001)
## Visual Check
# using Plots
# shift = -10
# Int(round(dz/2))+shift
# heatmap(cout[:,:,Int(round(dz/2))+shift])
# heatmap(o[:,:,Int(round(dz/2))+shift])


using StatsBase
import POMDPs: MDP, Policy

#
# Define a policy that will act as an crossentropy policy
#
type CrossentropyPolicy <: Policy
    action_map::Array{Float64,2}
    action_space::Vector{Int64}
end

#
# Default uniform intializer based on a games input
#
function CrossentropyPolicy(pomdp::MDP)
    action_count = n_actions(pomdp)
    default_map = ones(action_count, n_states(pomdp)) * 1.0 / action_count
    return CrossentropyPolicy(default_map, 1:action_count)
end

#
# Initialize policy with a predefined values
#
function CrossentropyPolicy(pomdp::MDP, values::Array{Float64,2})
    action_count = n_actions(pomdp)
    return CrossentropyPolicy(values, 1:action_count)
end

#
# Select random action from a policy based on a state and action weight
#
action(policy::CrossentropyPolicy, s::Int64) = sample(policy.action_space, Weights(policy.action_map[:, s]))


using StochasticDiffEq, Random
using DiffEqProblemLibrary.SDEProblemLibrary: importsdeproblems; importsdeproblems()
using DiffEqProblemLibrary.SDEProblemLibrary: prob_sde_2Dlinear
Random.seed!(100)

#Let the solver determine the initial stepsize for you!
sol = solve(prob_sde_2Dlinear, SRI())

# using Plots
# plot(sol,plot_analytic=true)
# gui()

#Make sure it does a good job
sol.t[2] > 1e-7


using Univariates
using Test
using DataFrames,Tables

@testset "Univariates.jl" begin
    # Write your own tests here.
    ## テスト用データセット
    try
        include("../src/makeirisdataset.jl")
    catch
        packagemasterpath=DEPOT_PATH[1]
        try
            include(joinpath(packagemasterpath,"packages/Univariates/src/makeirisdataset.jl"))
        catch
            include(joinpath(packagemasterpath,"dev/Univariates/src/makeirisdataset.jl"))
        end
    end

    INPUT=makeirisdataset()

    ## 検証用データセット sasで作成
    colname1=["SepalLength","SepalWidth","PetalLength","PetalWidth"]
    Var=[68.56935123,18.997941834,311.62778523,58.100626398]
    Std=[8.2806612798,4.3586628494,17.652982333,7.6223766896]
    Mean=[58.433333333,30.573333333,37.58,11.993333333]
    Min=[43,20,10,1]
    Quartile1st=[51,28,16,3]
    Median=[58,30,43.5,13]
    Quartile3rd=[64,33,51,18]
    Max=[79,44,69,25]

    Var=map(Float64,Var)
    Std=map(Float64,Std)
    Mean=map(Float64,Mean)
    Min=map(Float64,Min)
    Quartile1st=map(Float64,Quartile1st)
    Median=map(Float64,Median)
    Quartile3rd=map(Float64,Quartile3rd)
    Max=map(Float64,Max)

    testnum=DataFrame(colname=colname1,Var=Var,Std=Std,Mean=Mean,Min=Min,Quartile1st=Quartile1st,Median=Median,Quartile3rd=Quartile3rd,Max=Max)

    colname2=["setosa","versicolor","virginica"]
    count=[50,50,50]
    testnonnum=DataFrame(Species=colname2,count=count)

    @testset "test1 univariate" begin
        SummaryNum,SummaryNonNum=univariate(INPUT,graphplot=true)
        @test map(x->floor(x,digits=7),Tables.Matrix(SummaryNum[:,2:end])) == map(x->floor(x,digits=7),Tables.Matrix(testnum[:,2:end]))
        @test map(x->round(x,digits=8),Tables.Matrix(SummaryNum[:,2:end])) == map(x->round(x,digits=8),Tables.Matrix(testnum[:,2:end]))
        @test SummaryNonNum==testnonnum
    end

    @testset "test2 groupbycolunivariate" begin
        INPUT=makeirisdataset()
        groupbycol=:SepalLength
        staticstargetcol=:Species
        groupbycolSummary=groupbycolunivariate(INPUT,groupbycol,staticstargetcol)
        @test permutedims(Vector(groupbycolSummary[10,[:setosa,:versicolor,:virginica]]))==[3 1 0]
        @test permutedims(Vector(groupbycolSummary[15,[:setosa,:versicolor,:virginica]]))==[2 5 1]
        @test permutedims(Vector(groupbycolSummary[25,[:setosa,:versicolor,:virginica]]))==[0 3 5]

        groupbycol=:Species
        staticstargetcol=:SepalLength
        groupbycolSummary=groupbycolunivariate(INPUT,groupbycol,staticstargetcol)
        #sasでの算出結果
        sasans=[12.424897959 3.5248968721 50.06 43 48 50 52 58]
        result=Tables.Matrix(groupbycolSummary[groupbycolSummary[:,:colname] .=="setosa",[:Var,:Std,:Mean,:Min,:Quartile1st,:Median,:Quartile3rd,:Max]])
        @test map(x->floor(x,digits=7),result)==map(x->floor(x,digits=7),sasans)
        @test map(x->round(x,digits=8),result)==map(x->round(x,digits=8),sasans)
    end
end


function largest_independence_number(graph::AbstractGraph)
    return largest_clique_number(complement(graph))
end

function largest_independents_set(graph::AbstractGraph)
    return largest_cliques_set(complement(graph))
end

function largest_clique_number(graph::AbstractGraph)
    return length(largest_cliques_set(graph)[1])
end

function largest_cliques_set(graph::AbstractGraph)
    max_cliques = maximal_cliques(graph)
    largest_cliques = Vector{Vector{Integer}}()
    number = 0
    for clique = max_cliques
        lc=length(clique)
        number = lc>number ? lc : number
    end
    for clique = max_cliques
        if length(clique)==number; push!(largest_cliques, clique) end
    end

    return largest_cliques
end


using IntervalArithmetic

"""
    truncated_supports(udata::AbstractUncertainValueDataset; quantiles = [0.001, 0.999])

Truncate the furnishing distribution of each uncertain value in the dataset
to the provided `quantiles` range. Returns a vector of Interval{Float64}, one
for each value.
"""
function truncated_supports(udata::AbstractUncertainValueDataset;
            quantiles = [0.001, 0.999])
    n_vals = length(udata)

    # Using the provided quantiles, find the parts of the supports of the furnishing
    # distributions from which we're going to sample.
    supports = Vector{Interval{Float64}}(undef, n_vals)

    for i = 1:n_vals
        lowerbound = quantile(udata[i], minimum(quantiles))
        upperbound = quantile(udata[i], maximum(quantiles))

        supports[i] = interval(lowerbound, upperbound)
    end

    return supports
end

"""
    strictly_increasing_sequence_exists(udata::AbstractUncertainValueDataset;
        quantiles = [0.0001, 0.9999])

Does a path through the dataset exist? I.e, check that a strictly
increasing sequence can be found after first
constraining each distribution to the provided quantile range (this
is necessary because some distributions may have infinite support).
"""
function strictly_increasing_sequence_exists(udata, quantiles = [0.0001, 0.9999])
    n_vals = length(udata)
    sample = Vector{Float64}(undef, n_vals)

    # First, constrain all data such that the supports are all finite
    constrained_data = constrain(udata, TruncateQuantiles(quantiles...,))

    # Sample the first value in a way that ensures a strictly
    # increasing sequence from indices 2:end will exist.
    minima = minimum.(constrained_data)
    maxima = maximum.(constrained_data)

    increasing_sequence_exists = true

    for i = 2:n_vals - 1
        # Find the lower and upper bounds of the support from which
        # we can draw values while still ensuring an increasing
        # sequence of values.

        lo = minima[i]
        hi = minimum(maxima[i:end])
        if lo > hi
            increasing_sequence_exists = false
        end
    end

    return increasing_sequence_exists
end

"""
    strictly_decreasing_sequence_exists(udata::AbstractUncertainValueDataset;
        quantiles = [0.0001, 0.9999])

Does a path through the dataset exist? I.e,  check that a strictly
decreasing sequence can be found after first
constraining each distribution to the provided quantile range (this
is necessary because some distributions may have infinite support).
"""
function strictly_decreasing_sequence_exists(udata; quantiles = [0.0001, 0.9999])
    # If a strictly increasing sequence exists for the reversed dataset, then a strictly
    # decreasing sequence exists for the original dataset.
    strictly_increasing_sequence_exists(udata[end:-1:1], quantiles = quantiles)
end

export
strictly_increasing_sequence_exists,
strictly_decreasing_sequence_exists


using LightGraphs

function run_ttc_iteration(proposing_group::AbstractVector{Agent}, other_group::AbstractVector{Agent}, rankings::RankedPreferences)
  num_proposals = length(proposing_group)

  ## construct a  map from nodes to agents
  other_mapping = Dict()
  proposing_mapping = Dict()
  node_to_agent_map = Dict()
  for i in 1:length(other_group)
    idx = i + num_proposals
    other_mapping[other_group[i]] = idx
    node_to_agent_map[idx] = other_group[i]
  end
  for i in 1:length(proposing_group)
    proposing_mapping[proposing_group[i]] = i
    node_to_agent_map[i] = proposing_group[i]
  end

  g = SimpleDiGraph(length(proposing_group) + length(other_group))
  for i in 1:length(proposing_group)
    cur_student = proposing_group[i]
    target_school = 0
    for cur_school in rankings.ranking[cur_student]
      if haskey(other_mapping, cur_school)
        target_school = cur_school
        break
      end
    end
    add_edge!(g, i, other_mapping[target_school])
  end
  for i in 1:length(other_group)
    cur_school = other_group[i]
    target_student = 0
    for cur_student in rankings.ranking[cur_school]
      if haskey(proposing_mapping, cur_student)
        target_student = cur_student
        break
      end
    end
    cur_idx = i + num_proposals
    add_edge!(g, cur_idx, proposing_mapping[target_student])
  end

    # Find the cycles and the matches!
  matchings = Dict{Agent, Agent}()
  cycles = simplecycles(g)
  for cycle in cycles
    for i in 1:2:length(cycle)
      agent_1 = node_to_agent_map[cycle[i]]
      agent_2 = node_to_agent_map[cycle[i+1]]
      if agent_1.capacity > 0 && agent_2.capacity > 0
        agent_2.capacity = agent_2.capacity - agent_1.capacity_reduction
        agent_1.capacity = agent_1.capacity - agent_2.capacity_reduction
        if cycle[i] <= num_proposals
          matchings[agent_1] = agent_2
        else
          matchings[agent_2] = agent_1
        end
	  end
    end
  end
  new_proposing_group = Vector{Agent}()
  for proposer in proposing_group
    if proposer.capacity > 0
      push!(new_proposing_group, proposer)
    end
  end
  new_other_group = Vector{Agent}()
  for other in other_group
    if other.capacity > 0
      push!(new_other_group, other)
    end
  end
  matchings, new_proposing_group, new_other_group
end

function ttc_with_counter(proposing_group::AbstractVector{Agent}, other_group::AbstractVector{Agent}, rankings::RankedPreferences)
  matchings = Dict{Agent, Agent}()
  while length(proposing_group) != 0 && length(other_group) != 0
    new_matchings, proposing_group, other_group = run_ttc_iteration(proposing_group, other_group, rankings)
    merge!(matchings, new_matchings)
  end
  Match(matchings)
end


abstract type AbstractIFS <: Function end

abstract type AbstractIFSComplex <: AbstractIFS end

"""
An Iterated Function System formed by general Complex Contractions.
"""
struct IFSComplex{F <: Function} <: AbstractIFSComplex
  contractions::Vector{F}

  function IFSComplex{F}(funs::Vector{F}) where F <: Function
    new(deepcopy(funs))
  end
end

IFSComplex(funs::Vector{F}) where F <: Function = IFSComplex{F}(funs)

function (ifs::IFSComplex)(z::Number)
  n = rand(1:size(ifs))
  ifs.contractions[n](z), n
end

"""
An Iterated Function System formed by Complex Affine Contractions.
"""
struct IFSComplexAffine{F <: AbstractAffineTransformation} <: AbstractIFSComplex
  contractions::Vector{F}

  function IFSComplexAffine{F}(funs::Vector{F}) where F <: AbstractAffineTransformation
    new(deepcopy(funs))
  end
end

IFSComplexAffine(funs::Vector{F}) where F <: AbstractAffineTransformation =
IFSComplexAffine{F}(funs)

function (ifs::IFSComplexAffine)(z::Number)
  n = rand(1:size(ifs))
  ifs.contractions[n](z), n
end

contractions(ifs::AbstractIFS) = ifs.contractions
size(ifs::AbstractIFS) = length(ifs.contractions)


include("problems.jl")
using BenchmarkTools

using Distributed
addprocs(2)
@everywhere using CharibdeOptim
@everywhere using IntervalArithmetic

const SUITE = BenchmarkGroup()

for solver in (charibde_min, ibc_minimise)
    S = SUITE[string(solver)]
    s = S["Normal problems"]
    for prblm in (prblm_1, prblm_2, prblm_3)
        s[string(prblm)] = @benchmarkable($(solver), $(prblm.func), $(prblm.domain))


# Busjle et. al. 2009
# ===================

const _MI_MAT_TYPE = NamedArray{Float64,2,PairwiseListMatrix{Float64,false,Vector{Float64}},NTuple{2,OrderedDict{String,Int}}}

function _buslje09(aln, alphabet::A, clusters, lambda, apc) where A
    mi = mapcolpairfreq!(mutual_information, aln,
                         Counts{Float64,2,A}(ContingencyTable(Float64,Val{2},alphabet)),
                         Val{false}, # use diagonal
                         pseudocounts = AdditiveSmoothing{Float64}(lambda),
                         weights = clusters,
                         diagonalvalue = NaN)
    if apc
        APC!(mi)
    end
    mi
end

"""
`buslje09` takes a MSA or a file and a `FileFormat` as first arguments. It calculates a Z score
and a corrected MI/MIp as described on **Busjle et. al. 2009**.

keyword argument, type, default value and descriptions:

```
  - lambda      Float64   0.05    Low count value
  - clustering  Bool      true    Sequence clustering (Hobohm I)
  - threshold             62      Percent identity threshold for clustering
  - maxgap      Float64   0.5     Maximum fraction of gaps in positions included in calculation
  - apc         Bool      true    Use APC correction (MIp)
  - samples     Int       100     Number of samples for Z-score
  - fixedgaps   Bool      true    Fix gaps positions for the random samples
  - alphabet    ResidueAlphabet UngappedAlphabet()  Residue alphabet to be used
```

This function returns:

```
  - Z score
  - MI or MIp
```
"""
function buslje09(aln::AbstractMatrix{Residue};
                  lambda::Float64 = 0.05,
                  clustering::Bool = true,
                  threshold = 62,
                  maxgap::Float64 = 0.5,
                  apc::Bool = true,
                  samples::Int = 100,
                  alphabet::ResidueAlphabet = UngappedAlphabet(),
                  fixedgaps::Bool = true)::NTuple{2,_MI_MAT_TYPE}
    aln = filtercolumns(aln, gapfraction(aln,1) .<= maxgap)
    clusters = clustering ? hobohmI(aln, threshold) : NoClustering()
    mi = _buslje09(aln, alphabet, clusters, lambda, apc)
    if samples > 0
        rand_mi = Array{PairwiseListMatrix{Float64,false,Vector{Float64}}}(undef, samples)
        zmi = copy(mi)
        residuematrix = getresidues(aln)
        for ns in 1:samples
            shuffle!(residuematrix,1,fixedgaps)
            rand_mi[ns] = getarray(_buslje09(aln, alphabet, clusters, lambda, apc))
        end
        PairwiseListMatrices.zscore!(rand_mi, getarray(zmi))
        return(zmi, mi)
    else
        return(fill!(copy(mi),0.0), mi)
    end
end

function buslje09(filename::String, format::Type{T}; kargs...) where T <: FileFormat
    aln = read(filename, T, AnnotatedMultipleSequenceAlignment, generatemapping=true)
    buslje09(aln; kargs...)
end

# MIToS BLMI: Blosum MI
# =====================

function _BLMI(aln, clusters, alpha, beta, apc, lambda::Float64=0.0)
    mi = mapcolpairfreq!(mutual_information, aln,
                         Probabilities{Float64,2,UngappedAlphabet}(
                            ContingencyTable(Float64,Val{2},UngappedAlphabet())
                         ),
                         Val{false}, # use diagonal
                         pseudocounts = AdditiveSmoothing{Float64}(lambda),
                         weights = clusters,
                         diagonalvalue = NaN,
                         pseudofrequencies=BLOSUM_Pseudofrequencies(alpha,beta))
    if apc
        APC!(mi)
    end
    mi
end

"""
`BLMI` takes a MSA or a file and a `FileFormat` as first arguments. It calculates a Z score
(ZBLMI) and a corrected MI/MIp as described on **Busjle et. al. 2009** but using using
BLOSUM62 pseudo frequencies instead of a fixed pseudocount.

Keyword argument, type, default value and descriptions:

```
  - beta        Float64   8.512   β for BLOSUM62 pseudo frequencies
  - lambda      Float64   0.0     Low count value
  - threshold             62      Percent identity threshold for sequence clustering (Hobohm I)
  - maxgap      Float64   0.5     Maximum fraction of gaps in positions included in calculation
  - apc         Bool      true    Use APC correction (MIp)
  - samples     Int       50      Number of samples for Z-score
  - fixedgaps   Bool      true    Fix gaps positions for the random samples
```

This function returns:

```
  - Z score (ZBLMI)
  - MI or MIp using BLOSUM62 pseudo frequencies (BLMI/BLMIp)
```
"""
function BLMI(aln::AbstractMatrix{Residue};
              beta::Float64 = 8.512,
              threshold = 62,
              maxgap::Float64 = 0.5,
              apc::Bool = true,
              samples::Int = 50,
              fixedgaps::Bool = true,
              lambda::Float64 = 0.0)::NTuple{2,_MI_MAT_TYPE}
    aln = filtercolumns(aln, gapfraction(aln,1) .<= maxgap)
    clusters = hobohmI(aln, threshold)
    numbercl = Float64(nclusters(clusters))
    mi = _BLMI(aln, clusters, numbercl, beta, apc, lambda)
    if samples > 0
        rand_mi = Array{PairwiseListMatrix{Float64,false,Vector{Float64}}}(undef, samples)
        zmi = copy(mi)
        residuematrix = getresidues(aln)
        for ns in 1:samples
            shuffle!(residuematrix,1,fixedgaps)
            rand_mi[ns] = getarray(_BLMI(aln, clusters, numbercl, beta, apc, lambda))
        end
        PairwiseListMatrices.zscore(rand_mi, getarray(zmi))
        return(zmi, mi)
    else
        return(fill!(copy(mi),0.0), mi)
    end
end

function BLMI(filename::String, format::Type{T}; kargs...) where T <: FileFormat
    aln = read(filename, T, AnnotatedMultipleSequenceAlignment, generatemapping=true)
    BLMI(aln; kargs...)
end


include("euler/euler.jl")
using .Grid: max_path_sum

function compute(path::String)::Int
    triangle = map(x -> parse.(Int, split(x, " ")), split(read(path, String), "\n"))
    return max_path_sum(triangle)
end


# Rational approximations to generalized hypergeometric functions
# using Drummond's sequence transformation

# ₀F₀(;z)
function drummond0F0(z::T; kmax::Int = 10_000) where T
    if norm(z) < eps(real(T))
        return one(T)
    end
    ζ = inv(z)
    Nlo = ζ
    Dlo = ζ
    Tlo = Nlo/Dlo
    Nhi = (2ζ - 1)*Nlo + 2ζ
    Dhi = (2ζ - 1)*Dlo
    Thi = Nhi/Dhi
    k = 1
    Nhi, Nlo = ((k+2)*ζ-1)*Nhi + k*ζ*Nlo + ζ, Nhi
    Dhi, Dlo = ((k+2)*ζ-1)*Dhi + k*ζ*Dlo, Dhi
    Thi, Tlo = Nhi/Dhi, Thi
    k += 1
    while k < kmax && errcheck(Tlo, Thi, 10eps(real(T)))
        Nhi, Nlo = ((k+2)*ζ-1)*Nhi + k*ζ*Nlo, Nhi
        Dhi, Dlo = ((k+2)*ζ-1)*Dhi + k*ζ*Dlo, Dhi
        Thi, Tlo = Nhi/Dhi, Thi
        k += 1
    end
    return isfinite(Thi) ? Thi : Tlo
end

# ₁F₀(α;z)
function drummond1F0(α::T1, z::T2; kmax::Int = 10_000) where {T1, T2}
    T = promote_type(T1, T2)
    absα = abs(T(α))
    if norm(z) < eps(real(T)) || norm(α) < eps(absα)
        return one(T)
    end
    ζ = inv(z)
    Nlo = ζ/α
    Dlo = ζ/α
    Tlo = Nlo/Dlo
    Nhi = (2ζ - (α+1))*Nlo + 2ζ
    Dhi = (2ζ - (α+1))*Dlo
    Thi = Nhi/Dhi
    if norm(α+1) < eps(absα+1)
        return Thi
    end
    Nhi /= α+1
    Dhi /= α+1
    k = 1
    Nhi, Nlo = ((k+2)*ζ-(α+2k+1))*Nhi + k*(ζ-1)*Nlo + ζ, Nhi
    Dhi, Dlo = ((k+2)*ζ-(α+2k+1))*Dhi + k*(ζ-1)*Dlo, Dhi
    Thi, Tlo = Nhi/Dhi, Thi
    if norm(α+k+1) < eps(absα+k+1)
        return Thi
    end
    Nhi /= α+k+1
    Dhi /= α+k+1
    k += 1
    while k < kmax && errcheck(Tlo, Thi, 10eps(real(T)))
        Nhi, Nlo = ((k+2)*ζ-(α+2k+1))*Nhi + k*(ζ-1)*Nlo, Nhi
        Dhi, Dlo = ((k+2)*ζ-(α+2k+1))*Dhi + k*(ζ-1)*Dlo, Dhi
        Thi, Tlo = Nhi/Dhi, Thi
        if norm(α+k+1) < eps(absα+k+1)
            return Thi
        end
        Nhi /= α+k+1
        Dhi /= α+k+1
        k += 1
    end
    return isfinite(Thi) ? Thi : Tlo
end

# ₀F₁(β;z)
function drummond0F1(β::T1, z::T2; kmax::Int = 10_000) where {T1, T2}
    T = promote_type(T1, T2)
    if norm(z) < eps(real(T))
        return one(T)
    end
    ζ = inv(z)
    Nlo = β*ζ
    Dlo = β*ζ
    Tlo = Nlo/Dlo
    Nmid = ((β+1)*(2)*ζ - 1)*Nlo + (β+1)*(2)*ζ
    Dmid = ((β+1)*(2)*ζ - 1)*Dlo
    Tmid = Nmid/Dmid
    Nhi = ((β+2)*(3)*ζ - 1)*Nmid + (β+4)*ζ*Nlo + (β+4)*ζ
    Dhi = ((β+2)*(3)*ζ - 1)*Dmid + (β+4)*ζ*Dlo
    Thi = Nhi/Dhi
    k = 2
    Nhi, Nmid, Nlo = ((β+k+1)*(k+2)*ζ-1)*Nhi + k*(β+2k+2)*ζ*Nmid + k*(k-1)*ζ*Nlo + 2ζ, Nhi, Nmid
    Dhi, Dmid, Dlo = ((β+k+1)*(k+2)*ζ-1)*Dhi + k*(β+2k+2)*ζ*Dmid + k*(k-1)*ζ*Dlo, Dhi, Dmid
    Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
    k += 1
    while k < kmax && errcheck(Tmid, Thi, 10eps(real(T)))
        Nhi, Nmid, Nlo = ((β+k+1)*(k+2)*ζ-1)*Nhi + k*(β+2k+2)*ζ*Nmid + k*(k-1)*ζ*Nlo, Nhi, Nmid
        Dhi, Dmid, Dlo = ((β+k+1)*(k+2)*ζ-1)*Dhi + k*(β+2k+2)*ζ*Dmid + k*(k-1)*ζ*Dlo, Dhi, Dmid
        Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
        k += 1
    end
    return isfinite(Thi) ? Thi : isfinite(Tmid) ? Tmid : Tlo
end

# ₂F₀(α,β;z)
function drummond2F0(α::T1, β::T2, z::T3; kmax::Int = 10_000) where {T1, T2, T3}
    T = promote_type(T1, T2, T3)
    absα = abs(T(α))
    absβ = abs(T(β))
    if norm(z) < eps(real(T)) || norm(α*β) < eps(absα*absβ)
        return one(T)
    end
    ζ = inv(z)
    Nlo = ζ/(α*β)
    Dlo = ζ/(α*β)
    Tlo = Nlo/Dlo
    Nmid = (2ζ-(α+1)*(β+1))*Nlo + 2ζ
    Dmid = (2ζ-(α+1)*(β+1))*Dlo
    Tmid = Nmid/Dmid
    if norm((α+1)*(β+1)) < eps((absα+1)*(absβ+1))
        return Tmid
    end
    Nmid /= (α+1)*(β+1)
    Dmid /= (α+1)*(β+1)
    Nhi = (3ζ-(α+2)*(β+2)-(α+β+3))*Nmid - (α+β+3-ζ)*Nlo + ζ
    Dhi = (3ζ-(α+2)*(β+2)-(α+β+3))*Dmid - (α+β+3-ζ)*Dlo
    Thi = Nhi/Dhi
    if norm((α+2)*(β+2)) < eps((absα+2)*(absβ+2))
        return Thi
    end
    Nhi /= (α+2)*(β+2)
    Dhi /= (α+2)*(β+2)
    k = 2
    while k < kmax && errcheck(Tmid, Thi, 10eps(real(T)))
        Nhi, Nmid, Nlo = ((k+2)*ζ-(α+k+1)*(β+k+1)-k*(α+β+2k+1))*Nhi - k*(α+β+3k-ζ)*Nmid - k*(k-1)*Nlo, Nhi, Nmid
        Dhi, Dmid, Dlo = ((k+2)*ζ-(α+k+1)*(β+k+1)-k*(α+β+2k+1))*Dhi - k*(α+β+3k-ζ)*Dmid - k*(k-1)*Dlo, Dhi, Dmid
        Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
        if norm((α+k+1)*(β+k+1)) < eps((absα+k+1)*(absβ+k+1))
            return Thi
        end
        Nhi /= (α+k+1)*(β+k+1)
        Dhi /= (α+k+1)*(β+k+1)
        k += 1
    end
    return isfinite(Thi) ? Thi : isfinite(Tmid) ? Tmid : Tlo
end

# ₁F₁(α,β;z)
function drummond1F1(α::T1, β::T2, z::T3; kmax::Int = 10_000) where {T1, T2, T3}
    T = promote_type(T1, T2, T3)
    absα = abs(T(α))
    if norm(z) < eps(real(T)) || norm(α) < eps(absα)
        return one(T)
    end
    ζ = inv(z)
    Nlo = β*ζ/α
    Dlo = β*ζ/α
    Tlo = Nlo/Dlo
    Nmid = ((β+1)*(2)*ζ - (α+1))*Nlo + (β+1)*(2)*ζ
    Dmid = ((β+1)*(2)*ζ - (α+1))*Dlo
    Tmid = Nmid/Dmid
    if norm(α+1) < eps(absα+1)
        return Tmid
    end
    Nmid /= α+1
    Dmid /= α+1
    Nhi = ((β+2)*(3)*ζ - (α+3))*Nmid + ((β+4)*ζ-1)*Nlo + (β+4)*ζ
    Dhi = ((β+2)*(3)*ζ - (α+3))*Dmid + ((β+4)*ζ-1)*Dlo
    Thi = Nhi/Dhi
    if norm(α+2) < eps(absα+2)
        return Thi
    end
    Nhi /= α+2
    Dhi /= α+2
    k = 2
    Nhi, Nmid, Nlo = ((β+k+1)*(k+2)*ζ-(α+2k+1))*Nhi + k*((β+2k+2)*ζ-1)*Nmid + k*(k-1)*ζ*Nlo + 2ζ, Nhi, Nmid
    Dhi, Dmid, Dlo = ((β+k+1)*(k+2)*ζ-(α+2k+1))*Dhi + k*((β+2k+2)*ζ-1)*Dmid + k*(k-1)*ζ*Dlo, Dhi, Dmid
    Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
    if norm(α+k+1) < eps(absα+k+1)
        return Thi
    end
    Nhi /= α+k+1
    Dhi /= α+k+1
    k += 1
    while k < kmax && errcheck(Tmid, Thi, 10eps(real(T)))
        Nhi, Nmid, Nlo = ((β+k+1)*(k+2)*ζ-(α+2k+1))*Nhi + k*((β+2k+2)*ζ-1)*Nmid + k*(k-1)*ζ*Nlo, Nhi, Nmid
        Dhi, Dmid, Dlo = ((β+k+1)*(k+2)*ζ-(α+2k+1))*Dhi + k*((β+2k+2)*ζ-1)*Dmid + k*(k-1)*ζ*Dlo, Dhi, Dmid
        Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
        if norm(α+k+1) < eps(absα+k+1)
            return Thi
        end
        Nhi /= α+k+1
        Dhi /= α+k+1
        k += 1
    end
    return isfinite(Thi) ? Thi : isfinite(Tmid) ? Tmid : Tlo
end

# ₀F₂(α,β;z)
function drummond0F2(α::T1, β::T2, z::T3; kmax::Int = 10_000) where {T1, T2, T3}
    T = promote_type(T1, T2, T3)
    if norm(z) < eps(real(T)) || norm(α) < eps(real(T)) || norm(β) < eps(real(T))
        return one(T)
    end
    ζ = inv(z)
    Nlo = α*β*ζ
    Dlo = α*β*ζ
    Tlo = Nlo/Dlo
    k = 0
    Nmid2 = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Nlo + (2*(α+1)*(β+1))*ζ
    Dmid2 = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Dlo
    Tmid2 = Nmid2/Dmid2
    k = 1
    Nmid1 = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Nmid2 + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Nlo + (3*(α+β+3)+(α+1)*(β+1))*ζ
    Dmid1 = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Dmid2 + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Dlo
    Tmid1 = Nmid1/Dmid1
    k = 2
    Nhi = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Nmid1 + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Nmid2 + ζ*k*(k-1)*(3k+α+β+1)*Nlo + (14+2*(α+β))*ζ
    Dhi = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Dmid1 + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Dmid2 + ζ*k*(k-1)*(3k+α+β+1)*Dlo
    Thi = Nhi/Dhi
    k = 3
    Nhi, Nmid1, Nmid2, Nlo = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Nhi + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Nmid1 + ζ*k*(k-1)*(3k+α+β+1)*Nmid2 + ζ*k*(k-1)*(k-2)*Nlo + 6ζ, Nhi, Nmid1, Nmid2
    Dhi, Dmid1, Dmid2, Dlo = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Dhi + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Dmid1 + ζ*k*(k-1)*(3k+α+β+1)*Dmid2 + ζ*k*(k-1)*(k-2)*Dlo, Dhi, Dmid1, Dmid2
    Thi, Tmid1, Tmid2, Tlo = Nhi/Dhi, Thi, Tmid1, Tmid2
    k += 1
    while k < kmax && errcheck(Tmid1, Thi, 10eps(real(T)))
        Nhi, Nmid1, Nmid2, Nlo = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Nhi + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Nmid1 + ζ*k*(k-1)*(3k+α+β+1)*Nmid2 + ζ*k*(k-1)*(k-2)*Nlo, Nhi, Nmid1, Nmid2
        Dhi, Dmid1, Dmid2, Dlo = (ζ*(k+2)*(α+k+1)*(β+k+1)-1)*Dhi + ζ*k*((k+1)*(α+β+2k)+(α+k)*(β+k)+α+β+3k+2)*Dmid1 + ζ*k*(k-1)*(3k+α+β+1)*Dmid2 + ζ*k*(k-1)*(k-2)*Dlo, Dhi, Dmid1, Dmid2
        Thi, Tmid1, Tmid2, Tlo = Nhi/Dhi, Thi, Tmid1, Tmid2
        k += 1
    end
    return isfinite(Thi) ? Thi : isfinite(Tmid1) ? Tmid1 : isfinite(Tmid2) ? Tmid2 : Tlo
end

# ₂F₁(α,β,γ;z)
function drummond2F1(α::T1, β::T2, γ::T3, z::T4; kmax::Int = 10_000) where {T1, T2, T3, T4}
    T = promote_type(T1, T2, T3, T4)
    absα = abs(T(α))
    absβ = abs(T(β))
    if norm(z) < eps(real(T)) || norm(α*β) < eps(absα*absβ)
        return one(T)
    end
    ζ = inv(z)
    Nlo = γ*ζ/(α*β)
    Dlo = γ*ζ/(α*β)
    Tlo = Nlo/Dlo
    Nmid = ((2)*(γ+1)*ζ-(α+1)*(β+1))*Nlo + 2*(γ+1)*ζ
    Dmid = ((2)*(γ+1)*ζ-(α+1)*(β+1))*Dlo
    Tmid = Nmid/Dmid
    if norm((α+1)*(β+1)) < eps((absα+1)*(absβ+1))
        return Tmid
    end
    Nmid /= (α+1)*(β+1)
    Dmid /= (α+1)*(β+1)
    Nhi = ((3)*(γ+2)*ζ-(α+2)*(β+2)-(α+β+3))*Nmid + ((γ+4)*ζ-(α+β+3))*Nlo + (γ+4)*ζ
    Dhi = ((3)*(γ+2)*ζ-(α+2)*(β+2)-(α+β+3))*Dmid + ((γ+4)*ζ-(α+β+3))*Dlo
    Thi = Nhi/Dhi
    if norm((α+2)*(β+2)) < eps((absα+2)*(absβ+2))
        return Thi
    end
    Nhi /= (α+2)*(β+2)
    Dhi /= (α+2)*(β+2)
    k = 2
    Nhi, Nmid, Nlo = ((k+2)*(γ+k+1)*ζ-(α+k+1)*(β+k+1)-k*(α+β+2k+1))*Nhi + k*((γ+2k+2)*ζ-(α+β+3k))*Nmid + k*(k-1)*(ζ-1)*Nlo + 2ζ, Nhi, Nmid
    Dhi, Dmid, Dlo = ((k+2)*(γ+k+1)*ζ-(α+k+1)*(β+k+1)-k*(α+β+2k+1))*Dhi + k*((γ+2k+2)*ζ-(α+β+3k))*Dmid + k*(k-1)*(ζ-1)*Dlo, Dhi, Dmid
    Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
    if norm((α+k+1)*(β+k+1)) < eps((absα+k+1)*(absβ+k+1))
        return Thi
    end
    Nhi /= (α+k+1)*(β+k+1)
    Dhi /= (α+k+1)*(β+k+1)
    k += 1
    while k < kmax && errcheck(Tmid, Thi, 10eps(real(T)))
        Nhi, Nmid, Nlo = ((k+2)*(γ+k+1)*ζ-(α+k+1)*(β+k+1)-k*(α+β+2k+1))*Nhi + k*((γ+2k+2)*ζ-(α+β+3k))*Nmid + k*(k-1)*(ζ-1)*Nlo, Nhi, Nmid
        Dhi, Dmid, Dlo = ((k+2)*(γ+k+1)*ζ-(α+k+1)*(β+k+1)-k*(α+β+2k+1))*Dhi + k*((γ+2k+2)*ζ-(α+β+3k))*Dmid + k*(k-1)*(ζ-1)*Dlo, Dhi, Dmid
        Thi, Tmid, Tlo = Nhi/Dhi, Thi, Tmid
        if norm((α+k+1)*(β+k+1)) < eps((absα+k+1)*(absβ+k+1))
            return Thi
        end
        Nhi /= (α+k+1)*(β+k+1)
        Dhi /= (α+k+1)*(β+k+1)
        k += 1
    end
    return isfinite(Thi) ? Thi : isfinite(Tmid) ? Tmid : Tlo
end

# ₘFₙ(α;β;z)
function pFqdrummond(α::AbstractVector{T1}, β::AbstractVector{T2}, z::T3; kmax::Int = 10_000) where {T1, T2, T3}
    T = promote_type(T1, T2, T3)
    absα = abs.(T.(α))
    if norm(z) < eps(real(T)) || norm(prod(α)) < eps(prod(absα))
        return one(T)
    end
    ζ = inv(z)
    p = length(α)
    q = length(β)
    r = max(p+1, q+2)
    C = zeros(T, r)
    C[1] = one(T)
    Ĉ = zeros(T, r)
    Ĉ[1] = one(T)
    P = zeros(T, p+1)
    t = one(T)
    for j in 1:p
        t *= α[j]+1
    end
    P[1] = t
    err = one(real(T))
    for j in 1:p
        err *= absα[j]+1
    end
    Q = zeros(T, q+2)
    t = one(T)
    for j in 1:q
        t *= β[j]+1
    end
    Q[1] = 2t
    N = zeros(T, r+1)
    D = zeros(T, r+1)
    R = zeros(T, r+1)
    N[r+1] = prod(β)*ζ/prod(α)
    D[r+1] = prod(β)*ζ/prod(α)
    R[r+1] = N[r+1]/D[r+1]
    k = 0
    while k < r || (k < kmax && errcheck(R[r], R[r+1], 10eps(real(T))))
        for j in 1:r
            N[j] = N[j+1]
            D[j] = D[j+1]
            R[j] = R[j+1]
        end
        t1 = zero(T)
        for j in 0:min(k, q+1)
            t1 += Ĉ[j+1]*Q[j+1]*N[r-j]
        end
        if k ≤ q+1
            if p > q
                t1 += Q[k+1]
            else
                t1 += Q[k+1] / T(factorial(k+1))
            end
        end
        t2 = zero(T)
        t2 += Ĉ[1]*P[1]*N[r]
        for j in 1:min(k, p)
            t2 += P[j+1]*(C[j+1]*N[r-j+1] + Ĉ[j+1]*N[r-j])
        end
        N[r+1] = ζ*t1-t2
        t1 = zero(T)
        for j in 0:min(k, q+1)
            t1 += Ĉ[j+1]*Q[j+1]*D[r-j]
        end
        t2 = zero(T)
        t2 += Ĉ[1]*P[1]*D[r]
        for j in 1:min(k, p)
            t2 += P[j+1]*(C[j+1]*D[r-j+1] + Ĉ[j+1]*D[r-j])
        end
        D[r+1] = ζ*t1-t2
        R[r+1] = N[r+1]/D[r+1]
        if norm(P[1]) < eps(err)
            return R[r+1]
        end
        N[r+1] /= P[1]
        D[r+1] /= P[1]
        k += 1
        if p > q
            for j in min(k, max(p, q+1)):-1:1
                C[j+1] += C[j]
                Ĉ[j+1] += Ĉ[j]
            end
        else
            for j in min(k, max(p, q+1)):-1:1
                C[j+1] = (C[j+1]*(k+1-j) + C[j])/(k+1)
                Ĉ[j+1] = (Ĉ[j+1]*(k-j) + Ĉ[j])/(k+1)
            end
            Ĉ[1] = Ĉ[1]*k/(k+1)
        end
        t = one(T)
        for j in 1:p
            t *= α[j]+k+1
        end
        err = one(real(T))
        for j in 1:p
            err *= absα[j]+k+1
        end
        for j in 2:p+1
            s = t - P[j-1]
            P[j-1] = t
            t = s
        end
        P[p+1] = t
        t = one(T)
        for j in 1:q
            t *= β[j]+k+1
        end
        t *= k+2
        for j in 2:q+2
            s = t - Q[j-1]
            Q[j-1] = t
            t = s
        end
        Q[q+2] = t
    end
    return isfinite(R[r+1]) ? R[r+1] : R[r]
end


using JuMP
using Gurobi
using Plasmo
using PlasmoAlgorithms

##Place MP and SP into ModelGraph
mp = Model(solver = GurobiSolver(OutputFlag=0))
sp = Model(solver = GurobiSolver(OutputFlag=0))

@variable(mp,y>=0)
@objective(mp,Min,2y)

@variable(sp,x[1:2]>=0)
@variable(sp,y>=0)
@constraint(sp,c1,2x[1]-x[2]+3y>=4)
@constraint(sp,x[1]+2x[2]+y>=3)
@objective(sp,Min,2x[1]+3x[2])

## Plasmo Graph
g = ModelGraph()
setsolver(g, GurobiSolver(OutputFlag=0))
n1 = add_node(g)
setmodel(n1,mp)
n2 = add_node(g)
setmodel(n2,sp)

##Set n2 as a child node of n1
edge = Plasmo.add_edge(g,n1,n2)

## Linking constraints between MP and SP
@linkconstraint(g, n1[:y] == n2[:y])

r = bendersolve(g, max_iterations=5)


@testset "'MBESS' .............................. " begin

  ss = SampleSpace(5:20, 60)
  prior = p -> Distributions.pdf(Distributions.Beta(2 , 5), p)
  params = MBESS(ss, .2, .3, prior, a = 1, b = 1)
  design = optimaldesign(10, params, solver)

  @test expectedpower(design, prior) > .2

  params = MBESS(ss, .2, .3, prior, a = Inf, b = Inf, targetpower = 0.8)
  design = optimaldesign(10, params, solver)

  z =  z = quadgk(prior, mcrv(parameters(design)), 1)[1]
  tmp = fzero(p -> power(design, p) - .8, 0, 1)
  @test quadgk(prior, tmp, 1)[1]/z > 0.8

end


using Distributions, Random, LinearAlgebra
# This is a bad design change it
Base.@kwdef struct EKI{A,B,C,D,E}
    Γ::A
    Σ::B
    N::C
    Δt::D
    J::E
end

⊗(a, b) = a * b'

"""
eki_step!(u, forward_map, y̅, J, ξ, Γ, Δt)

# Description
Take a single step of the Ensemble Kalman Inversion (EKI) algorithm
# Langevin formulation
du = Cᵘᵖ Γ⁻¹(y- G(u)) dt + Cᵘᵖ Γ^(-1/2) dW
# Fokker-Planck formulation
∂ₜρ = - ∇ᵤ⋅([y- G(u)]ᵀΓ⁻¹Cov(u, G(u)) + 1/2 Tr(Cov(u, G)Γ⁻¹Cov(u, G) Hᵤ(ρ) )
where the covariances are calculated using the instantaneous density
and Hᵤ is the Hessian of ρ. Note the location of the diffusivity
# Heuristically it is not ∇⋅(κ∇ρ) but rather Trace(κ∇⊗∇ρ)

# Arguments
`u`: Vector{S} where S. OVERWRITTEN. Empirical prior distribution
`forward_map`: Function. Operates on objects of type S
`data`: Vector or Array
`J`: int, number of ensemble members
`ξ`: multivariate distribution,
`Γ`: matrix corresponding to regularization
`Δt`: timestep size

# Return
nothing
"""
function eki_step!(u, forward_map, data, J, ξ, Γ, Δt)
    u̅ = mean(u)
    G = forward_map.(u) # error handling needs to go here
    G̅ = mean(G)

    Cᵘᵖ = (u[1] - u̅) ⊗ (G[1] - G̅)
    Cᵖᵖ = (G[1] - G̅) ⊗ (G[1] - G̅)
    for j in 2:J
        Cᵘᵖ += (u[j] - u̅) ⊗ (G[j] - G̅)
        Cᵖᵖ += (G[j] - G̅) ⊗ (G[j] - G̅)
    end
    Cᵘᵖ *= 1 / J
    Cᵖᵖ *= 1 / J

    y = [data + rand(ξ) for j in 1:J]
    residual = y - G

    factored_Cᵖᵖ = cholesky(Symmetric(Cᵖᵖ + 1 / Δt * Γ))
    for j in 1:J
        u[j] += Cᵘᵖ * (factored_Cᵖᵖ \ residual[j])
    end
    return nothing
end

function eki_loop!(u, forward_map, y̅, J, Γ, Δt, N)
    ξ = MvNormal(1 / Δt * Γ)
    for n in 1:N
        eki_step!(u, forward_map, y̅, J, ξ, Γ, Δt)
    end
end

function calibrate!(x, G, y, method::EKI)
    (; J, Γ, Δt, N) = method
    eki_loop!(G, x, y, J, Γ, Δt, N)
end

function two_player_eki_step!(u, forward_map, data, J, ξ, Γ, Δt)
    u̅ = mean(u)
    G = forward_map.(u) # error handling needs to go here
    G̅ = mean(G)

    Cᵘᵘ = (u[1] - u̅) ⊗ (u[1] - u̅)
    Cᵘᵖ = (u[1] - u̅) ⊗ (G[1] - G̅)
    Cᵖᵖ = (G[1] - G̅) ⊗ (G[1] - G̅)
    for j in 2:J
        Cᵘᵘ += (u[j] - u̅) ⊗ (u[j] - u̅)
        Cᵘᵖ += (u[j] - u̅) ⊗ (G[j] - G̅)
        Cᵖᵖ += (G[j] - G̅) ⊗ (G[j] - G̅)
    end
    Cᵘᵘ *= 1 / J
    Cᵘᵖ *= 1 / J
    Cᵖᵖ *= 1 / J

    y = [data + rand(ξ) for j in 1:J]
    residual = y - G

    factored_Cᵖᵖ = cholesky(Symmetric(Cᵖᵖ + 1 / Δt * Γ))
    for j in 1:J
        u[j] += Cᵘᵖ * (factored_Cᵖᵖ \ residual[j])
    end

    for j in div(J, 2):J
        u[j] += logpdf(MvNormal(u̅, Cᵘᵘ), u[j])
    end

    return nothing
end


#Plot basic Gaussian Process

using Gadfly, GaussianProcesses

#Training data
d, n = 2, 50         #Dimension and number of observations

x = 2π * rand(d, n)
y = vec(sin(x[1,:]).*sin(x[2,:])) + 0.05*rand(n)

mZero = MeanZero()                    #Zero mean function
#kern = Mat(5/2,[0.0,0.0],0.0) + SE(0.0,0.0)        #Matern 5/2 ARD kernel with parameters log(l₁) = 0 log(l₂) = 0 and log(σ) = 0 and SE Iso kernel with parameters log(ℓ) = 0 and log(σ) = 0

kern = SE(0.0, 0.0)

gp = GP(x,y,mZero,kern,-2.0)          # Fit the GP
optimize!(gp)                         # Optimize the hyperparameters

push!(gp, [[2.0; 3.0] [3.0, 4.0]], [2.0, 3.0])
push!(gp, [2.0; 3.0], 2.0)
plot(gp; clim=(-10.0, 10.0,-10.0,10.0)) #Plot the GP over range clim


@rule BIFM(:out, Marginalisation) (m_in::MultivariateNormalDistributionsFamily, m_zprev::ProdFinal{<:MultivariateNormalDistributionsFamily}, m_znext::MultivariateNormalDistributionsFamily, meta::BIFMMeta) = begin

    # fetch information from meta data
    A       = getA(meta)
    B       = getB(meta)
    C       = getC(meta)
    H       = getH(meta)
    ξztilde = getξztilde(meta)
    BHBt    = getBHBt(meta)
    Λz      = getΛz(meta)

    # fetch statistics
    μ_in, Σ_in       = mean_cov(m_in)
    μ_zprev, Σ_zprev = mean_cov(m_zprev)

    # calculate intermediate quantities
    F = I - Λz * BHBt

    μ_ztilde = A * μ_zprev
    Σ_ztilde = A * Σ_zprev * A'

    μ_znext = F' * μ_ztilde + (B * (Σ_in * (B' * ξztilde))) + B * μ_in
    Σ_znext = F' * Σ_ztilde * F + BHBt

    # save required intermediate variables
    setΣu!(meta, Σ_in)

    # calculate outgoing message to output
    μ_out = C * μ_znext
    Σ_out = C * Σ_znext * C'

    # Actual return type depends on meta object as well, so we explicitly cast the result here
    # Should be noop if type matches
    T = promote_type(eltype(m_in), eltype(m_zprev), eltype(m_znext))

    # return outgoing marginal
    return ProdFinal(convert(MvNormalMeanCovariance{T}, MvNormalMeanCovariance(μ_out, Σ_out)))

end


using LinearAlgebra
using SparseArrays
using Random
Random.seed!(2)
include("../src/SocpSolver.jl")
using .SocpSolver

A = sparse(randn(2, 5))
c = rand(5)
b = A*randn(5)
G = -sparse(Diagonal(ones(5)))
h = zeros(5)
prob = LinearProgram(A, G, c, b, h)
sol, stats = solve(prob, Settings());


module PowerAnalysis
    using Distributions
    using HypothesisTests
    using Roots
    using QuadGK

    export
        power,
        effect_size,
        sample_size,
        type_1,
        type_2,
        type_s,
        type_m,
        exaggeration_factor

    include("utils.jl")
    include("generics.jl")
    include("t_test.jl")
    include("z_test.jl")
end


function kernel_relativity(xi::SVector, xj::SVector{3,T}, pj::SVector{3,T}) where {T}
    R = xi - xj
    return R / sqrt(dot(R, R) + dot(pj, R)^2 + eps())^3
end


srand(1)
using ExperimentalSVM
iris = readcsv(joinpath(dirname(@__FILE__), "iris.csv"))
irisX = float(iris[:, 1:4]')
p, n = size(irisX)
train = bitrand(n)
irisY = [species == "setosa" ? 1.0 : -1.0 for species in  iris[:, 5]]
iris_model_pegasos_optim = svm(irisX[:,train], irisY[train])
@assert (predict(iris_model_pegasos_optim, irisX[:, ~train])) == irisY[~train]
iris_model_cddual_optim = cddual(irisX[:,train], irisY[train])
@assert (predict(iris_model_cddual_optim, irisX[:, ~train])) == irisY[~train]
iris_model_cddual_shrinking_optim = cddual_shrinking(irisX[:,train], irisY[train])
@assert (predict(iris_model_cddual_shrinking_optim, irisX[:, ~train])) == irisY[~train]
rcvY, rcvX = read_svm_data(joinpath(dirname(@__FILE__), "rcv1_train.binary"))
rcv_model_pegasos_optim = svm(rcvX, rcvY)
rcv_model_cddual_optim = cddual(rcvX, rcvY)
rcv_model_cddual_shrinking_optim = cddual_shrinking(rcvX, rcvY)


@i function (:-=)(gaussian_log)(y!::GVar{T}, x::GVar{T}) where T
	y!.x -= gaussian_log(x.x)
	@routine @invcheckoff begin
		exp_x ← zero(x)
		jac ← zero(x)
		exp_x += exp(-x)
		exp_x += 1
		jac += 1/exp_x
	end
	x.g += y!.g * jac
	~@routine
end

@i function (:-=)(gaussian_nlog)(y!::GVar{T}, x::GVar{T}) where T
	y!.x -= gaussian_nlog(x.x)
	@routine @invcheckoff begin
		exp_x ← zero(x)
		jac ← zero(x)
		exp_x += exp(-x)
		exp_x -= 1
		jac -= 1/exp_x
	end
	x.g += y!.g * jac
	~@routine
end

@i function :(-=)(convert)(out!::GVar{Tx, Tg}, y::ULogarithmic) where {Tx, Tg}
	out! -= exp(y.log)
end


using LTC
using BenchmarkTools
using Zygote

function test()

  function generate_data()
      in_features = 2
      out_features = 1
      N = 48
      data_x = [sin.(range(0,stop=3π,length=N)), cos.(range(0,stop=3π,length=N))]
      data_x = [reshape([Float32(data_x[1][i]),Float32(data_x[2][i])],2,1) for i in 1:N]# |> f32
      data_y = [reshape([Float32(y)],1) for y in sin.(range(0,stop=6π,length=N))]# |> f32

      data_x = [repeat(x,1,20) for x in data_x]
      data_y = [repeat(x,1,20) for x in data_y]

      data_x, data_y
  end

  function lossf(x,y)
    Flux.reset!(ltc)
    ŷ = ltc.(x)
    sum(sum([(ŷ[i] .- y[i]) .^ 2 for i in 1:length(y)]))/length(y)#, ŷ
  end

  data_x,data_y = generate_data()

  ltc = NCP(Wiring(2,1))

  θ = Flux.params(ltc)

  @time gs = Zygote.gradient(θ) do
    lossf(data_x,data_y)#[1]
  end
  @time gs = Zygote.gradient(θ) do
    lossf(data_x,data_y)#[1]
  end
  @time gs = Zygote.gradient(θ) do
    lossf(data_x,data_y)#[1]
  end
end
@time test()


using Compat, ClassicalLaminateTheory

forcesAndMoments =
  @compat Dict(
    :Nx => 300,
    :Ny => 100,
    :Nxy => 0,
    :Mx => 5,
    :My => 0,
    :Mxy => 0)

materialProperties = @compat Dict(
  :E1 => 20.0*10^6,
  :E2 => 1.5*10^6,
  :G12 => 1.0*10^6,
  :ν12 => .34,
  :alpha1 => 0.2e-6,
  :alpha2 => 20e-6,
  :beta1 => 0.2e-4,
  :beta2 => 20e-4,
  :thickness => 0.0075)

laminateProperties = @compat Dict(
  :nplies => 4,
  :repeats => 1,
  :symmetric => true,
  :orientation => [0, 45, 90, -45],
  :deltaTemp => 0.0,
  :deltaMoisture => 0
)

createLaminate!(laminateProperties, materialProperties, forcesAndMoments)

ABDmat = [
  525698.0  155811.0       0.0    0.0    -0.0     0.0;
  155811.0  525698.0       0.0   -0.0     0.0     0.0;
       0.0       0.0  184943.0    0.0     0.0     0.0;
       0.0      -0.0       0.0  250.495  32.687  23.619;
      -0.0       0.0       0.0   32.687  93.036  23.619;
       0.0       0.0       0.0   23.619  23.619  41.427
];

@assert round(laminateProperties[:ABD]) == round(ABDmat)
println("Tests for CLT1 passed!")


@inline function safe_regress!(coef, XX, X, W, F_)
    try
        coef .= XX\(X'F_)
    catch
        @warn "Regression failed"
        coef .= 0.0
        coef[1] = dot(W, F_)
    end
    nothing
end

@inline function intercept_var_factor(XWX::Matrix{FT}) where {FT <: AbstractFloat}
    try
        max(zero(FT), inv(XWX)[1,1])
    catch
        FT(Inf)
    end
end

"""
    est = abc_regress(out, f, tol; kernel=out.cutoff.fun)

Calculate post-correction estimates for ABC-MCMC output using regression
correction of Beaumont (2002), and producing approximate confidence
intervals similar to `abc_postprocess`

# Arguments
- `out`: Output of the `abc_mcmc` function (with `record_summaries=true`)
- `f`: Function of interest.
- `tol`: Vector of tolerance values for which estimates are calculated.
- `kernel`: Kernel function used in regression correction.
- `est`: Estimates which may be inspected by `ABCPlots.plot_abc(est)`
"""
function abc_regress(out, f::Function, tol::T;
    kernel::Function=out.cutoff.fun,
    normal_quantile=FT(1.96),
    iact=nothing) where {FT <: AbstractFloat, T <: AbstractVector{FT}}
    if typeof(out.Summary) == Missing
        error("Summary statistics not recorded; please re-run abc_mcmc with 'record_summaries=true'")
    end
    F = eval_testfunction(out, f)
    (d_f,n) = size(F)
    n_tol = length(tol)
    E = Matrix{FT}(undef, d_f, n_tol)
    S = Matrix{FT}(undef, d_f, n_tol)
    ci_L = Matrix{FT}(undef, d_f, n_tol)
    ci_U = Matrix{FT}(undef, d_f, n_tol)

    d_s = size(out.Summary, 1)
    X = Matrix{FT}(undef, n, d_s+1)
    WX = Matrix{FT}(undef, n, d_s+1)
    W = Vector{FT}(undef, n)
    F_ = Vector{FT}(undef, n)
    phi_0 = out.cutoff.(out.Dist)
    invalid = phi_0 .== zero(FT)

    # Construct the regression data matrix X:
    for i in 1:n
        X[i, 1] = one(FT)
        X[i, 2:end] .= out.Summary[:,i] .- out.s_obs
    end

    # Calculate the integrated autocorrelation for
    # regression-corrected samples (using all!)
    tau0 = Vector{FT}(undef, d_f)
    XX = X'*X
    W .= one(FT)/n

    coef = Vector{FT}(undef, d_s+1)

    for j in 1:d_f
        F_ .= F[j,:]
        safe_regress!(coef, XX, X, W, F_)
        F_ .= F[j,:]
        F_ -= X*coef
        tau0[j] = iact(F_)
    end

    for i in 1:n_tol
        tol_ = tol[i]
        # Calculate unnormalised weights
        map!(x -> kernel(x/tol_), W, out.Dist)
        W .= W./phi_0
        W[invalid] .= zero(FT)

        # Normalise (and warn if cannot)
        Ws = sum(W)
        if Ws == zero(FT)
            @warn string("No matching observations for tolerance ", tol_)
            continue
        end
        W /= Ws

        # Calculate weighted regression
        WX .= X
        @inbounds for j in 1:n
            WX[j,:] *= W[j]
        end
        # The 'projection matrix' for tolerance tol_
        XWX = X'*WX
        for j in 1:d_f
            F_ .= F[j,:]
            # Calculate the regression coefficients:
            safe_regress!(coef, XWX, WX, W, F_)
            #coef = XWX\(WX'F_)
            # The mean estimate is simply the intercept:
            alpha = coef[1]
            E[j,i] = alpha
            # F_ = -(regression-corrected values)
            mul!(F_, X, coef)
            F_ -= F[j,:]
            # Calculate S:
            s_ = zero(FT)
            @inbounds for k in 1:n
                s_ += W[k]^2 * F_[k]^2
            end
            s_ *= intercept_var_factor(XWX)
            S[j,i] = s_
            # Confidence bounds:
            dx = normal_quantile*sqrt(s_*tau0[j])
            ci_L[j,i] = alpha - dx
            ci_U[j,i] = alpha + dx
        end
    end
    (E=E, S=S, eps=tol, ci_L=ci_L, ci_U=ci_U, normal_quantile=normal_quantile)
end


function util(c::AbstractFloat,γ::AbstractFloat)
  (c^(1.0-γ))/(1.0-γ)
end

function utilh(c::AbstractFloat,h::AbstractFloat,γ::AbstractFloat,η::AbstractFloat)
  ((c^(1.0-η)*h^η)^(1.0-γ))/(1.0-γ)
end


@testset "Poisson loss" begin
    N = 6
    img = abs.(randn((N, N)))

    @test 0.22741127776021886 ≈ poisson_aux([1.,2.], [3.,4.])

    @test all(0 .≈ Zygote.gradient(poisson_aux, img, img)[1])

    @test all( -1 .≈ Zygote.gradient(poisson_aux, [1.], [2.])[1])
end

@testset "Gaussian Loss" begin
    N = 6
    img = abs.(randn((N, N)))

    gauss = Gauss()
    @test 0 ≈ gauss(img, img)

    @test 0 ≈ gauss_aux(img, img)

    @test all(0 .≈ Zygote.gradient(gauss_aux, img, img)[1])

end

@testset "Scaled Gauss" begin
    N = 6
    img = abs.(randn((N, N)))

    scaled_gauss = ScaledGauss()

    @test 1.7047189562170502 ≈ scaled_gauss([5.], [2.])
    @test 1.7047189562170502 ≈ scaled_gauss_aux([5.], [2.])

    @test all( -1 .≈ Zygote.gradient(scaled_gauss_aux, [1.], [2.])[1])

end


include("../demo_utilities.jl")
include("../../src/cg_modified.jl")
using BenchmarkTools, Profile

n = 1024
A = Diagonal(collect(LinRange(1e-3, 1, n)).^2)
b = randn(n)
bNorm = norm(b)
function trial()
    cgm = ConjGradModified(A, b)
    ChangeStorageLimit(cgm, 1024)
    TurnOffReorthgonalize(cgm)
    ResNorm = norm(cgm.r)
    iterationCount = 0
    while ResNorm > 1e-10
        ResNorm = cgm()/bNorm
        iterationCount += 1
        if ResNorm == Inf || ResNorm == NaN
            error("Resnorm is inf or nan.")
        end
    end
    println("iterationCount: $iterationCount")
end
@time trial()
display(@benchmark trial())

# function Trial2()
#     cgm = ConjGradModified(A, b)
#     SetStorageLimit(cgm, 1024)
#     # TurnOffReorthgonalize(cgm)
#     ResNorm = norm(cgm.r)
#     iterationCount = 0
#     while ResNorm > 1e-10
#         ResNorm = cgm()/bNorm
#         iterationCount += 1
#         if ResNorm == Inf || ResNorm == NaN
#             error("Resnorm is inf or nan.")
#         end
#     end
#     println("iterationCount: $iterationCount")

# end
# @time Trial2()


using Base.Test, DataFrames, EventHistory, StatsModels

function test_show(x)
    io = IOBuffer()
    show(io, x)
end

d = DataFrame(start = [1,2,5,2,1,7,3,4,8,8],
              stop  = [2,3,6,7,8,9,9,9,14,17],
              event = [1,1,1,1,1,1,1,0,0,0],
              x     = [1,0,0,1,0,1,1,1,0,0]);
d[:S] = EventHistory.Event([:start,:stop,:event], d);
mm = EventHistory.phreg(@formula(S~x), d)

@test EventHistory.coef(mm) ≈ [-0.02110521] atol=1e-8
@test mm.invhess ≈ [0.6323063] atol=1e-6

@test mm.grad[1]<1e-6

p = EventHistory.predict(mm, X=[0; 1], time=[6]; order=true);
@test isapprox(p[2:3], [0.3522254,0.3599854], atol=1e-6)

d = DataFrame(start = [1,2,5,2,1,7,3,4,8,8],
              stop  = [2,3,6,7,8,9,9.5,10,14,17],
              event = [1,1,1,1,1,1,1,0,0,0],
              x     = [1,0,0,1,0,1,1,1,0,0],
              z     = [1.0,0,2.0,0,3.0,0,4.0,0,5.0,0]);
d[:S] = EventHistory.Event([:start,:stop,:event], d);
mm = EventHistory.phreg(@formula(S~x+z), d)
@test length(coef(mm)) == 2


immutable Spec1D{T<:Real,D<:LineDomains,
        G<:LineGroups,W<:RedundantWaveletClass} <: AbstractSpec{T,D,G,W}
    ɛ::Float64
    ϕmeta::ΦMeta
    ψmetas::Array{ΨMeta1D,3}
    class::W
    domain::D
    log2_size::Int
    max_qualityfactor::Float64
    max_scale::Float64
    motherfrequency::Float64
    n_filters_per_octave::Int
    n_octaves::Int
    pointgroup::G
    signaltype::Type{T}
    function (::Type{Spec1D}){T,D,G,W}( ;
            class::W = Morlet(),
            pointgroup::G = TrivialGroup(),
            signaltype::Type{T} = Float32,
            domain::D = FourierDomain(1),
            ɛ = default_ɛ(signaltype),
            log2_size = 15,
            max_qualityfactor = nothing,
            max_scale = Inf,
            n_filters_per_octave = nothing,
            n_octaves = nothing,
            tuningfrequency = nothing)
        """Maximum quality factor and number of filters per octave are
        set simultaneously, so that they default to each other if either
        is present."""
        max_qualityfactor, n_filters_per_octave =
             default_max_qualityfactor(max_qualityfactor, n_filters_per_octave),
             default_n_filters_per_octave(n_filters_per_octave, max_qualityfactor)
        """The center frequency of the mother wavelet (`motherfrequency`)
        may be tuned to user-defined frequency (e.g. 440 Hz in music) or left
        as a default."""
        motherfrequency =
            tune_motherfrequency(tuningfrequency, class, n_filters_per_octave)
        """By default, the number of octaves in the filter bank is such that
        all wavelet scales do not exceed the signal length, nor the
        user-defined maximum scale `max_scale` if present."""
        n_octaves = default_n_octaves(n_octaves, class, log2_size,
            max_qualityfactor, max_scale, motherfrequency, n_filters_per_octave)
        """The number of orientations of a one-dimensional filter bank is
        equal to 1 for real input (`pointgroup::TrivialGroup`), equal to 2 for
        complex input (`pointgroup::ReflectionGroup`)."""
        nΘs = get_n_orientations(pointgroup)
        ħ = uncertainty(class)
        """The meta-information of the wavelets is computed as a 3-d array,
        whose dimensions respectively correspond to
        * the orientation variable `θ`,
        * the chroma variable `χ`, and
        * the octave variable `j`."""
        ψmetas = Array{ΨMeta1D}(nΘs, n_filters_per_octave, n_octaves)
        for j in 0:(n_octaves-1), χ in 0:(n_filters_per_octave-1)
            γ = j * n_filters_per_octave + χ
            resolution = exp2(-γ / n_filters_per_octave)
            centerfrequency = motherfrequency * resolution
            unbounded_scale = ħ * max_qualityfactor / centerfrequency
            scale = min(unbounded_scale, max_scale)
            unbounded_q = scale * centerfrequency / ħ
            qualityfactor = clamp(unbounded_q, 1.0, max_qualityfactor)
            bandwidth = centerfrequency / qualityfactor
            for θ in 0:(nΘs-1)
                ψmetas[1+θ, 1+χ, 1+j] = ΨMeta1D(γ, θ, χ, bandwidth,
                    centerfrequency, j, qualityfactor, scale)
            end
        end
        """The bandwidth of the lowpass filter `ϕ` is such that the Fourier
        transform of `ϕ` will cover the low-frequency part of the spectrum
        that is not covered by the wavelets `ψs`."""
        ϕbandwidth = motherfrequency * exp2(-n_octaves)
        ϕscale = ħ / ϕbandwidth
        ϕmeta = ΦMeta(ϕbandwidth, ϕscale)
        spec = new{T,D,G,W}(ɛ, ϕmeta, ψmetas, class, domain, log2_size,
            max_qualityfactor, max_scale, motherfrequency, n_filters_per_octave,
            n_octaves, pointgroup, signaltype)
        """Before returning the `spec`, we call the function `checkspec`
        which enforces properties of the wavelet filter bank to satisfy null
        mean, limited spatial support, and Littlewood-Paley inequality."""
        checkspec(spec) && return spec
    end
end

checkspec(spec::Spec1D) = checkspec_super(spec) && return true

"""Given a dimensionless tuning frequency, returns the maximal admissible
mother frequency such that the subsequent wavelets will be in tune with the
tuning frequency.

For example, to tune a 12-chroma Morlet filter bank to a concert pitch of
440 Hz at a sample rate of 44,1 kHz:
    `Morlet1DSpec(n_filters_per_octave = 12, tuning_frequency = 440.0/44100.0)`
"""
function tune_motherfrequency(tuningfrequency, spectype, n_filters_per_octave)
    max_centerfrequency =
        default_motherfrequency(spectype, n_filters_per_octave)
    tuning_ratio = max_centerfrequency / tuningfrequency
    tuning_γ = floor(n_filters_per_octave*log2(tuning_ratio))
    return tuningfrequency * exp2(tuning_γ/n_filters_per_octave)
end
tune_motherfrequency(tuningfrequency::Void, spectype, n_filters_per_octave) =
    default_motherfrequency(spectype, n_filters_per_octave)


using BenchmarkTools
using Hydroelastics
using LinearAlgebra
using StaticArrays
import Hydroelastics: intersect_halfplanes, intersect_halfplanes_slow, HalfPlane, Point

macro run_bench(name, expr, args...)
    quote
        printstyled("Benchmark: ", $name, "\n"; color = :yellow)
        display(@benchmark esc($expr) $(args...))
        println("\n\n")
    end
end

tet1 = Object(
    Mesh(
        [
            0.0 1.0 0.0 0.0
            0.0 0.0 1.0 0.0
            0.0 0.0 0.0 1.0
        ],
        reshape([1, 2, 3, 4], :, 1),
        [0.0, 0.0, 0.0, 1.0],
    ),
)
tet2 = Object(
    Mesh(
        [
            0.0 1.0 0.0 0.0
            0.0 0.0 1.0 0.0
            1.8 1.8 1.8 0.8
        ],
        reshape([1, 2, 3, 4], :, 1),
        [0.0, 0.0, 0.0, 1.0],
    ),
)

@run_bench "Tet force" begin
    Hydroelastics.tet_force(tet1, tet2, 1, 1)
end

sphere = make_icosphere(2)
@run_bench "Icosphere force" begin
    local object1 = translate(sphere, @SVector [0.031, -0.5, 0.052])
    local object2 = translate(sphere, @SVector [0, 0.5, 0])
    compute_force(object1, object2)
end

halfplanes = [
    HalfPlane(Point(-0.16469227056401645, -0.2664776914569725), Point(-0.8506508083520399, 0.5257311121191335)),
    HalfPlane(Point(0.028287259696359788, -0.040547756753354335), Point(0.8201447309019166, 0.5721561153861965)),
    HalfPlane(Point(-0.16620458460952442, 0.09547326678562677), Point(-0.49810107091297723, -0.8671189786617204)),
    HalfPlane(Point(0.2970629430715192, -0.23999299562154927), Point(0.6284282897093981, 0.77786752387082)),
    HalfPlane(Point(-0.05197402131154934, 0.06002998491806123), Point(-0.7560128937297079, -0.6545567236797993)),
    HalfPlane(Point(0.1879473775300143, 0.30410524493997143), Point(0.85065080835204, -0.5257311121191336)),
    HalfPlane(Point(0.1619918586413753, -0.0851641599977757), Point(0.46534112719498627, 0.8851314226378474)),
    HalfPlane(Point(0.2970629430715192, -0.23999299562154938), Point(0.6284282897093982, 0.7778675238708199)),
]

@run_bench "Original halfplane algorithm using Polyhedra" begin
    intersect_halfplanes_slow(halfplanes)
end

@run_bench "New halfplane algorithm" begin
    intersect_halfplanes(halfplanes)
end


function triangle(adj_mtx_file)
    if ~isfile( adj_mtx_file )
        println("unable to open input file");
        return (-1);
    end

    # load input data
    t_read_adj=@elapsed ii = readdlm( adj_mtx_file, '\t', Int64);
    println("adjacency matrix read time : ", t_read_adj);

    t_create_adj=@elapsed A = sparse( ii[:,1], ii[:,2], ii[:,3] );
    println("sparse adj. matrix creation time : ", t_create_adj);

    tic;
    nt = sum( A^2 .*  A )/6;
    t = toc;
    println("triangle count time : ", t);
    println("number of triangles : ", nt);

    return(nt)
end

#######################################################
# Graph Challenge Benchmark
# Developer : Dr. Siddharth Samsi (ssamsi@mit.edu)
#
# MIT
########################################################
# (c) <2017> Massachusetts Institute of Technology
########################################################


module test_stdlib_printf

using Printf
using Test

@test Printf.@sprintf("%.7f", 1/11025) == "0.0000907"

end # module test_stdlib_printf


using Test
using LinearAlgebra
include("../src/NumDiff.jl")
include("../src/Parsers.jl")

# Lorenz system
data = datagen(LorenzSystem())
v_ana = differentiate(data,LorenzSystem(),AnalyticalDeriv())
v_diff = differentiate(data,FiniteDiff())
v_vtd = differentiate(data,TotalVariationalDerivativative())
error_diff = v_ana[:,1:end-1] .- v_diff[:,:]
error_tvd = v_ana[:,:] .- munge(v_vtd)
p_diff = [norm(error_diff[:,i]) for i = 1:size(error_diff,2)]
p_tvd = [norm(error_tvd[:,i]) for i = 1:size(error_diff,2)]
using Plots
plot(p_diff,title="Lorenz - Error Finite Difference ",ylabel="Error")
savefig("./figures/error_finitediff.png")
plot(p_tvd,title="Lorenz - Error TVD",ylabel="Error")
savefig("./figures/error_tvd.png")

# Lotka VOlterra
data = datagen(LotkaVolterra())
v_ana = differentiate(data,LotkaVolterra(),AnalyticalDeriv())
v_diff = differentiate(data,FiniteDiff())
v_vtd = differentiate(data,TotalVariationalDerivativative())
error_diff = norm(v_ana[:,1:end-1] .- v_diff[:,:])
error_tvd = norm(v_ana[:,:] .- munge(v_vtd))

# Helicopter data
data = permutedims(datagen(HelicopterData("src/data/heli.csv")))
t = Vector(0.01:0.01:size(data,2)*0.01)
v_tvd = munge(differentiate(data,t,TotalVariationalDerivativative()))

v_diff = differentiate(data,t,FiniteDiff())


# TODO - overflow
# TODO - fast mult as http://maths-people.anu.edu.au/~brent/pd/rpb232.pdf

# a pragmatic library for doing modular arithmetic.

# the aim here is not to encapsulate a large amount of theory, or to
# describe the theoretical relationships between different structures,
# but to enable arithmetic on various types, motivated largely by the
# practical needs of crypto code.

# incomplete; pull requests welcome

# much thanks to Andreas Noack Jensen
# https://groups.google.com/d/msg/julia-users/Ui977brdzAU/u4rWiDeJv-MJ
# https://gist.github.com/andreasnoackjensen/9132511 (but since this
# changes things significanty, mistakes are mine...)

module IntModN

import Base: show, showcompact, zero, one, inv, real, abs, convert,
       promote_rule, length, getindex, setindex!, start, done, next,
       rand, rand!, print, map, leading_zeros, divrem, endof, bits,
       div, rem, mod, trailing_zeros, /, ==, <=, <, -, ^, +, *, &,
       |, $, %, <<, >>>, >>
import Base.Random.AbstractRNG

using AutoTypeParameters

export ZModN, ZField, ZRing, ZR, ZF, GF2, @zring, @zfield,
       ZPoly, ZP, X, P, order, modulus, factor, itype,
       GF2Poly, GF2P, GF2X,
       FModN, FRing, FR,
       extended_euclidean

# we generally support promotion into types here (from integers) and
# conversion back.  but that's all.  users can add other promotion /
# conversion if required.

# using Integer as the base type here as: (1) some numeric type seems
# to be necessary to access many generic numerical functions; (2) need
# Real to use comparisons; (3) have integer-like accuracy behaviour
# (no need to worry about rounding etc).

# but we need an additional type so that we can intercept things like
# automatic promotion to floats from integers.

abstract Residue <: Integer

/{I<:Integer,M<:Residue}(i::I, m::M) = convert(M, i) / m
/{I<:Integer,M<:Residue}(m::M, i::I) = m / convert(M, i)
inv{M<:Residue}(m::M) = error("no inverse defined")

# --- integers modulo n

# J is the type of N.  this may seem superfluous, but N itself is frozen, so its type is not always available (see AutoTypeParameters)

abstract ZModN{J<:Integer, N, I<:Integer} <: Residue

# we need two types here because prime moduli have a faster inverse
# (via euler's theorem) (note - we do NOT test for primality).

# you can construct these directly, but code assumes that the integer is
# already reduced mod N, that capacities are ok, etc.

immutable ZRing{J<:Integer, N, I<:Integer} <: ZModN{J,N,I}
    i::I
    ZRing(i) = (@assert isa(i, J); new(i))
end

# this assumes N is prime and uses a faster inverse
immutable ZField{J<:Integer, N, I<:Integer} <: ZModN{J,N,I}
    i::I
    ZField(i) = (@assert isa(i, J); new(i))
end

# but typically you would use one of these constructors (ZF or ZR)

function prepare_z{I<:Integer}(n::Int, i::I)
    n > 0 || error("modulus ($n) too small")
    n <= typemax(typeof(i)) || error("modulus ($n) too large for $(typeof(i))")
    mod(i, n)
end

for (f, Z) in ((:ZR, :ZRing), (:ZF, :ZField))
    @eval $f{J<:Integer, I<:Integer}(n, ::Type{J}, i, ::Type{I}) = $Z{J, freeze(convert(J, n)), I}(prepare_z(n, convert(I, i)))
    @eval $f{I<:Integer}(n::Int, i, ::Type{I}) = $Z{typeof(n), freeze(n), I}(prepare_z(n, convert(I, i)))
    @eval $f(n::Int, i::Integer) = $Z{typeof(n), freeze(n), typeof(i)}(prepare_z(n, i))
    @eval $f(n::Integer, i::Integer) = $Z{typeof(n), freeze(n), typeof(i)}(mod(i, n))
    @eval $f(n) = i::Integer -> $f(n, i)  # used to construct constructors
end
GF2 = ZF(2)

modulus{J<:Integer, N, I<:Integer}(::Type{ZModN{J, N, I}}) = convert(J, thaw(eval, N))
modulus{T<:ZModN}(::Type{T}) = modulus(super(T))  # jameson type chaina
modulus{T<:ZModN}(::T) = modulus(T)
order{T<:ZModN}(::Type{T}) = modulus(T)
order{T<:ZModN}(::T) = modulus(T)
itype{J, N, I<:Integer}(::Type{ZModN{J, N, I}}) = I
itype{T<:ZModN}(::Type{T}) = itype(super(T))  # jameson type chain
itype{T<:ZModN}(::T) = itype(T)
ntype{J, N, I<:Integer}(::Type{ZModN{J, N, I}}) = J
ntype{T<:ZModN}(::Type{T}) = ntype(super(T))  # jameson type chain
ntype{T<:ZModN}(::T) = ntype(T)

for (f, Z) in ((:ZR, :ZRing), (:ZF, ZField))
    # julia's comversion is pretty damn dumb at times
    @eval convert{J<:Integer,N,I<:Integer}(::Type{$Z{J,N,I}}, a::$Z{J,N,I}) = a
    # this promotion needed because linalg code has direct compariosn with 0
    @eval promote_rule{J<:Integer, N, I<:Integer}(::Type{$Z{J,N,I}}, ::Type{Int}) = $Z{J,N,I}
    @eval convert{J<:Integer,N,I<:Integer}(::Type{$Z{J,N,I}}, i::Integer) = $f(convert(J, N), convert(I, i))
end
# conversion to int, but no promotion
for T in (:Int, :UInt)
    @eval convert(::Type{$T}, z::ZModN) = convert($T, z.i)
end

# all the methods that make these numbers

for Z in (:ZRing, :ZField)
    @eval zero{J,N,I}(::Type{$Z{J,N,I}}) = $Z{J,N,I}(zero(I))
    @eval one{J,N,I}(::Type{$Z{J,N,I}}) = $Z{J,N,I}(one(I))
end
zero{Z<:ZModN}(z::Z) = zero(Z)
one{Z<:ZModN}(z::Z) = one(Z)

showcompact{J,N,I}(io::IO, z::ZModN{J,N,I}) = showcompact(io, z.i)
print{J,N,I}(io::IO, z::ZModN{J,N,I}) = print(io, "$(z.i) mod $N")
show{J,N,I<:Integer}(io::IO, z::ZField{J,N,I}) = print(io, "ZField{$J,$N,$I}($(z.i))")
show{J,N,I<:Integer}(io::IO, z::ZRing{J,N,I}) = print(io, "ZRing{$J,$N,$I}($(z.i))")

rand_{J,N,I<:Integer}(T::Type{ZRing{J,N,I}}) = ZR(N, J, rand(one(I):convert(I,modulus(T))), I)
rand_{J,N,I<:Integer}(T::Type{ZField{J,N,I}}) = ZF(N, J, rand(one(I):convert(I,modulus(T))), I)
function rand!{T<:ZModN}(::Type{T}, a::AbstractArray)
    for i in 1:length(a)
        a[i] = rand_(T)
    end
    a
end

rand(::Type{ZModN}, dims...) = error("use more specific type")

function rand{T<:ZModN}(::Type{T}, dims...)
    if length(dims) > 0
        rand!(T, Array(T, dims))
    else
        rand_(T)
    end
end

if VERSION >= v"0.4-"
    rand{J<:Integer,N,I<:Integer}(rng::AbstractRNG, T::Type{ZRing{J,N,I}}) = ZR(N, J, rand(rng, one(I):convert(I,N)), I)
    rand{J<:Integer,N,I<:Integer}(rng::AbstractRNG, T::Type{ZField{J,N,I}}) = ZF(N, J, rand(rng, one(I):convert(I,N)), I)
    function rand!{T<:ZModN}(rng::AbstractRNG, ::Type{T}, a::AbstractArray)
        for i in 1:length(a)
            a[i] = rand(rng, T)
        end
        a
    end
    rand(rng::AbstractRNG, ::Type{ZModN}, dims::Int...) =
    error("use more specific type")
    rand{T<:ZModN}(rng::AbstractRNG, ::Type{T}, dims::Int...) =
    rand!(rng, T, Array(T, dims))
end

# equal N from types, so we don't need to test explicitly
=={T,N,I}(a::ZModN{T,N,I}, b::ZModN{T,N,I}) = a.i == b.i
<={T,N,I}(a::ZModN{T,N,I}, b::ZModN{T,N,I}) = a.i <= b.i
<{T,N,I}(a::ZModN{T,N,I}, b::ZModN{T,N,I}) = a.i < b.i

real{T,N,I}(a::ZModN{T,N,I}) = a
abs{T,N,I}(a::ZModN{T,N,I}) = a

liftz(c, i, f, a) = c(convert(i, mod(f(a.i), modulus(a))))
liftz(c, i, f, a, b) = c(convert(i, mod(f(a.i, b.i), modulus(a))))
for Z in (ZRing, ZField)
    @eval -{J,N,I}(a::$Z{J,N,I}) = liftz($Z{J,N,I}, I, -, a)
    @eval ^{J,N,I}(a::$Z{J,N,I}, p::Int) = liftz($Z{J,N,I}, I, x -> powermod(x, p, modulus(a)), a)
    for op in (:+, :-, :*, :&, :|, :$)
        @eval $op{J,N,I}(a::$Z{J,N,I}, b::$Z{J,N,I}) = liftz($Z{J,N,I}, I, $op, a, b)
    end
end

# http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Residue_integers
function extended_euclidean{I<:Integer}(a::I, n::I)
    t::I, newt::I = zero(I), one(I)
    r::I, newr::I = n, a
    while newr != zero(I)
        q::I = div(r, newr)
        t, newt = newt, t - q * newt
        r, newr = newr, r - q * newr
    end
    r <= 1 || error("$a is not invertible mod $n")
    t < 0 ? t + n : t
end

inv{J,N,I}(a::ZRing{J,N,I}) = ZRing{J,N,I}(extended_euclidean(a.i, convert(I, modulus(a))))
inv{J,N,I}(a::ZField{J,N,I}) = a^(modulus(a)-2)

function /{T,N,I}(a::ZModN{T,N,I}, b::ZModN{T,N,I})
    if a == b
        one(a)
    elseif a == zero(a)
        a
    elseif b == zero(b)
        error("division by zero: $a/$b")
    else
        a * inv(b)
    end
end

# macros to rewrite literals mod n
# (these rewrite ALL ints, including args to literal ZF(...), etc)

macro zring(n::Int, code)
    :($(esc(rewrite_ints(n, x -> ZR(n, x), code))))
end

macro zfield(n::Int, code)
    :($(esc(rewrite_ints(n, x -> ZF(n, x), code))))
end

function rewrite_ints(n, c, val)
    if isa(val, Integer)
        :($(c(val)))
    else
        if isa(val, Expr) && val.head != :line
            val.args = [rewrite_ints(n, c, a) for a in val.args]
        end
        val
    end
end

# --- supertype for all integer polynomial representations

# I is the type that [] returns when indexing a coefficient
abstract Poly{I<:Integer} <: Residue

endof(a::Poly) = length(a)
start(::Poly) = 1
done(a::Poly, i) = i > length(a)
next(a::Poly, i) = (a[i], i+1)

length{P<:Poly}(a::P) = error("define a length() for type $P")
getindex{P<:Poly}(a::P, i) = error("define a getindex() for type $P")

# delegate modulus to the coefficient type

modulus{T}(::Poly{T}) = modulus(T)
modulus{T}(::Type{Poly{T}}) = modulus(T)   # is this useful?

# generic comparison

function cmp{P<:Poly}(a::P, b::P, eq)
    if length(a) < length(b)
        true
    elseif length(a) > length(b)
        false
    else
        for i in length(a):-1:1
            if a[i] < b[i]
                return true
            elseif a[i] > b[i]
                return false
            end
        end
        eq
    end
end
<={P<:Poly}(a::P, b::P) = cmp(a, b, true)
<{P<:Poly}(a::P, b::P) = cmp(a, b, false)

# exotic polynomials may require a suitable show_coeff and/or show_mod to
# display well

show_coeff(io::IO, i::Integer) = show(io, i)
show_coeff{T,N,I<:Integer}(io::IO, z::ZModN{T,N,I}) = show(io, convert(I, z))

show_mod{I<:Integer}(io::IO, p::Poly{I}) = nothing

function print_no_mod{I<:Integer}(io::IO, p::Poly{I})
    n = length(p)
    if n <= 0
        show_coeff(io, zero(I))
    else
        for j = n:-1:1
            pj = p[j]
            if pj != zero(I)
                if j == n
                    pj < 0 && print(io, "-")
                else
                    pj < 0 ? print(io," - ") : print(io," + ")
                end
                magpj = abs(pj)
                if j == 1 || magpj != one(I)
                    show_coeff(io, magpj)
                end
                if j > 1
                    print(io, :x)
                    if j > 2
                        print(io, '^', j-1)
                    end
                end
            end
        end
    end
end

function print{I<:Integer}(io::IO, p::Poly{I})
    print_no_mod(io, p)
    show_mod(io, p)
end

function show{I<:Integer}(io::IO, p::Poly{I})
    print(io, "ZP($I")
    for i in length(p):-1:1
        print(io, ",")
        show_coeff(io, p[i])
    end
    print(io, ")")
end

# --- arbitrary (but "integer") coeff polynomials

immutable ZPoly{I<:Integer} <: Poly{I}
    a::Vector{I}
    ZPoly(a) = length(a) == 0 || a[end] != zero(I) ? new(a) : error("zero leading coeff")
end

immutable FZP{I<:Integer}
    a::Tuple
end

# constructors

function prepare_p{N}(coeffs::Vector{N})
    start = endof(coeffs)
    while start > 0 && coeffs[start] == zero(N)
        start -= 1
    end
    start == endof(coeffs) ? coeffs : coeffs[1:start]
end

ZP() = error("provide at least one (zero?) coeff")
ZP(c::Function) = error("provide at least one (zero?) coeff")
# the vector constructors are in storage order
ZP(t::Type, coeffs::Vector) = ZP(t[map(c -> convert(t, c), coeffs)...])
ZP(c::Function, coeffs::Vector) = ZP(map(c, coeffs))
ZP{I<:Integer}(coeffs::Vector{I}) = ZPoly{I}(prepare_p(coeffs))
# the direct (tuple) constructors are in natural order
ZP(t::Type, coeffs...) = ZP(reverse(t[map(c -> convert(t, c), coeffs)...]))
ZP(c::Function, coeffs...) = ZP(reverse([map(c, coeffs)...]))
ZP{I<:Integer}(coeffs::I...) = ZP(reverse([coeffs...]))

# nice syntax for creating polynomials.
# use x=X(ZF(2)) or x=X(GF2) or x=X(ZField{2,Int}) and then x^2+3 etc
X(c::Function) = ZP([c(0), c(1)])
X{I<:Integer}(::Type{I}) = ZP([zero(I), one(I)])

# this promotion to write x^2+1 etc
convert{I<:Integer}(::Type{ZPoly{I}}, p::ZPoly{I}) = p
promote_rule{I<:Integer}(::Type{ZPoly{I}}, ::Type{Int}) = ZPoly{I}
convert{I<:Integer}(::Type{ZPoly{I}}, i::Integer) = ZP([convert(I, i)])

# can be accessed and modified (but don't do that in public) as arrays
getindex(a::ZPoly, i) = getindex(a.a, i)
setindex!{T,I<:Integer}(a::ZPoly{T}, v::I, i) = setindex!(a.a, convert(T, v), i)
length(a::ZPoly) = length(a.a)

show_mod{Z<:ZModN}(io::IO, p::ZPoly{Z}) = print(io, " mod $(modulus(Z))")
showcompact(io::IO, p::ZPoly) = showcompact(io, p.a)

# number methods

zero{T}(::Type{ZPoly{T}}) = ZPoly{T}(T[])
zero{Z<:ZPoly}(z::Z) = zero(Z)
one{T}(::Type{ZPoly{T}}) = ZPoly{T}([one(T)])
one{Z<:ZPoly}(z::Z) = one(Z)

=={T}(a::ZPoly{T}, b::ZPoly{T}) = a.a == b.a
-{T}(a::ZPoly{T}) = ZPoly{T}(-a.a)

function trailing_zeros{T}(p::ZPoly{T})
    n = 1
    while n < length(p) && p[n] == zero(T)
        n += 1
    end
    n - 1
end

# a and b same length; discard leading zeroes
function _truncate{T}(f, a::Vector{T}, b::Vector{T})
    ZERO, la = zero(T), length(a)
    copied = false
    for i in la:-1:1
        x::T = f(a[i], b[i])
        if x != ZERO && !copied
            result = Array(T, i)
            copied = true
        end
        if copied
            result[i] = x
        end
    end
    if !copied
        result = T[]
    end
    result
end

# big longer than small; copy extra values
function _skip{T}(f, big::Vector{T}, small::Vector{T})
    lb, ls = length(big), length(small)
    result = Array(T, lb)
    for i in lb:-1:(ls+1)
        result[i] = big[i]
    end
    for i in ls:-1:1
        result[i] = f(big[i], small[i])
    end
    result
end

function +{T}(a::ZPoly{T}, b::ZPoly{T})
    la, lb = length(a), length(b)
    if la == 0
        b
    elseif lb == 0
        a
    elseif la == lb
        ZPoly{T}(_truncate(+, a.a, b.a))
    elseif la > lb
        ZPoly{T}(_skip(+, a.a, b.a))
    else
        ZPoly{T}(_skip(+, b.a, a.a))
    end
end

function -{T}(a::ZPoly{T}, b::ZPoly{T})
    la, lb = length(a), length(b)
    if la == lb
        if a == b
            zero(ZPoly{T})
        else
            ZPoly{T}(_truncate(-, a.a, b.a))
        end
    elseif la > lb
        ZPoly{T}(_skip(-, a.a, b.a))
    else
        # in this case, b is strictly bigger than a, and leading
        # coeffs must be negated.  so there's no chance to truncate
        # and we cannot shift past the start.  so do it here.
        result = Array(T, lb)
        for i = lb:-1:(la+1)
            result[i] = -b.a[i]
        end
        for i = la:-1:1
            result[i] = a[i] - b[i]
        end
        ZPoly{T}(result)
    end
end

function *{T}(a::ZPoly{T}, b::ZPoly{T})
    big, small = length(a) > length(b) ? (a, b) : (b, a)
    if length(small) == 0  # no need to test big
        small
    elseif length(small) == 1 && small[1] == one(T)
        big
    else
        result = zeros(T, length(big) + length(small) - 1)
        for i in 1:length(small)
            z = small[i]  # enumerate here uses strange amount of memory
            if z != zero(T)
                for j in 1:length(big)
                    result[i+j-1] += big[j] * z
                end
            end
        end
        ZP(result)
    end
end

function _divrem{T}(a::Vector{T}, b::Vector{T})
    la, lb = length(a), length(b)
    lb > 0 || error("division by zero polynomial")
    if la == 0
        (a, a)
    elseif lb == 1 && b[1] == one(T)
        (a, T[])
    elseif lb > la
        (T[], a)
    elseif a == b
        ([one(T)], T[])
    else
        shift = la - lb  # non-negative
        rem, div = copy(a), zeros(T, shift + 1)
        for s in 0:shift
            factor = rem[end-s] / b[end]
            if factor != zero(T)
                div[end-s] = factor
                for i in 1:lb
                    rem[i+shift-s] -= factor * b[i]
                end
            end
        end
        div, rem
    end
end

function divrem{T}(a::ZPoly{T}, b::ZPoly{T})
    map(ZP, _divrem(a.a, b.a))  # ZP discards leading zeroes
end

div{T}(a::ZPoly{T}, b::ZPoly{T}) = ZP(_divrem(a.a, b.a)[1])
/{T}(a::ZPoly{T}, b::ZPoly{T}) = ZP(_divrem(a.a, b.a)[1])
rem{T}(a::ZPoly{T}, b::ZPoly{T}) = ZP(_divrem(a.a, b.a)[2])
mod{T}(a::ZPoly{T}, b::ZPoly{T}) = ZP(_divrem(a.a, b.a)[2])
%{T}(a::ZPoly{T}, b::ZPoly{T}) = ZP(_divrem(a.a, b.a)[2])
# ^ will come from power_by_squaring in stdlib
degree{T}(a::ZPoly{T}) = length(a) - 1

# defining bitwise ops seems odd, but makes sense for GF2, especially
# when using the compact binary repn below.  for N>2 we apply the
# operation to each coefficient pair in turn.

function same_length{T}(a::Vector{T}, b::Vector{T})
    big, small = length(a) > length(b) ? (a, b) : (b, a)
    pad = length(big) - length(small)
    small = vcat(small, zeros(T, pad))
    big, small
end

map{P<:ZPoly}(f::Function, a::P, b::P) = ZP(map(f, same_length(a.a, b.a)...))
for op in (:&, :|, :$)
    @eval $op{P<:ZPoly}(a::P, b::P) = map($op, a, b)
end
<<{T}(a::ZPoly{T}, n::Int) = ZPoly{T}(vcat(zeros(T, n), a.a))
>>>{T}(a::ZPoly{T}, n::Int) = ZPoly{T}(a.a[n+1:end])
>>{T}(a::ZPoly{T}, n::Int) = a >>> n

# --- GF(2) polynomials encoded as bits

# the type of Poly{} is the type returned by []
immutable GF2Poly{U<:Unsigned} <: Poly{ZField{Int,2,Int}}
    i::U
end

# constructors
GF2P{U<:Unsigned}(p::U) = GF2Poly{U}(p)
GF2P{S<:Signed}(p::S) = GF2P(unsigned(p))
GF2X{U<:Unsigned}(::Type{U}) = GF2Poly{U}(one(U) + one(U))
GF2X() = GF2X(UInt)

modulus(::Type{GF2Poly}) = 2
modulus(::GF2Poly) = 2

convert{I<:Integer, U<:Unsigned}(::Type{GF2Poly{U}}, i::I) = GF2P(convert(U, i % 2))
promote_rule{I<:Unsigned, U<:Unsigned}(::Type{GF2Poly{U}}, ::Type{I}) = GF2Poly{U}
promote_rule{U<:Unsigned}(::Type{GF2Poly{U}}, ::Type{Int}) = GF2Poly{U}

# promote to ZPoly mainly for equality tests (convert below)
promote_rule{U<:Unsigned,J<:Integer,I<:Integer}(::Type{GF2Poly{U}}, ::Type{ZPoly{ZField{J,2,I}}}) = ZPoly{ZField{J,2,I}}

# no setindex! as immutable
getindex{U<:Unsigned}(a::GF2Poly{U}, i) = a.i & (one(U) << (i-1)) == 0 ? GF2(0) : GF2(1)
length{U<:Unsigned}(a::GF2Poly{U}) = 8 * sizeof(U) - leading_zeros(a.i)

# for display we first convert to to ZPoly.  this is not efficient,
# but we probably need the interop anyway, and who cares if printing
# is efficient?

convert{J<:Integer, I<:Integer, U<:Unsigned}(::Type{ZPoly{ZField{J,2,I}}}, p::GF2Poly{U}) = _convert(ZPoly{ZField{J,2,I}}, p)
convert{J<:Integer, I<:Integer, U<:Unsigned}(::Type{GF2Poly{U}}, p::ZPoly{ZField{J,2,I}}) = _convert(GF2Poly{U}, p)
convert{U<:Unsigned}(::Type{GF2Poly{U}}, p::GF2Poly{U}) = p

function _convert{T, F}(::Type{T}, from::F)
    result, mask = zero(T), one(T)
    for _ in 1:length(from)
        if from & one(F) != zero(F)
            result += mask
        end
        from >>>= 1
        mask <<= 1
    end
    result
end

#print{U<:Unsigned}(io::IO, p::GF2Poly{U}) = print(io, convert(ZPoly{ZField{2,Int}}, p))
#print_no_mod{U<:Unsigned}(io::IO, p::GF2Poly{U}) = print_no_mod(io, convert(ZPoly{ZField{2,Int}}, p))
show_mod{U<:Unsigned}(io::IO, p::GF2Poly{U}) = print(io, " mod 2")
show{U<:Unsigned}(io::IO, p::GF2Poly{U}) = print(io, "GF2Poly{$U}($(p.i))")

bits(p::GF2Poly) = bits(p.i)

zero{U<:Unsigned}(::Type{GF2Poly{U}}) = GF2P(zero(U))
zero{U<:Unsigned}(::GF2Poly{U}) = GF2P(zero(U))
one{U<:Unsigned}(::Type{GF2Poly{U}}) = GF2P(one(U))
one{U<:Unsigned}(::GF2Poly{U}) = GF2P(one(U))

-{U<:Unsigned}(a::GF2Poly{U}) = a
for (name, op) in ((:+, :$), (:-, :$), (:|, :|), (:&, :&), (:$, :$))
    @eval $name{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = GF2Poly{U}($op(a.i, b.i))
end
for op in (:(==), :<=, :<)
    @eval $op{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = $op(a.i, b.i)
end
<<{U<:Unsigned}(a::GF2Poly{U}, n::Int) = GF2Poly(a.i << n)
>>>{U<:Unsigned}(a::GF2Poly{U}, n::Int) = GF2Poly(a.i >>> n)
>>{U<:Unsigned}(a::GF2Poly{U}, n::Int) = a >>> n
leading_zeros{U<:Unsigned}(a::GF2Poly{U}) = leading_zeros(a.i)

function *{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U})
    big, small = a > b ? (a, b) : (b, a)
    if small == zero(GF2Poly{U})  # no need to test big
        small
    elseif small == one(GF2Poly{U})
        big
    else
        result = zero(GF2Poly{U})
        while small != zero(GF2Poly{U}) && big != zero(GF2Poly{U})
            if small & one(GF2Poly{U}) == one(GF2Poly{U})
                result += big
            end
            small >>>= 1
            big <<= 1
        end
        result
    end
end

function divrem{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U})
    ONE, ZERO = one(GF2Poly{U}), zero(GF2Poly{U})
    b != ZERO || error("division by zero polynomial")
    if a == ZERO
        (a, a)
    elseif b == ONE
        (a, ZERO)
    elseif a == b
        (ONE, ZERO)
    else
        shift = leading_zeros(b) - leading_zeros(a)
        if shift < 0
            (ZERO, a)
        else
            rem::GF2Poly{U}, div::GF2Poly{U} = a, ZERO
            factor = ONE << shift
            b = b << shift  # no overflow (b *= factor)
            mask = ONE << (8 * sizeof(U) - leading_zeros(rem) - 1)  # msb(rem)
            for _ in shift:-1:0
                if rem & mask != ZERO
                    div += factor
                    rem -= b
                end
                factor >>>= 1
                b >>>= 1
                mask >>>= 1
            end
            div, rem
        end
    end
end

div{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = divrem(a, b)[1]
/{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = divrem(a, b)[1]
rem{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = divrem(a, b)[2]
mod{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = divrem(a, b)[2]
%{U<:Unsigned}(a::GF2Poly{U}, b::GF2Poly{U}) = divrem(a, b)[2]

degree{U<:Unsigned}(p::GF2Poly{U}) = 8*sizeof(U) - leading_zeros(p)

end

#ZField{ATP ZP(IntModN.ZField{2,Int64},1,0,0,0,1,1,0,1,1),
#       IntModN.ZPoly{IntModN.ZField{2,Int64}}}(x^6 + x^5 + x^3 + x^2 + x + 1 mod 2)
#ZField{ATP ZP(IntModN.ZField{2,Int64},1,0,0,0,1,1,0,1,1),
#       IntModN.ZPoly{IntModN.ZField{2,Int64}}}(x^6 + x^4 + x + 1 mod 2)