module Rainflow
import Base.show

export sort_peaks, find_boundary_vals, count_cycles, sum_cycles

""" This function sort out points where the slope is changing sign."""
function sort_peaks(signal::AbstractArray{Float64,1}, dt=[0.:length(signal)-1.])
    slope = diff(signal)
    # Determines if the point is local extremum
    is_extremum = [true, (slope[1:end-1].*slope[2:end]).<=0., true]
    return signal[is_extremum] , dt[is_extremum]
end

immutable Cycle  # This is the information stored for each cycle found
    count::Float64
    range::Float64
    mean::Float64  # value
    v_s::Float64   # value start
    t_s::Float64   # time start
    v_e::Float64   # value end
    t_e::Float64   # time end
end

show(io::IO,x::Cycle) = print(io, "Cycle: count=", x.count, ", range=",x.range, ", mean=",x.mean)

function cycle(count::Float64, v_s::Float64, t_s::Float64, v_e::Float64, t_e::Float64)
    Cycle(count, abs(v_s-v_e), (v_s+v_e)/2, v_s, t_s, v_e, t_e)
end

""" Count the cycles from the data. """
function count_cycles(ext_in::Array{Float64,1},t::Array{Float64,1})
    ext = copy(ext_in) # Makes a copy because there is going to be sorted in the vectors
    time = copy(t)
    i = 1
    j = 2
    cycles = Cycle[]
    sizehint(cycles, length(ext)) # This reduces the memory consumption a bit
    @inbounds begin
    while length(ext)>(i+1)
        Y = abs(ext[i+1]-ext[i])
        X = abs(ext[j+1]-ext[j])
        if X>=Y
            if i == 1 # This case counts a half and cycle deletes the poit that is counted
                #println("Half cycle $(ext[i]), $(ext[i+1])")
                push!(cycles,cycle(0.5 ,ext[i], time[i], ext[i+1],time[i+1]))
                shift!(ext) # Removes the first entrance in ext and time
                shift!(time)
            else # This case counts one cycle and deletes the poit that is counted
                #println("One cycle $(ext[i]), $(ext[i+1])")
                push!(cycles,cycle(1. ,ext[i], time[i], ext[i+1],time[i+1]))
                splice!(ext,i+1)  # Removes the i and i+1 entrance in ext and time
                splice!(ext,i)
                splice!(time,i+1)
                splice!(time,i)
            end
            i = 1
            j = 2
        else
            i += 1
            j += 1
        end
    end
    for i=1:length(ext)-1 # This counts the rest of the points that have not been counted as a half cycle
        #println("Half cycle $(ext[i]), $(ext[i+1])")
        push!(cycles,cycle(0.5 ,ext[i], time[i], ext[i+1],time[i+1]))
    end
    end
    return cycles
end

type Cycles_bounds #
    min_mean::Float64
    max_mean::Float64
    max_range::Float64
end

show(io::IO,x::Cycles_bounds) = print(io, "Cycles_bounds : min mean value=", x.min_mean, ", max mean value=", x.max_mean, ", max range=",x.max_range)

""" Find the minimum and maximum mean value and maximum range from a vector of cycles. """
function find_boundary_vals(cycles::Array{Cycle,1})
    bounds = Cycles_bounds(Inf, -Inf, -Inf)
    for cycle in cycles
        cycle.mean > bounds.max_mean && setfield!(bounds, :max_mean, cycle.mean)
        cycle.mean < bounds.min_mean && setfield!(bounds, :min_mean, cycle.mean)
        cycle.range > bounds.max_range && setfield!(bounds, :max_range, cycle.range)
    end
    return bounds
end

""" Returns the range index the value is belonging in """
function find_range{T<:Real}(interval::Array{T,1},value)
    issorted(interval) || error("The array needs to be sorted in raising order")
    for i=1:length(interval)-1
        if interval[i] <= value <= interval[i+1]
            return i
        end
    end
    error("The value where not in range")
end

if v"0.4.0-dev+4986" <  VERSION
    typealias Interval{T} Union{Array{T,1}, LinSpace{T}}
    """ Returns the range index the value is belonging in """
    function find_range{T<:Real}(interval::LinSpace{T}, value)
        issorted(interval) || error("The array needs to be sorted in raising order")
        start = interval.start
        stop = interval.stop
        (start < value < stop) || error("The value where not in range, see if the vectors in calc_sum(cycles::Array{Cycle,1}, range_intervals::Array{T,1}, mean_intervals::Array{T,1}) are continious increasing in value, or adjust the nr_digits parameter")
        inc = (interval.stop - start) / interval.divisor
        i = int(fld(value - start, inc) ) + 1
    end
else
    typealias Interval{T} Array{T,1}
end

""" Sums the cycle count given intervals of range_intervals and mean_intervals. The range_intervals and mean_intervals is given in fraction of range size"""
function sum_cycles{T<:Real}(cycles::Array{Cycle,1}, range_intervals::Interval{T}, mean_intervals::Interval{T})
    bounds = find_boundary_vals(cycles)
    bins = zeros(length(range_intervals)-1, length(mean_intervals)-1)
    range_intervals *= bounds.max_range/100
    #show(range_intervals)
    mean_intervals *= (bounds.max_mean-bounds.min_mean)/100
    mean_intervals += bounds.min_mean
    nr_digits = 14  # The rounding is performed due to numerical noise in the floats when comparing
    if v"0.4.0-dev+4986" >  VERSION || isa(mean_intervals,LinSpace)
        mean_intervals = round(mean_intervals, nr_digits)
    elseif v"0.4.0-dev+4986" >  VERSION || isa(range_intervals,LinSpace)
        range_intervals = round(range_intervals, nr_digits)
    end
    #show(mean_intervals)
    for cycle in cycles
        i = find_range(range_intervals,round(cycle.range, nr_digits))
        j = find_range(mean_intervals,round(cycle.mean, nr_digits))
        bins[i,j] += cycle.count
    end
    return bins
end

function sum_cycles(cycles::Array{Cycle,1}, nr_ranges::Int=10, nr_means::Int=1)
    range_intervals = linspace(0,100,nr_ranges+1)
    mean_intervals = linspace(0,100,nr_means+1)
    sum_cycles(cycles, range_intervals, mean_intervals)
end

try
    include("plot.jl")
catch
    println("""For added plotting features do: Pkg.add("PyPlot")""")
end

end # module


################################################################################
## Linear Regression
##   y ~ N(b0 + b1 * x, s2)
##   b0, b1 ~ N(0, 1000)
##   s2 ~ invgamma(0.001, 0.001)
################################################################################

data = Dict{Symbol,Any}(
    :x => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    :y => [1, 3, 3, 3, 5, 5, 7, 7, 7, 9],
)
data[:xmat] = [ones(10) data[:x]]
model = Model(
    y = Stochastic(1, (μ, s2) -> MultivariateNormal(μ, s2), false),
    μ = Logical(1, (β, xmat) -> xmat * β, false),
    β = Stochastic(1, () -> Normal(0, sqrt(1000)), true),
    s2 = Stochastic(() -> InverseGamma(0.001, 0.001), true),
)

inits = [
    Dict{Symbol,Any}(:y => data[:y], :β => rand(Normal(0, 1), 2), :s2 => rand(Gamma(1, 1))) for i = 1:2
]

@testset "Slice" begin
    Random.seed!(123)
    samplers = [Slice([:β, :s2], 1.0)]
    setsamplers!(model, samplers)

    sim = mcmc(
        model,
        data,
        inits,
        100000,
        burnin = 50000,
        thin = 10,
        chains = 2,
        verbose=false
    )

    r_m = summarystats(sim).value[2:3, 1, 1]
    r_sd = summarystats(sim).value[2:3, 2, 1]
    @test r_m[1]-r_sd[1] <= 0.60 <= r_m[1]+r_sd[1]
    @test r_m[2]-r_sd[2] <= 0.80 <= r_m[2]+r_sd[2]
end

@testset "RWM" begin
    Random.seed!(123)
    samplers = [RWM([:β, :s2], 1.0)]
    setsamplers!(model, samplers)

    sim = mcmc(
        model,
        data,
        inits,
        100000,
        burnin = 50000,
        thin = 10,
        chains = 2,
        verbose=false
    )

    r_m = summarystats(sim).value[2:3, 1, 1]
    r_sd = summarystats(sim).value[2:3, 2, 1]
    @test r_m[1]-r_sd[1] <= 0.60 <= r_m[1]+r_sd[1]
    @test r_m[2]-r_sd[2] <= 0.80 <= r_m[2]+r_sd[2]
end

@testset "NUTS_Classic" begin
    Random.seed!(123)
    samplers = [NUTS([:β, :s2], variant=:classic)]
    setsamplers!(model, samplers)

    sim = mcmc(
        model,
        data,
        inits,
        100000,
        burnin = 50000,
        thin = 10,
        chains = 2,
        verbose=false
    )

    r_m = summarystats(sim).value[2:3, 1, 1]
    r_sd = summarystats(sim).value[2:3, 2, 1]
    @test r_m[1]-r_sd[1] <= 0.60 <= r_m[1]+r_sd[1]
    @test r_m[2]-r_sd[2] <= 0.80 <= r_m[2]+r_sd[2]
end

@testset "NUTS_Riemann" begin
    Random.seed!(123)
    samplers = [NUTS([:β, :s2], variant=:riemann)]
    setsamplers!(model, samplers)

    sim = mcmc(
        model,
        data,
        inits,
        100000,
        burnin = 50000,
        thin = 10,
        chains = 2,
        verbose=false
    )

    r_m = summarystats(sim).value[2:3, 1, 1]
    r_sd = summarystats(sim).value[2:3, 2, 1]
    @test r_m[1]-r_sd[1] <= 0.60 <= r_m[1]+r_sd[1]
    @test r_m[2]-r_sd[2] <= 0.80 <= r_m[2]+r_sd[2]
end

@testset "HMC" begin
    Random.seed!(123)
    samplers = [HMC([:β, :s2], 0.007, 8)]
    setsamplers!(model, samplers)

    sim = mcmc(
        model,
        data,
        inits,
        100000,
        burnin = 50000,
        thin = 10,
        chains = 2,
        verbose=false
    )

    r_m = summarystats(sim).value[2:3, 1, 1]
    r_sd = summarystats(sim).value[2:3, 2, 1]
    @test r_m[1]-r_sd[1] <= 0.60 <= r_m[1]+r_sd[1]
    @test r_m[2]-r_sd[2] <= 0.80 <= r_m[2]+r_sd[2]
end


##
using QuasiMonteCarlo: LowDiscrepancySample, sample
using Plots

lb = [0.0, -0.5]  # Lower bounds
ub = [4.0, 3.5]   # Upper bounds
n = 100           # Number of samples

halton = LowDiscrepancySample([3, 5])  # Halton with prime number sequence
xtrain = sample(n, lb, ub, halton)

scatter(xtrain[1,:], xtrain[2,:])
##

# f(x) = cos.(x[1,:]).*sin.(x[2,:])
# ytrain = f(xtrain)

# #scatter3d(xtrain[1,:], xtrain[2,:], ytrain, marker_z=ytrain)
# #savefig("test.png")


using MathGL.Capi
mgl = MathGL.Capi

gr = mgl.create_graph(800, 500)
dat = mgl.create_data()
mgl.data_link(dat, 0.8*sin(linspace(-4pi, 4pi, 200)), 200, 1, 1)
mgl.label(gr, 'x', "x", 0, "")
mgl.label(gr, 'y', "y", 0, "")
mgl.box(gr)
mgl.axis(gr, "xyz", "", "")
mgl.axis_grid(gr, "xyz", "H|", "")
#=mgl.set_origin(gr, 0., 0., 0.)=#
mgl.plot(gr, dat, "", "")
mgl.write_frame(gr, "../graphs/sin_C.png", "")


module mmLE1NAFEMSsstress
using FinEtools
using FinEtoolsDeforLinear
using Test
import LinearAlgebra: norm, cholesky, cross
function test()
    E = 210e3*phun("MEGA*PA");# 210e3 MPa
    nu = 0.3;
    p = 10*phun("MEGA*PA");# 10 MPA Outward pressure on the outside ellipse
    sigma_yD = 92.7*phun("MEGA*PA");# tensile stress at [2.0, 0.0] meters
    Radius = 1.0*phun("m")
    Thickness = 0.1*phun("m")
    n = 2; # number of elements per side
    tolerance = 1.0/n/1000.; # Geometrical tolerance

    fens,fes = Q4block(1.0, pi/2, n, n*2)
    fens,fes  = H8extrudeQ4(fens, fes,
      1, (xyz, layer)->[xyz[1], xyz[2], (layer)*Thickness]);

    bdryfes = meshboundary(fes);
    icl = selectelem(fens, bdryfes, box=[1.0, 1.0, 0.0, pi/2, 0.0, Thickness], inflate=tolerance);
    for i=1:count(fens)
        t=fens.xyz[i,1]; a=fens.xyz[i,2]; z=fens.xyz[i,3]
        fens.xyz[i,:]=[(t*3.25+(1-t)*2)*cos(a), (t*2.75+(1-t)*1)*sin(a), z];
    end

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),3)) # displacement field

    l1 =selectnode(fens; box=[0.0, Inf, 0.0, 0.0, 0.0, Thickness], inflate = tolerance)
    setebc!(u,l1,true, 2, 0.0)
    l1 =selectnode(fens; box=[0.0, 0.0, 0.0, Inf, 0.0, Thickness], inflate = tolerance)
    setebc!(u,l1,true, 1, 0.0)
    l1 =selectnode(fens; box=[0.0, Inf, 0.0, Inf, 0.0, 0.0], inflate = tolerance)
    setebc!(u,l1,true, 3, 0.0)

    applyebc!(u)
    numberdofs!(u)

    el1femm =  FEMMBase(IntegDomain(subset(bdryfes,icl), GaussRule(2, 2)))
    function pfun(forceout::FVec{T}, XYZ::FFltMat, tangents::FFltMat, fe_label::FInt) where {T}
        pt= [2.75/3.25*XYZ[1], 3.25/2.75*XYZ[2], 0.0]
        forceout .=    vec(p*pt/norm(pt));
        return forceout
    end
    fi = ForceIntensity(FFlt, 3, pfun);
    F2 = distribloads(el1femm, geom, u, fi, 2);

    # Note that the material object needs to be created with the proper
    # model-dimension reduction in mind.  In this case that is the fully three-dimensional solid.
    MR = DeforModelRed3D

    material = MatDeforElastIso(MR, E, nu)

    femm = FEMMDeforLinearMSH8(MR, IntegDomain(fes, GaussRule(3, 2)), material)

    # The geometry field now needs to be associated with the FEMM
    femm = associategeometry!(femm, geom)

    K = stiffness(femm, geom, u)
    K = cholesky(K)
    U = K\(F2)
    scattersysvec!(u, U[:])

    nl = selectnode(fens, box=[2.0, 2.0, 0.0, 0.0, 0.0, 0.0],inflate=tolerance);
    thecorneru = zeros(FFlt,1,3)
    gathervalues_asmat!(u, thecorneru, nl);
    thecorneru = thecorneru/phun("mm")
    # println("displacement =$(thecorneru) [MM] as compared to reference [-0.10215,0] [MM]")
    @test norm(thecorneru - [-0.107276 0.0 0.0]) < 1.0e-4

    fld= fieldfromintegpoints(femm, geom, u, :Cauchy, 2;
        nodevalmethod = :invdistance, reportat = :meanonly)
    # println("Sigma_y =$(fld.values[nl,1][1]/phun("MPa")) as compared to reference sigma_yD = $(sigma_yD/phun("MPa")) [MPa]")
    @test abs(fld.values[nl,1][1]/phun("MPa") - 42.54884174624546) < 1.0e-3
    fld= fieldfromintegpoints(femm, geom, u, :Cauchy, 2;
        nodevalmethod = :averaging, reportat = :extrapmean)
    # println("Sigma_y =$(fld.values[nl,1][1]/phun("MPa")) as compared to reference sigma_yD = $(sigma_yD/phun("MPa")) [MPa]")
    @test abs(fld.values[nl,1][1]/phun("MPa") - 42.54884174624546) < 1.0e-3
    fld= fieldfromintegpoints(femm, geom, u, :Cauchy, 2;
        nodevalmethod = :averaging, reportat = :extraptrend)
    # println("Sigma_y =$(fld.values[nl,1][1]/phun("MPa")) as compared to reference sigma_yD = $(sigma_yD/phun("MPa")) [MPa]")
    @test abs(fld.values[nl,1][1]/phun("MPa") - 45.44562958746983) < 1.0e-3

    fld= fieldfromintegpoints(femm, geom, u, :Cauchy, 2;
        nodevalmethod = :invdistance, reportat = :meanonly)
    # println("Sigma_y =$(fld.values[nl,1][1]/phun("MPa")) as compared to reference sigma_yD = $(sigma_yD/phun("MPa")) [MPa]")
    @test abs(fld.values[nl,1][1]/phun("MPa") - 42.54884174624546) < 1.0e-3

end
end
using .mmLE1NAFEMSsstress
mmLE1NAFEMSsstress.test()

module mocylpullFun
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsDeforLinear.AlgoDeforLinearModule
using Test
function test()

    # Cylinder  pulled by enforced displacement, axially symmetric model

    # Parameters:
    E1=1.0;
    E2=1.0;
    E3=3.0;
    nu12=0.29;
    nu13=0.29;
    nu23=0.19;
    G12=0.3;
    G13=0.3;
    G23=0.3;
    p= 0.15;
    rin=1.;
    rex =1.2;
    Length = 1*rex
    ua = -0.05*Length
    tolerance=rin/1000.

    ##
    # Note that the FinEtools objects needs to be created with the proper
    # model-dimension reduction at hand.  In this case that is the axial symmetry
    # assumption.
    MR = DeforModelRed2DAxisymm

    # Create the mesh and initialize the geometry.  First we are going
    # to construct the block of elements with the first coordinate
    # corresponding to the thickness in the radial direction, and the second
    # coordinate is the thickness in the axial direction.

    fens,fes = Q4block(rex-rin,Length,5,20);
    fens.xyz[:, 1] = fens.xyz[:, 1] .+ rin
    bdryfes = meshboundary(fes);

    # the symmetry plane
    la1 =selectnode(fens; box=[0 rex 0 0], inflate = tolerance)
    # The other end
    la2 =selectnode(fens; box=[0 rex Length Length], inflate = tolerance)

    e1 = FDataDict("node_list"=>la1, "component"=>2, "displacement"=>x -> 0.0)
    e2 = FDataDict("node_list"=>la2, "component"=>2, "displacement"=>x -> ua)

    # Property and material
    material=MatDeforElastOrtho(MR, E1,E2,E3,nu12,nu13,nu23,G12,G13,G23)

    femm = FEMMDeforLinear(MR, IntegDomain(fes, GaussRule(2, 2), true), material)

    # Make region
    region = FDataDict("femm"=>femm)

    # Make model data
    modeldata =  FDataDict(
    "fens"=> fens, "regions"=>  [region],
    "essential_bcs"=>[e1, e2])

    # Call the solver
    modeldata = AlgoDeforLinearModule.linearstatics(modeldata)
    geom = modeldata["geom"]
    u = modeldata["u"]

    # Produce a plot of the radial stress component in the cylindrical
    # coordinate system. Note that this is the usual representation of
    # stress using nodal stress field.

    fld= fieldfromintegpoints(femm, geom, u, :Cauchy, 2)
    # println("Minimum/maximum = $(minimum(fld.values))/$(maximum(fld.values))")
    @test abs(minimum(fld.values) - -0.050318853446676574) < 1.0e-4
    @test abs(maximum(fld.values) - -0.04973951673608955) < 1.0e-4
    # File =  "orthoballoon_sigmaz.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("sigmaz", fld.values)],
    #               vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)
end
end
using .mocylpullFun
mocylpullFun.test()

module mmLE11malgo
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsDeforLinear.AlgoDeforLinearModule
using Test
import LinearAlgebra: norm, cholesky, cross
function test()

    # NAFEMS LE11 benchmark with Q8 elements.
    # # This is a test recommended by the National Agency for Finite Element
    # # Methods and Standards (U.K.): Test LE11 from NAFEMS Publication TNSB,
    # # Rev. 3, “The Standard NAFEMS Benchmarks,” October 1990.
    # #
    # # Target solution: Direct stress,   =  –105 MPa at point A.
    #function  LE11NAFEMS()
    # Parameters:
    Ea =  210000*phun("MEGA*Pa")
    nua =  0.3;
    alphaa = 2.3e-4;              # thermal expansion coefficient
    sigmaA = -105*phun("MEGA*Pa")
    nref =  1;                        # how many times should we refine the mesh?
    X = [1.     0.;#A
    1.4    0.;#B
    0.995184726672197   0.098017140329561;
    1.393258617341076 0.137223996461385;
    0.980785  0.195090;#
    1.37309939 0.27312645;
    0.956940335732209   0.290284677254462
    1.339716470025092 0.406398548156247
    0.9238795  0.38268;#C
    1.2124  0.7;#D
    0.7071  0.7071;#E
    1.1062  1.045;#F
    0.7071  (0.7071+1.79)/2;#(E+H)/2
    1.      1.39;#G
    0.7071  1.79;#H
    1.      1.79;#I
    ]*phun("M")
    tolerance  = 1.e-6*phun("M")
    ##
    # Note that the material object needs to be created with the proper
    # model-dimension reduction in mind.  In this case that is the axial symmetry
    # assumption.
    MR  =  DeforModelRed2DAxisymm

    fens = FENodeSet(X);
    fes = FESetQ4([1 2 4 3; 3 4 6 5; 5 6 8 7; 7 8 10 9; 9 10 12 11; 11 12 14 13; 13 14 16 15]);
    for ref = 1:nref
      fens,fes = Q4refine(fens,fes);
      list = selectnode(fens,distance = 1.0+0.1/2^nref, from = [0. 0.], inflate = tolerance);
      fens.xyz[list,:] =  FinEtools.MeshUtilModule.ontosphere(fens.xyz[list,:],1.0);
    end
    fens,fes = Q4toQ8(fens,fes)
    list = selectnode(fens,distance = 1.0+0.1/2^nref, from = [0. 0.], inflate = tolerance);
    fens.xyz[list,:] =  FinEtools.MeshUtilModule.ontosphere(fens.xyz[list,:],1.0);

    # EBC's
    l1 = selectnode(fens,box = [-Inf Inf 0 0],inflate = tolerance)
    e1 = FDataDict("node_list"=>l1, "component"=>2, "displacement"=>x -> 0.0)
    l1 = selectnode(fens,box = [-Inf Inf 1.79  1.79],inflate = tolerance)
    e2 = FDataDict("node_list"=>l1, "component"=>2, "displacement"=>x -> 0.0)

    # Temperature field
    dtemp = FDataDict("temperature"=>x -> x[1] + x[2])

    # Property and material
    material = MatDeforElastIso(MR, 0.0, Ea, nua, alphaa)

    femm  =  FEMMDeforLinear(MR, IntegDomain(fes, GaussRule(2, 3), true), material)

    # Make region 1
    region = FDataDict("femm"=>femm)
    # Make model data
    modeldata =  FDataDict(
    "fens"=> fens, "regions"=>  [region],
    "essential_bcs"=>[e1, e2], "temperature_change"=>dtemp)

    # Call the solver
    modeldata = AlgoDeforLinearModule.linearstatics(modeldata)
    geom = modeldata["geom"]
    u = modeldata["u"]
    dT = modeldata["temp"]

    nA  = selectnode(fens,box = FFlt[1.0  1.0 0.0 0.0], inflate = tolerance);

    fld =  fieldfromintegpoints(femm, geom, u, dT, :Cauchy, 2)

    File  =   "LE11NAFEMS_Q8_sigmay.vtk"
    vtkexportmesh(File, fens, fes; scalars = [("sigmay", fld.values)],
        vectors = [("u", u.values)])
    # println("range of  sigmay = $((minimum(fld.values), maximum(fld.values)))")
    @test norm([minimum(fld.values), maximum(fld.values)] - [-1.443052182185007e8, -1.4106181545272522e7]) < 1.0e-1
        # @async run(`"paraview.exe" $File`)
    try rm(File)   catch end

    sA  =  fld.values[nA]/phun("MEGA*Pa")
    sAn  =  fld.values[nA]/sigmaA
    # println("Stress at point A: $(sA) i. e.  $( sAn*100  )% of reference value")
    @test norm(sA .- -93.8569) < 1.0e-4

    fen2fe  = FENodeToFEMap(connasarray(fes), nnodes(geom))
    function inspector(idat, elnum, conn, xe,  out,  xq)
    #   println("loc = $(  xq  ) : $(  transpose(out)/phun("MEGA*Pa")  )")
      return idat
    end

    inspectintegpoints(femm, geom, u, dT,  fen2fe.map[nA[1]],
      inspector, []; quantity = :Cauchy)

end
end
using .mmLE11malgo
mmLE11malgo.test()

module mmtwistedmsh8ort
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsDeforLinear.AlgoDeforLinearModule
using Test
import LinearAlgebra: norm, cholesky, cross
import Statistics: mean
function test()

  # println("""
  # The initially twisted cantilever beam is one of the standard test
  # problems for verifying the finite-element accuracy [1]. The beam is
  #   clamped at one end and loaded either with unit in-plane or
  #   unit out-of-plane force at the other. The centroidal axis of the beam is
  #   straight at the undeformed  configuration, while its cross-sections are
  #   twisted about the centroidal axis from 0 at the clamped end to pi/2 at
  #   the free end.
  #
  #   Reference:
  #   Zupan D, Saje M (2004) On "A proposed standard set of problems to test
  #   finite element accuracy": the twisted beam. Finite Elements in Analysis
  #   and Design 40: 1445-1451.
  #   """)
  E1s =   E2s =  E3s = 0.29e8
  nu12s = nu13s = nu23s = 0.22
  G12s = G13s = G23s = E1s/2/(1+nu12s)
  # E = 0.29e8;
  # nu = 0.22;
  W = 1.1;
  L = 12.;
  t =  0.32;
  nl = 2; nt = 1; nw = 1; ref = 7;
  p =   1/W/t;
  #  Loading in the Z direction
  loadv = [0;0;p]; dir = 3; uex = 0.005424534868469; # Harder: 5.424e-3;
  #   Loading in the Y direction
  #loadv = [0;p;0]; dir = 2; uex = 0.001753248285256; # Harder: 1.754e-3;
  tolerance  = t/1000;

  fens,fes  = H8block(L,W,t, nl*ref,nw*ref,nt*ref)

  # Reshape into a twisted beam shape
  for i = 1:count(fens)
    a = fens.xyz[i,1]/L*(pi/2); y = fens.xyz[i,2]-(W/2); z = fens.xyz[i,3]-(t/2);
    fens.xyz[i,:] = [fens.xyz[i,1],y*cos(a)-z*sin(a),y*sin(a)+z*cos(a)];
  end

  # Clamped end of the beam
  l1  = selectnode(fens; box = [0 0 -100*W 100*W -100*W 100*W], inflate  =  tolerance)
  e1 = FDataDict("node_list"=>l1, "component"=>1, "displacement"=>0.0)
  e2 = FDataDict("node_list"=>l1, "component"=>2, "displacement"=>0.0)
  e3 = FDataDict("node_list"=>l1, "component"=>3, "displacement"=>0.0)

  # Traction on the opposite edge
  boundaryfes  =   meshboundary(fes);
  Toplist   = selectelem(fens,boundaryfes, box =  [L L -100*W 100*W -100*W 100*W], inflate =   tolerance);
  el1femm  = FEMMBase(IntegDomain(subset(boundaryfes,Toplist), GaussRule(2, 2)))
  flux1 = FDataDict("femm"=>el1femm, "traction_vector"=>loadv)

  # Make the region
  MR = DeforModelRed3D
  material = MatDeforElastOrtho(MR,
    0.0, E1s, E2s, E3s,
    nu12s, nu13s, nu23s,
    G12s, G13s, G23s,
    0.0, 0.0, 0.0)
  region1 = FDataDict("femm"=>FEMMDeforLinearMSH8(MR, IntegDomain(fes, GaussRule(3,2)),
  material))

  # Make model data
  modeldata =  FDataDict(
  "fens"=> fens, "regions"=>  [region1],
  "essential_bcs"=>[e1, e2, e3], "traction_bcs"=>  [flux1])

  # Call the solver
  modeldata = AlgoDeforLinearModule.linearstatics(modeldata)
  geom = modeldata["geom"]
  u = modeldata["u"]

  # Extract the solution
  nl = selectnode(fens, box = [L L -100*W 100*W -100*W 100*W],inflate = tolerance);
  theutip = mean(u.values[nl,:], dims = 1)
  # println("displacement  = $(theutip[dir]) as compared to converged $uex")
  @test abs(theutip[dir]-uex)/uex < 0.0012

  # Write out mesh with displacements
  modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8")
  modeldata = AlgoDeforLinearModule.exportdeformation(modeldata)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  # Write out mesh with stresses
  modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8",
  "quantity"=> :Cauchy, "component"=> :xy)
  modeldata = AlgoDeforLinearModule.exportstress(modeldata)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  # Write out mesh with stresses
  modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8",
  "quantity"=> :Cauchy, "component"=> :xz)
  modeldata = AlgoDeforLinearModule.exportstress(modeldata)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  # Write out mesh with von Mises stresses
  modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8",
  "quantity"=> :vm)
  modeldata = AlgoDeforLinearModule.exportstress(modeldata)
  vm  = modeldata["postprocessing"]["exported"][1]["field"]
  # println("extremes of vm, nodal: $([minimum(vm.values),   maximum(vm.values)])")
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end
  @test norm([minimum(vm.values),   maximum(vm.values)]-[4.78774, 522.126]) < 0.01

  # Write out mesh with von Mises stresses, elementwise
  modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8-ew",
  "quantity"=> :vm)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  vm  = modeldata["postprocessing"]["exported"][1]["field"]
  # println("extremes of vm, elemental: $([minimum(vm.values),   maximum(vm.values)])")
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end
  @test norm([minimum(vm.values),   maximum(vm.values)]-[1.85882, 522.126]) < 0.01

  # Write out mesh with von Mises stresses, elementwise
  modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8-ew",
  "quantity"=> :Cauchy, "component"=> :xz)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

end
end
using .mmtwistedmsh8ort
mmtwistedmsh8ort.test()

module mmtwistedmsh9ort
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsDeforLinear.AlgoDeforLinearModule
using Test
import LinearAlgebra: norm, cholesky, cross
import Statistics: mean
function test()

    # println("""
    # The initially twisted cantilever beam is one of the standard test
    # problems for verifying the finite-element accuracy [1]. The beam is
    #   clamped at one end and loaded either with unit in-plane or
    #   unit out-of-plane force at the other. The centroidal axis of the beam is
    #   straight at the undeformed  configuration, while its cross-sections are
    #   twisted about the centroidal axis from 0 at the clamped end to pi/2 at
    #   the free end.
    #
    #   Reference:
    #   Zupan D, Saje M (2004) On "A proposed standard set of problems to test
    #   finite element accuracy": the twisted beam. Finite Elements in Analysis
    #   and Design 40: 1445-1451.
    #   """)
    E1s =   E2s =  E3s = 0.29e8
    nu12s = nu13s = nu23s = 0.22
    G12s = G13s = G23s = E1s/2/(1+nu12s)
    # E = 0.29e8;
    # nu = 0.22;
    W = 1.1;
    L = 12.;
    t =  0.32;
    nl = 2; nt = 1; nw = 1; ref = 7;
    p =   1/W/t;
    #  Loading in the Z direction
    loadv = [0;0;p]; dir = 3; uex = 0.005424534868469; # Harder: 5.424e-3;
    #   Loading in the Y direction
    #loadv = [0;p;0]; dir = 2; uex = 0.001753248285256; # Harder: 1.754e-3;
    tolerance  = t/1000;

    fens,fes  = H8block(L,W,t, nl*ref,nw*ref,nt*ref)

    # Reshape into a twisted beam shape
    for i = 1:count(fens)
        a = fens.xyz[i,1]/L*(pi/2); y = fens.xyz[i,2]-(W/2); z = fens.xyz[i,3]-(t/2);
        fens.xyz[i,:] = [fens.xyz[i,1],y*cos(a)-z*sin(a),y*sin(a)+z*cos(a)];
    end

    # Clamped end of the beam
    l1  = selectnode(fens; box = [0 0 -100*W 100*W -100*W 100*W], inflate  =  tolerance)
    e1 = FDataDict("node_list"=>l1, "component"=>1, "displacement"=>0.0)
    e2 = FDataDict("node_list"=>l1, "component"=>2, "displacement"=>0.0)
    e3 = FDataDict("node_list"=>l1, "component"=>3, "displacement"=>0.0)

    # Traction on the opposite edge
    boundaryfes  =   meshboundary(fes);
    Toplist   = selectelem(fens,boundaryfes, box =  [L L -100*W 100*W -100*W 100*W], inflate =   tolerance);
    el1femm  = FEMMBase(IntegDomain(subset(boundaryfes,Toplist), GaussRule(2, 2)))
    flux1 = FDataDict("femm"=>el1femm, "traction_vector"=>loadv)

    # Make the region
    MR = DeforModelRed3D
    material = MatDeforElastOrtho(MR,
    0.0, E1s, E2s, E3s,
    nu12s, nu13s, nu23s,
    G12s, G13s, G23s,
    0.0, 0.0, 0.0)
    region1 = FDataDict("femm"=>FEMMDeforLinearMSH8(MR, IntegDomain(fes, GaussRule(3,2)),
    material))

    # Make model data
    modeldata =  FDataDict( "fens"=> fens, "regions"=>  [region1],
    "essential_bcs"=>[e1, e2, e3], "traction_bcs"=>  [flux1])

    # Call the solver
    modeldata = AlgoDeforLinearModule.linearstatics(modeldata)
    geom = modeldata["geom"]
    u = modeldata["u"]

    # Extract the solution
    nl = selectnode(fens, box = [L L -100*W 100*W -100*W 100*W],inflate = tolerance);
    theutip = mean(u.values[nl,:], dims = 1)
    # println("displacement  = $(theutip[dir]) as compared to converged $uex")
    @test abs(theutip[dir]-uex)/uex < 0.0012

    # Write out mesh with displacements
    modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8")
    modeldata = AlgoDeforLinearModule.exportdeformation(modeldata)
    try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

    # Write out mesh with stresses
    modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8",
    "quantity"=> :Cauchy, "component"=> :xy, "outputcsys"=>CSys(3))
    modeldata = AlgoDeforLinearModule.exportstress(modeldata)
    try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

    # Write out mesh with stresses
    modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8",
    "quantity"=> :Cauchy, "component"=> :xz, "outputcsys"=>CSys(3))
    modeldata = AlgoDeforLinearModule.exportstress(modeldata)
    try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

    # Write out mesh with von Mises stresses
    modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8",
    "quantity"=> :vm, "outputcsys"=>CSys(3))
    modeldata = AlgoDeforLinearModule.exportstress(modeldata)
    vm  = modeldata["postprocessing"]["exported"][1]["field"]
    # println("extremes of vm, nodal: $([minimum(vm.values),   maximum(vm.values)])")
    try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end
    @test norm([minimum(vm.values),   maximum(vm.values)]-[4.78774, 522.126]) < 0.01

    # Write out mesh with von Mises stresses, elementwise
    modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8-ew",
    "quantity"=> :vm, "outputcsys"=>CSys(3))
    modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
    vm  = modeldata["postprocessing"]["exported"][1]["field"]
    # println("extremes of vm, elemental: $([minimum(vm.values),   maximum(vm.values)])")
    try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end
    @test norm([minimum(vm.values),   maximum(vm.values)]-[1.85882, 522.126]) < 0.01

    # Write out mesh with von Mises stresses, elementwise
    modeldata["postprocessing"] = FDataDict("file"=>"twisted_beam_msh8-ew",
    "quantity"=> :Cauchy, "component"=> :xz, "outputcsys"=>CSys(3))
    modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
    try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

end
end
using .mmtwistedmsh9ort
mmtwistedmsh9ort.test()

module mxRMSerror3a1
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsDeforLinear.AlgoDeforLinearModule
using FinEtools.MeshUtilModule
using FinEtools.AlgoBaseModule
using Test
function test()

    elementtag = "MSH8"
    # println("""
    # Pagano_3layer_cylindrical_bending: $(elementtag)
    # """)

    # This example provides three-dimensional finite element model for  the
    # transverse shear stress calculations. The problem consists of a one-, two- or
    # three-layer plate subjected to a sinusoidal distributed load, as
    # described by Pagano (1969). The resulting transverse shear and axial
    # stresses through the thickness of the plate are compared to two existing
    # analytical solutions by Pagano (1969). The first solution is derived from
    # classical laminated plate theory (CPT), while the second is an exact
    # solution from linear elasticity theory.

    filebase = "Pagano_3layer_cylindrical_bending_$(elementtag)_convergence"

    modeldatasequence = FDataDict[]
    for Refinement = [1, 2, 4]

        # Orthotropic material for the 3 layers
        E1 = 25e6*phun("PSI"); E2 = 1e6*phun("PSI"); E3 = E2;
        G12 = 0.5e6*phun("PSI");  G13 = G12; G23 = 0.2e6*phun("PSI")
        nu12 =  0.25; nu13 =  0.25; nu23 =  0.25;
        Span_to_thickness = 4.0;
        T = 2.5*phun("in"); # total thickness of the plate
        L = Span_to_thickness*T;
        h = 1*phun("in");  # depth of the plate
        q0 = 1*phun("PSI")
        CTE1 =  CTE2 =  CTE3 = 0.0

        # Here we define the layout and the thicknesses of the layers.
        angles = vec([0.0 90.0 0.0]);
        nLayers = length(angles)
        ts = T/nLayers * ones(nLayers); # layer thicknesses

        tolerance = 0.0001*T

        # Select how find the mesh should be
        nL, nh = Refinement * 2 * 4, Refinement*1;
        nts= Refinement * 2 * ones(Int, nLayers);# number of elements per layer

        xs = collect(linearspace(0.0, L, nL+1))
        ys = collect(linearspace(0.0, h, nh+1))

        fens,fes = H8layeredplatex(xs, ys, ts, nts)
        # println("count(fens) = $(count(fens))")

        # This is the material  model
        MR = DeforModelRed3D
        skinmaterial = MatDeforElastOrtho(MR,
        0.0, E1, E2, E3,
        nu12, nu13, nu23,
        G12, G13, G23,
        CTE1, CTE2, CTE3)

        # The material coordinate system function is defined as:
        function updatecs!(csmatout::FFltMat, XYZ::FFltMat, tangents::FFltMat, fe_label::FInt)
            rotmat3!(csmatout, angles[fe_label]/180.0*pi* [0.0; 0.0; 1.0]);
        end

        # The vvolume integrals are evaluated using this rule
        gr = GaussRule(3, 2)

        # We will create 3 regions, one for each of the layers
        regions = FDataDict[]
        for layer = 1:nLayers
            rls = selectelem(fens, fes, label =  layer)
            push!(regions, FDataDict("femm"=>FEMMDeforLinearMSH8(MR,
            IntegDomain(subset(fes, rls), gr), CSys(3, 3, updatecs!), skinmaterial)))
        end

        # File =  "Meyer_Piening_sandwich-r1.vtk"
        # vtkexportmesh(File, skinregion["femm"].integdomain.fes.conn, fens.xyz, FinEtools.MeshExportModule.VTK.VTK.H8)
        # # @async run(`"paraview.exe" $File`)

        # The essential boundary conditions are applied to enforce the plane strain constraint.
        ly0 = selectnode(fens, box=[-Inf Inf 0.0 0.0 -Inf Inf], inflate=tolerance)
        lyh = selectnode(fens, box=[-Inf Inf h h -Inf Inf], inflate=tolerance)
        ey = FDataDict("displacement"=>  0.0, "component"=> 2, "node_list"=>vcat(ly0, lyh))
        # The transverse displacement is fixed at the two ends.
        lz0 = selectnode(fens, box=[0.0 0.0 -Inf Inf -Inf Inf], inflate=tolerance)
        lzL = selectnode(fens, box=[L L -Inf Inf -Inf Inf], inflate=tolerance)
        ez = FDataDict("displacement"=>  0.0, "component"=> 3, "node_list"=>vcat(lz0, lzL))
        ex = FDataDict("displacement"=>  0.0, "component"=> 1, "node_list"=>[1])

        # The traction boundary condition is applied at the top of the plate.
        bfes = meshboundary(fes)
        function pfun(forceout::FVec{T}, XYZ::FFltMat, tangents::FFltMat, fe_label::FInt) where {T}
            forceout[1] = 0.0
            forceout[2] = 0.0
            forceout[3] = -q0*sin(pi*XYZ[1]/L)
            return forceout
        end
        # From  the entire boundary we select those quadrilaterals that lie on the plane
        # Z = thickness
        tl = selectelem(fens, bfes, box = [-Inf Inf -Inf Inf T T], inflate=tolerance)
        Trac = FDataDict("traction_vector"=>pfun,
        "femm"=>FEMMBase(IntegDomain(subset(bfes, tl), GaussRule(2, 2))))

        modeldata = FDataDict("fens"=>fens,
        "regions"=>regions,
        "essential_bcs"=>[ex, ey, ez],
        "traction_bcs"=> [Trac]
        )
        modeldata = AlgoDeforLinearModule.linearstatics(modeldata)

        modeldata["postprocessing"] = FDataDict("file"=>filebase * "-u")
        modeldata = AlgoDeforLinearModule.exportdeformation(modeldata)
        for e in modeldata["postprocessing"]["exported"]
            try rm(e["file"]) catch end
        end

        u = modeldata["u"]
        geom = modeldata["geom"]

        # The results of the displacement and stresses will be reported at
        # nodes located at the appropriate points.
        ntopcenter = selectnode(fens, box=[L/2 L/2 0.0 h T T], inflate=tolerance)
        ncenterline = selectnode(fens, box=[L/2 L/2 0.0 0.0 0.0 T], inflate=tolerance)
        nx0line = selectnode(fens, box=[0.0 0.0 0.0 0.0 0.0 T], inflate=tolerance)

        zclo = sortperm(vec(geom.values[ncenterline, 3]))
        ncenterline = ncenterline[zclo]
        centerz = geom.values[ncenterline, 3]

        # println("Top Center deflection: $(mean(u.values[ntopcenter, 3], 1)/phun("in")) [in]")

        # # extrap = :extrapmean
        extrap = :extraptrend
        nodevalmeth = :averaging
        # extrap = :default
        # nodevalmeth = :invdistance

        # Compute  all stresses
        modeldata["postprocessing"] = FDataDict("file"=>filebase * "-s",
        "quantity"=>:Cauchy, "component"=>collect(1:6), "outputcsys"=>CSys(3),
        "nodevalmethod"=>nodevalmeth, "reportat"=>extrap)
        modeldata = AlgoDeforLinearModule.exportstress(modeldata)
        for e in modeldata["postprocessing"]["exported"]
            try rm(e["file"]) catch end
        end

        modeldata["elementsize"] = 1.0/Refinement
        modeldata["geometricaltolerance"] = tolerance
        modeldata["targetfields"] = [e["field"] for e in modeldata["postprocessing"]["exported"]]
        push!(modeldatasequence, modeldata)
    end # for refinement

    elementsizes, errornorms, p = AlgoBaseModule.evalconvergencestudy(modeldatasequence)

    # println("")
    # println("Normalized Approximate Error = $(errornorms)")
    @test abs(p[1] - 1.3347513854727369)/1.3347513854727369 < 1.0e-3
    # csvFile = filebase * "_errors" * ".CSV"
    # savecsv(csvFile,
    # elementsizes=vec(elementsizes[1:end-1]),
    # elementsizes2=vec(elementsizes[1:end-1].^2),
    # elementsizes3=vec(elementsizes[1:end-1].^3),
    # errornorms=vec(errornorms)
    # )

    # @async run(`"paraview.exe" $csvFile`)

    # println("Done")

end
end
using .mxRMSerror3a1
mxRMSerror3a1.test()

module munit_cube_modes_nice_t4
using FinEtools
using FinEtoolsDeforLinear
using Test
using Arpack
using LinearAlgebra
function test()
    # println("""
    # Vibration modes of unit cube  of almost incompressible material.
    # %
    # Reference: Puso MA, Solberg J (2006) A stabilized nodally integrated
    # tetrahedral. International Journal for Numerical Methods in
    # Engineering 67: 841-867.
    # """)
    t0 = time()

    E = 1*phun("PA");
    nu = 0.499;
    rho = 1*phun("KG/M^3");
    a = 1*phun("M"); b = a; h =  a;
    n1 = 10;# How many element edges per side?
    na =  n1; nb =  n1; nh  = n1;
    neigvs = 20                   # how many eigenvalues
    OmegaShift = (0.01*2*pi)^2;
    stabfact = 0.015
    Eigenvalues = [0.0, 5.93656e-8, 7.54751e-8, 9.80131e-8, 1.14899e-7, 1.27725e-7, 0.264544, 0.266128, 0.350568, 0.352546, 0.355279, 0.357389, 0.357701, 0.359704, 0.402389, 0.402968, 0.404977, 0.45061, 0.450974, 0.452039]

    MR = DeforModelRed3D
    fens,fes  = T4block(a,b,h, na,nb,nh)

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),3)) # displacement field

    numberdofs!(u)

    material = MatDeforElastIso(MR, rho, E, nu, 0.0)

    femm = FEMMDeforLinearNICET4(MR, IntegDomain(fes, NodalSimplexRule(3)), material, stabfact)
    associategeometry!(femm,  geom)
    K  = stiffness(femm, geom, u)
    M = mass(femm, geom, u)
    d,v,nev,nconv = eigs(K+OmegaShift*M, M; nev=neigvs, which=:SM, explicittransform=:none)
    d = d .- OmegaShift;
    fs = real(sqrt.(complex(d)))/(2*pi)
    # # println("Eigenvalues: $fs [Hz]")
    @test norm(vec(fs) .- vec(Eigenvalues)) < 1.0e-4*maximum(vec(Eigenvalues))

    # mode = 17
    # scattersysvec!(u, v[:,mode])
    # File =  "unit_cube_modes.vtk"
    # vtkexportmesh(File, fens, fes; vectors=[("mode$mode", u.values)])

    true

end
end
using .munit_cube_modes_nice_t4
munit_cube_modes_nice_t4.test()

module malum_cyl_mode_nice_t4
using FinEtools
using FinEtoolsDeforLinear
using Test
using Arpack
using LinearAlgebra
function test()
    # Aluminum cylinder free vibration, mesh imported from Abaqus
    # Mesh converted from quadratic tetrahedra to linear tetrahedra
    # NICE tetrahedral elements used
    E = 70000*phun("MPa");
    nu = 0.33;
    rho = 2700*phun("KG/M^3");
    radius = 0.5*phun("ft");
    neigvs = 20                   # how many eigenvalues
    OmegaShift = (10.0*2*pi)^2;
    stabfact = 0.005
    Eigenvalues = [4.54746e-5, 6.82231e-5, 8.7071e-5, 9.99708e-5, 0.000112778, 0.000116397, 2533.6, 2535.12, 2574.64, 4086.61, 4652.66, 4654.16, 5122.94, 6755.62, 6756.45, 6872.26, 6875.3, 6883.49, 6888.53, 6983.99]

    MR = DeforModelRed3D
    output = import_ABAQUS("alum_cyl.inp")
    fens, fes = output["fens"], output["fesets"][1]
    fens.xyz .*= phun("mm") # The input is provided in SI(mm) units
    fens, fes = T10toT4(fens, fes)

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),3)) # displacement field

    numberdofs!(u)

    material = MatDeforElastIso(MR, rho, E, nu, 0.0)

    femm = FEMMDeforLinearNICET4(MR, IntegDomain(fes, NodalSimplexRule(3)), material, stabfact)
    associategeometry!(femm,  geom)
    K  = stiffness(femm, geom, u)
    M = mass(femm, geom, u)
    d,v,nev,nconv = eigs(K+OmegaShift*M, M; nev=neigvs, which=:SM, explicittransform=:none)
    d = d .- OmegaShift;
    fs = real(sqrt.(complex(d)))/(2*pi)
    # # println("Eigenvalues: $fs [Hz]")
    @test norm(vec(fs) .- vec(Eigenvalues)) < 1.0e-3*maximum(vec(Eigenvalues))

    true
end
end
using .malum_cyl_mode_nice_t4
malum_cyl_mode_nice_t4.test()

module malum_cyl_mode_esnice_t4
using FinEtools
using FinEtoolsDeforLinear
using Test
using Arpack
using LinearAlgebra
function test()
    # Aluminum cylinder free vibration, mesh imported from Abaqus
    # Mesh converted from quadratic tetrahedra to linear tetrahedra
    # NICE tetrahedral elements used
    E = 70000*phun("MPa");
    nu = 0.33;
    rho = 2700*phun("KG/M^3");
    radius = 0.5*phun("ft");
    neigvs = 20                   # how many eigenvalues
    OmegaShift = (10.0*2*pi)^2;
    stabfact = 0.005
    Eigenvalues =   [0.0, 0.0, 0.0, 1.8846e-5, 7.35917e-5, 0.000119445, 2498.15, 2498.88, 2513.31, 4082.65, 4585.99, 4586.42, 4987.01, 6648.02, 6648.48, 6679.04, 6682.16, 6777.89, 6780.59, 6799.36]
    MR = DeforModelRed3D
    output = import_ABAQUS("alum_cyl.inp")
    fens, fes = output["fens"], output["fesets"][1]
    fens.xyz .*= phun("mm") # The input is provided in SI(mm) units
    fens, fes = T10toT4(fens, fes)

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),3)) # displacement field

    numberdofs!(u)

    material = MatDeforElastIso(MR, rho, E, nu, 0.0)

    femm = FEMMDeforLinearESNICET4(MR, IntegDomain(fes, NodalSimplexRule(3)), material)
    associategeometry!(femm,  geom)
    K  = stiffness(femm, geom, u)
    M = mass(femm, geom, u)
    d,v,nev,nconv = eigs(K+OmegaShift*M, M; nev=neigvs, which=:SM, explicittransform=:none)
    d = d .- OmegaShift;
    fs = real(sqrt.(complex(d)))/(2*pi)
    # println("Eigenvalues: $fs [Hz]")
    @test norm(vec(fs) .- vec(Eigenvalues)) < 1.0e-3*maximum(vec(Eigenvalues))

    true
end
end
using .malum_cyl_mode_esnice_t4
malum_cyl_mode_esnice_t4.test()

module mocylpull14 # From linear deformation
using FinEtools
using FinEtoolsDeforLinear
using Test
function test()
    # Cylinder  compressed by enforced displacement, axially symmetric model

    # Parameters:
    E1=1.0;
    E2=1.0;
    E3=3.0;
    nu12=0.29;
    nu13=0.29;
    nu23=0.19;
    G12=0.3;
    G13=0.3;
    G23=0.3;
    p= 0.15;
    rin=1.;
    rex =1.2;
    Length = 1*rex
    ua = -0.05*Length
    tolerance=rin/1000.

    ##
    # Note that the FinEtools objects needs to be created with the proper
    # model-dimension reduction at hand.  In this case that is the axial symmetry
    # assumption.
    MR = DeforModelRed2DAxisymm

    fens,fes = Q4block(rex-rin,Length,5,20);
    fens.xyz[:, 1] = fens.xyz[:, 1] .+ rin
    bdryfes = meshboundary(fes);

    # now we create the geometry and displacement fields
    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),2)) # displacement field

    # the symmetry plane
    l1 =selectnode(fens; box=[0 rex 0 0], inflate = tolerance)
    setebc!(u,l1,true, 2, 0.0)
    # The other end
    l1 =selectnode(fens; box=[0 rex Length Length], inflate = tolerance)
    setebc!(u,l1,true, 2, ua)

    applyebc!(u)
    numberdofs!(u)
    # println("Number of degrees of freedom = $(u.nfreedofs)")
    @test u.nfreedofs == 240

    # Property and material
    material=MatDeforElastIso(MR, 00.0, E1, nu23, 0.0)
    # display(material)
    # println("$(material.D)")

    femm = FEMMDeforLinear(MR, IntegDomain(fes, GaussRule(2, 2), true), material)

    K =stiffness(femm, geom, u)
    F = nzebcloadsstiffness(femm, geom, u)
    U=  K\(F)
    scattersysvec!(u,U[:])

    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 1)
    # println("Minimum/maximum = $(minimum(fld.values))/$(maximum(fld.values))")
    @test abs(minimum(fld.values) - 0.0) < 1.0e-5
    @test abs(maximum(fld.values) - 0.0) < 1.0e-5
    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 2)
    # println("Minimum/maximum = $(minimum(fld.values))/$(maximum(fld.values))")
    @test abs(minimum(fld.values) - 0.0) < 1.0e-5
    @test abs(maximum(fld.values) - 0.0) < 1.0e-5
    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 3)
    # println("Minimum/maximum = $(minimum(fld.values))/$(maximum(fld.values))")
    @test abs(minimum(fld.values) - -0.050) < 1.0e-5
    @test abs(maximum(fld.values) - -0.04999999999999919) < 1.0e-5

    # File =  "mocylpull14.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("sigmaz", fld.values)],
    #               vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)
end
end
using .mocylpull14
mocylpull14.test()

module mocylpull1 # From deformation
using FinEtools
using FinEtoolsDeforLinear
using Test
function test()
    # Cylinder  pulled by enforced displacement, axially symmetric model

    # Parameters:
    E1=1.0;
    E2=1.0;
    E3=3.0;
    nu12=0.29;
    nu13=0.29;
    nu23=0.19;
    G12=0.3;
    G13=0.3;
    G23=0.3;
    p= 0.15;
    rin=1.;
    rex =1.2;
    Length = 1*rex
    ua = -0.05*Length
    tolerance=rin/1000.

    ##
    # Note that the FinEtools objects needs to be created with the proper
    # model-dimension reduction at hand.  In this case that is the axial symmetry
    # assumption.
    MR = DeforModelRed2DAxisymm

    # Create the mesh and initialize the geometry.  First we are going
    # to construct the block of elements with the first coordinate
    # corresponding to the thickness in the radial direction, and the second
    # coordinate is the thickness in the axial direction.

    fens,fes = Q4block(rex-rin,Length,5,20);
    fens.xyz[:, 1] = fens.xyz[:, 1] .+ rin
    bdryfes = meshboundary(fes);

    # now we create the geometry and displacement fields
    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),2)) # displacement field

    # the symmetry plane
    l1 =selectnode(fens; box=[0 rex 0 0], inflate = tolerance)
    setebc!(u,l1,true, 2, 0.0)
    # The other end
    l1 =selectnode(fens; box=[0 rex Length Length], inflate = tolerance)
    setebc!(u,l1,true, 2, ua)

    applyebc!(u)
    numberdofs!(u)
    # println("Number of degrees of freedom = $(u.nfreedofs)")
    @test u.nfreedofs == 240

    # Property and material
    material=MatDeforElastOrtho(MR, E1,E2,E3,nu12,nu13,nu23,G12,G13,G23)

    femm = FEMMDeforLinear(MR, IntegDomain(fes, GaussRule(2, 2), true), material)

    K =stiffness(femm, geom, u)
    F = nzebcloadsstiffness(femm, geom, u)
    U=  K\(F)
    scattersysvec!(u,U[:])

    # Produce a plot of the radial stress component in the cylindrical
    # coordinate system. Note that this is the usual representation of
    # stress using nodal stress field.

    fld= fieldfromintegpoints(femm, geom, u, :Cauchy, 2)
    # println("Minimum/maximum = $(minimum(fld.values))/$(maximum(fld.values))")
    @test abs(minimum(fld.values) - -0.050318853446676436) < 1.0e-5
    @test abs(maximum(fld.values) - -0.0497395167360893) < 1.0e-5
    # File =  "orthoballoon_sigmaz.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("sigmaz", fld.values)],
    #               vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)
end
end
using .mocylpull1
mocylpull1.test()

module mmLE1NAFEMSsstressx1
using FinEtools
using FinEtoolsDeforLinear
using Test
import LinearAlgebra: norm, cholesky, cross
function test()
    E = 210e3*phun("MEGA*PA");# 210e3 MPa
    nu = 0.3;
    p = 10*phun("MEGA*PA");# 10 MPA Outward pressure on the outside ellipse
    sigma_yD = 92.7*phun("MEGA*PA");# tensile stress at [2.0, 0.0] meters
    Radius = 1.0*phun("m")
    Thickness = 0.1*phun("m")
    n = 20; # number of elements per side
    tolerance = 1.0/n/1000.; # Geometrical tolerance

    fens,fes = Q4block(1.0, pi/2, n, n*2)
    fens,fes  = H8extrudeQ4(fens, fes,
      1, (xyz, layer)->[xyz[1], xyz[2], (layer)*Thickness]);

    bdryfes = meshboundary(fes);
    icl = selectelem(fens, bdryfes, box=[1.0, 1.0, 0.0, pi/2, 0.0, Thickness], inflate=tolerance);
    for i=1:count(fens)
        t=fens.xyz[i,1]; a=fens.xyz[i,2]; z=fens.xyz[i,3]
        fens.xyz[i,:]=[(t*3.25+(1-t)*2)*cos(a), (t*2.75+(1-t)*1)*sin(a), z];
    end

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz,1),3)) # displacement field

    l1 =selectnode(fens; box=[0.0, Inf, 0.0, 0.0, 0.0, Thickness], inflate = tolerance)
    setebc!(u,l1,true, 2, 0.0)
    l1 =selectnode(fens; box=[0.0, 0.0, 0.0, Inf, 0.0, Thickness], inflate = tolerance)
    setebc!(u,l1,true, 1, 0.0)
    l1 =selectnode(fens; box=[0.0, Inf, 0.0, Inf, 0.0, 0.0], inflate = tolerance)
    setebc!(u,l1,true, 3, 0.0)

    applyebc!(u)
    numberdofs!(u)

    el1femm =  FEMMBase(IntegDomain(subset(bdryfes,icl), GaussRule(2, 2)))
    function pfun(forceout::FVec{T}, XYZ::FFltMat, tangents::FFltMat, fe_label::FInt) where {T}
        pt= [2.75/3.25*XYZ[1], 3.25/2.75*XYZ[2], 0.0]
        forceout .=    vec(p*pt/norm(pt));
        return forceout
    end
    fi = ForceIntensity(FFlt, 3, pfun);
    F2 = distribloads(el1femm, geom, u, fi, 2);

    # Note that the material object needs to be created with the proper
    # model-dimension reduction in mind.  In this case that is the fully three-dimensional solid.
    MR = DeforModelRed3D

    material = MatDeforElastIso(MR, E, nu)

    femm = FEMMDeforLinearMSH8(MR, IntegDomain(fes, GaussRule(3, 2)), material)

    # The geometry field now needs to be associated with the FEMM
    femm = associategeometry!(femm, geom)

    K = stiffness(femm, geom, u)
    K = cholesky(K)
    U = K\(F2)
    scattersysvec!(u, U[:])

    nl = selectnode(fens, box=[2.0, 2.0, 0.0, 0.0, 0.0, 0.0],inflate=tolerance);
    thecorneru = zeros(FFlt,1,3)
    gathervalues_asmat!(u, thecorneru, nl);
    thecorneru = thecorneru/phun("mm")
    # println("displacement =$(thecorneru) [MM] as compared to reference [-0.10215,0] [MM]")
    @test norm(thecorneru - [-0.10082864119023721 0.0 0.0]) < 1.0e-4

    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 1)
    @test norm(maximum(fld.values) - 8.190847372888073e7)/8.190847372888073e7 <= 1.0e-4
    # File =  "mmLE1NAFEMSsstressx1-s1.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("sigma_1", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)
    fld= fieldfromintegpoints(femm, geom, u, :maxshear, 1)
    @test norm(maximum(fld.values) - 8.19559427861603e7)/8.190847372888073e7 <= 1.0e-4
    # File =  "mmLE1NAFEMSsstressx1-maxshear.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("maxshear", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)

end
end
using .mmLE1NAFEMSsstressx1
mmLE1NAFEMSsstressx1.test()

module mholestr1
using FinEtools
using FinEtoolsDeforLinear
using Test
import LinearAlgebra: norm, cholesky, cross
function test()
    E = 100.0;
    nu = 1.0/3;
    cte = 0.15
    xradius = 1.0
    yradius = 1.0
    L = 3.0
    H = 3.0
    # nL = 50
    # nH = 50
    # nW = 70
    nL = 100
    nH = 100
    nW = 120
    tolerance = min(xradius, yradius, L, H)/min(nL, nH, nW)/1000.;#Geometrical tolerance

    fens, fes = Q4elliphole(xradius, yradius, L, H,    nL, nH, nW)

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz, 1), 2)) # displacement field

    l1 = selectnode(fens; box=[0, 0, -Inf,  Inf],  inflate = tolerance)
    setebc!(u, l1, 1, 0.0)
    l2 = selectnode(fens; box=[-Inf,  Inf, 0, 0],  inflate = tolerance)
    setebc!(u, l2, 2, 0.0)
    applyebc!(u)
    numberdofs!(u)

    boundaryfes =  meshboundary(fes);
    lce = selectelem(fens, boundaryfes, facing=true, direction = x -> -x,  inflate=  tolerance);
    lc = connectednodes(subset(boundaryfes, lce))

    le = selectelem(fens, boundaryfes,  box= [-Inf,  Inf, H, H],  inflate=  tolerance);
    el1femm =  FEMMBase(IntegDomain(subset(boundaryfes, le),  GaussRule(1, 2)))
    fi = ForceIntensity([0.0, 1.0]);
    Fm = distribloads(el1femm,  geom,  u,  fi,  2);
    le = selectelem(fens, boundaryfes,  box= [L, L, -Inf,  Inf],  inflate=  tolerance);
    el2femm =  FEMMBase(IntegDomain(subset(boundaryfes, le),  GaussRule(1, 2)))
    fi = ForceIntensity([1.0, 0.0]);
    Fm += distribloads(el2femm,  geom,  u,  fi,  2);

    MR = DeforModelRed2DStress
    material = MatDeforElastIso(MR,  0.0, E, nu, cte)

    femm = FEMMDeforLinear(MR, IntegDomain(fes,  GaussRule(2, 2)),  material)

    K = stiffness(femm,  geom,  u)
    U=  K\(Fm)
    scattersysvec!(u, U[:])

    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 1)
    # @test norm(maximum(fld.values) - 8.190847372888073e7)/8.190847372888073e7 <= 1.0e-4
    # File =  "mholestr1-s1.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("sigma_1", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)
    fld= fieldfromintegpoints(femm, geom, u, :maxshear, 1)
    @test norm(maximum(fld.values) - 2.210557410276065)/2.210557410276065 <= 1.0e-4
    # File =  "mholestr1-maxshear.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("maxshear", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)

end
end
using .mholestr1
mholestr1.test()

module mholestr2
using FinEtools
using FinEtoolsDeforLinear
using Test
import LinearAlgebra: norm, cholesky, cross
function test()
    E = 100.0;
    nu = 1.0/3;
    cte = 0.15
    xradius = 1.0
    yradius = 1.0
    L = 3.0
    H = 3.0
    # nL = 50
    # nH = 50
    # nW = 70
    nL = 100
    nH = 100
    nW = 120
    tolerance = min(xradius, yradius, L, H)/min(nL, nH, nW)/1000.;#Geometrical tolerance

    fens, fes = Q4elliphole(xradius, yradius, L, H,    nL, nH, nW)

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz, 1), 2)) # displacement field

    l1 = selectnode(fens; box=[0, 0, -Inf,  Inf],  inflate = tolerance)
    setebc!(u, l1, 1, 0.0)
    l2 = selectnode(fens; box=[-Inf,  Inf, 0, 0],  inflate = tolerance)
    setebc!(u, l2, 2, 0.0)
    applyebc!(u)
    numberdofs!(u)

    boundaryfes =  meshboundary(fes);
    lce = selectelem(fens, boundaryfes, facing=true, direction = x -> -x,  inflate=  tolerance);
    lc = connectednodes(subset(boundaryfes, lce))

    le = selectelem(fens, boundaryfes,  box= [-Inf,  Inf, H, H],  inflate=  tolerance);
    el1femm =  FEMMBase(IntegDomain(subset(boundaryfes, le),  GaussRule(1, 2)))
    fi = ForceIntensity([0.0, 1.0]);
    Fm = distribloads(el1femm,  geom,  u,  fi,  2);
    le = selectelem(fens, boundaryfes,  box= [L, L, -Inf,  Inf],  inflate=  tolerance);
    el2femm =  FEMMBase(IntegDomain(subset(boundaryfes, le),  GaussRule(1, 2)))
    fi = ForceIntensity([1.0, 0.0]);
    Fm += distribloads(el2femm,  geom,  u,  fi,  2);

    MR = DeforModelRed2DStrain
    material = MatDeforElastIso(MR,  0.0, E, nu, cte)

    femm = FEMMDeforLinear(MR, IntegDomain(fes,  GaussRule(2, 2)),  material)

    K = stiffness(femm,  geom,  u)
    U=  K\(Fm)
    scattersysvec!(u, U[:])

    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 1)
    # @test norm(maximum(fld.values) - 8.190847372888073e7)/8.190847372888073e7 <= 1.0e-4
    # File =  "mholestr2-s1.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("sigma_1", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)
    fld= fieldfromintegpoints(femm, geom, u, :maxshear, 1)
    @test norm(maximum(fld.values) - 2.2102738887214257)/2.2102738887214257 <= 1.0e-4
    # File =  "mholestr2-maxshear.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("maxshear", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)

end
end
using .mholestr2
mholestr2.test()

module mholestr3
using FinEtools
using FinEtoolsDeforLinear
using Test
import LinearAlgebra: norm, cholesky, cross
function test()
    E = 100.0;
    nu = 1.0/3;
    cte = 0.15
    xradius = 1.0
    yradius = 1.0
    L = 3.0
    H = 3.0
    # nL = 50
    # nH = 50
    # nW = 70
    nL = 100
    nH = 100
    nW = 120
    tolerance = min(xradius, yradius, L, H)/min(nL, nH, nW)/1000.;#Geometrical tolerance

    fens, fes = Q4elliphole(xradius, yradius, L, H,    nL, nH, nW)

    geom = NodalField(fens.xyz)
    u = NodalField(zeros(size(fens.xyz, 1), 2)) # displacement field

    l1 = selectnode(fens; box=[0, 0, -Inf,  Inf],  inflate = tolerance)
    setebc!(u, l1, 1, 0.0)
    l2 = selectnode(fens; box=[-Inf,  Inf, 0, 0],  inflate = tolerance)
    setebc!(u, l2, 2, 0.0)
    applyebc!(u)
    numberdofs!(u)

    boundaryfes =  meshboundary(fes);
    lce = selectelem(fens, boundaryfes, facing=true, direction = x -> -x,  inflate=  tolerance);
    lc = connectednodes(subset(boundaryfes, lce))

    le = selectelem(fens, boundaryfes,  box= [-Inf,  Inf, H, H],  inflate=  tolerance);
    el1femm =  FEMMBase(IntegDomain(subset(boundaryfes, le),  GaussRule(1, 2)))
    fi = ForceIntensity([0.0, -1.0]);
    Fm = distribloads(el1femm,  geom,  u,  fi,  2);
    le = selectelem(fens, boundaryfes,  box= [L, L, -Inf,  Inf],  inflate=  tolerance);
    el2femm =  FEMMBase(IntegDomain(subset(boundaryfes, le),  GaussRule(1, 2)))
    fi = ForceIntensity([1.0, 0.0]);
    Fm += distribloads(el2femm,  geom,  u,  fi,  2);

    MR = DeforModelRed2DStress
    material = MatDeforElastIso(MR,  0.0, E, nu, cte)

    femm = FEMMDeforLinear(MR, IntegDomain(fes,  GaussRule(2, 2)),  material)

    K = stiffness(femm,  geom,  u)
    U=  K\(Fm)
    scattersysvec!(u, U[:])

    fld= fieldfromintegpoints(femm, geom, u, :princCauchy, 1)
    # @test norm(maximum(fld.values) - 8.190847372888073e7)/8.190847372888073e7 <= 1.0e-4
    File =  "mholestr3-s1.vtk"
    vtkexportmesh(File, fens, fes; scalars=[("sigma_1", fld.values)], vectors=[("u", u.values)])
    #@async run(`"paraview.exe" $File`)
    fld= fieldfromintegpoints(femm, geom, u, :maxshear, 1)
    @test norm(maximum(fld.values) - 5.921999943843146)/5.921999943843146 <= 1.0e-4
    # File =  "mholestr3-maxshear.vtk"
    # vtkexportmesh(File, fens, fes; scalars=[("maxshear", fld.values)], vectors=[("u", u.values)])
    # @async run(`"paraview.exe" $File`)

end
end
using .mholestr3
mholestr3.test()


module FTUtils

export ftdiff, ftaxis, ft, ift, psd, ssb

function ftdiff(N::Int, dx::Real)
    return 1/(N*dx)
end

function ftaxis(N::Int, dx::Real, ssb::Bool=false)
    f = (collect(0:N-1)-fld(N,2)) * ftdiff(N,dx)
    if ssb
        f = f[Int(fld(N,2))+1:end]
    end
    return f
end

function ft(y::Vector{T}, dt::Real) where {T<:Number}
    return y*dt |> ifftshift |> fft |> fftshift
end

function ift(Y::Vector{T}, df::Real) where {T<:Number}
    return Y*df |> ifftshift |> bfft |> fftshift
end

function psd(y::Vector{T}, dt::Real, ssb=true) where {T<:Number}
    N = length(y)
    psd = abs2.(ft(y,dt)) * ftdiff(N-1,dt)
    if ssb
        dc = Int(fld(N,2)) + 1
        psd[dc+1:end] *= 2
        psd = psd[dc:end]
    end
    return psd
end

end # module


#
# Basic two-body orbit simularion written in julia
#
using PyPlot

# Universal gravitation constant
const G = 6.67e-11

#
# Read speed, position and mass of first body
#
linia = readline()
pos = split(strip(linia), " ")
linia = readline()
spd = split(strip(linia), " ")
mass = strip(readline())

# Initialize and load position and velocity vectors
r1 = zeros(3,1)
v1 = zeros(3,1)
for i in 1:3
  r1[i] = float64(pos[i])
  v1[i] = float64(spd[i])
end
# Initialize mass
m1 = float64(mass)

#
# Read speed, position and mass of second body
#
linia = readline()
pos = split(strip(linia), " ")
linia = readline()
spd = split(strip(linia), " ")
mass = strip(readline())

# Initialize and load position and velocity vectors
r2 = zeros(3,1)
v2 = zeros(3,1)
for i in 1:3
  r2[i] = float64(pos[i])
  v2[i] = float64(spd[i])
end
# Initialize mass
m2 = float64(mass)

# Time increment (in seconds) and number of steps
# will be a day.
linia = readline()
times = split(strip(linia), " ")
nintv = int32(times[2])
dt = float64(times[1])

# Newton's formula for gravitational force
function grav(r1,r2,m1,m2)
  r = norm(r1-r2)
  f1 = G * m1 * m2 * (r2 - r1) / r^3
  f2 = G * m1 * m2 * (r1 - r2) / r^3
  return f1,f2
end

# Newton's formula for acceleration
function accel(f,m)
  return f/m
end

# Apply a change (accel or velocity) to a vector for an interval (dt)
function applydt(vector, change, dt)
  return vector + change * dt
end

# Matrices to contain the (x,y) coordinates computed for each iteration
# for bodies 1 and 2
C1 = zeros(nintv ,2)
C2 = zeros(nintv ,2)

# Compute the orbit position for each step
for i in 1:nintv
  C1[i,1] = r1[1]
  C1[i,2] = r1[2]
  C2[i,1] = r2[1]
  C2[i,2] = r2[2]

  f1,f2 = grav(r1,r2,m1,m2)
  a1 = accel(f1,m1)
  a2 = accel(f2,m2)
  v1 = applydt(v1,a1,dt)
  v2 = applydt(v2,a2,dt)
  r1 = applydt(r1,v1,dt)
  r2 = applydt(r2,v2,dt)
  println(i," r1=",r1,",r2=",r2)
end

# Plot both orbits
scatter(C1[1:nintv,1],C1[1:nintv,2],marker=".")
scatter(C2[1:nintv,1],C2[1:nintv,2],marker=",")


abstract QuadratureRule

immutable StroudRule{sd, deg} <: QuadratureRule end

nqp1d{sd, deg}(::Type{StroudRule{sd, deg}}) = deg
nqp{sd, deg}(::Type{StroudRule{sd, deg}}) = deg^sd

# let's take another look at the stroud evaluation.
# can we make it cleaner and closer to Ainsworth's paper?

function degminalphas(ell, deg)
    result = :($deg)
    for i=1:ell
        ai = symbol("alpha",i)
        result = :($(result) - $(ai))
    end
    return result
end

function alphaloop(alphamin, alphamax, deg, body)
    result = body
    for i=alphamax:-1:alphamin
        ai = symbol("alpha", i)
        result = Expr(:for,
                      :($(ai)=0:$(degminalphas(i-1, deg))),
                      result)
    end
    return result
end

function iloop(imin, imax, q, body)
    result = body
    for i=imax:-1:imin
        ii = symbol("i",i)
        result = Expr(:for,
                      :(($ii)=1:$(q)),
                      result)
    end
    return result
end

function index_expr(head, idx)
    ref = Expr(:ref, head)
    for i in idx
        push!(ref.args, i)
    end
    return ref
end

# I just need to write a function that will generate the body for eval_step
# and can use it for each dimension

# warning, I have reversed the order of indices over Ainsworth's
# paper to allow maximal stride-1 access!

# warning, I also seem to have the look-up into the stroud qp in the reverse
# order

function eval_step_impl(sdim, ell, deg, q)
    alpha_symbs = [symbol("alpha",i) for i=1:sdim]
    i_symbs = [symbol("i",i) for i=1:sdim]

    aip1 = [:($(a)+1) for a in alpha_symbs]

    # build innermost loop nest
    Cin_args = vcat(reverse(i_symbs[ell+1:sdim]),
                    reverse(aip1[1:ell]))
    Cout_args = vcat(reverse(i_symbs[ell:sdim]),
                     reverse(aip1[1:ell-1]))

    Cin_ref = index_expr(symbol("C",sdim-ell), Cin_args)
    Cout_ref = index_expr(symbol("C",sdim-ell+1), Cout_args)

    result = Expr(:for,
                  :($(alpha_symbs[ell])=0:$(degminalphas(ell-1, deg))),
                  Expr(:block,
                       iloop(ell+1, sdim, q,
                             :($(Cout_ref) += w * $(Cin_ref))),
                       :(w *= r * ($(degminalphas(ell, deg)))
                         / (1.0 + $(alpha_symbs[ell])))
                       )
                  )

    # this gets rid of the artificial "begin/end" block in the ell=1 case
    if ell > 1
        result = Expr(:for,
                      :($(i_symbs[ell]) = 1:$(q)),
                      Expr(:block,
                           :(xi=qpts[$(i_symbs[ell]),$(sdim-ell+1)]),
                           :(s=1-xi),
                           :(r=xi/s),
                           alphaloop(1, ell-1, deg,
                                     Expr(:block,
                                          :(w=s^$(degminalphas(ell-1, deg))),
                                          result))))

    else
        result = Expr(:for,
                      :($(i_symbs[ell]) = 1:$(q)),
                      Expr(:block,
                           :(xi=qpts[$(i_symbs[ell]),$(sdim-ell+1)]),
                           :(s=1-xi),
                           :(r=xi/s),
                           :(w=s^$(degminalphas(ell-1, deg))),
                           result))
    end

    return result

end

function make_evaluator(sdim, qpts, pdeg)
    # TODO: special case for 1d...
    qdeg = size(qpts, 1)

    alpha_symbs = [symbol("alpha",i) for i=1:sdim]
    aip1 = [:($(a)+1) for a in alpha_symbs]
    i_symbs = [symbol("i",i) for i=1:sdim]
    Ci = [symbol("C", i) for i=0:sdim]

    inside_impl = Expr(:block)

    initializeCs = [:($(Ci[i+1]) *= 0.0) for i=1:sdim]

    for initC in initializeCs
        push!(inside_impl.args, initC)
    end

    C0ref = index_expr(:C0, reverse(aip1))

    setC0 = alphaloop(1, sdim, pdeg,
                      Expr(:block,
                           :($(C0ref) = coeffs[cur]),
                           :(cur += 1)))

    push!(inside_impl.args, :(cur=1))
    push!(inside_impl.args, setC0)

    for ell=sdim:-1:1
        push!(inside_impl.args, eval_step_impl(sdim, ell, pdeg, qdeg))
    end

    # write from last C to result
    push!(inside_impl.args, :(cur=1))
    result_src = index_expr(symbol("C",sdim),
                            reverse(i_symbs))
    push!(inside_impl.args,
          iloop(1,sdim, qdeg,
                Expr(:block,
                     :(result[cur] = $(result_src)),
                     :(cur += 1))))

    eval_func = Expr(:function,
                     :(evaluate(coeffs, result)),
                     inside_impl)

    # enclose this inside a function that initializes the C
    # values

    maker_body = Expr(:block)

    # initialize the C arrays
    for i=0:sdim
        Ci = symbol("C", i)
        zs = Expr(:call,
                  :zeros)
        for j=1:i
            push!(zs.args, qdeg)
        end
        for j=i+1:sdim
            push!(zs.args, pdeg+1)
        end
        push!(maker_body.args, :($(Ci) = $(zs)))
    end

    push!(maker_body.args, eval_func)
    push!(maker_body.args, :(return evaluate))

    maker = Expr(:function,
                 :(make(qpts)),
                 maker_body)

    println(maker)

    maker_f = eval(maker)
    return maker_f(qpts)

end

function make_evaluator(sdim, qpts, pdeg)
    # TODO: special case for 1d...
    qdeg = size(qpts, 1)

    alpha_symbs = [symbol("alpha",i) for i=1:sdim]
    aip1 = [:($(a)+1) for a in alpha_symbs]
    i_symbs = [symbol("i",i) for i=1:sdim]
    Ci = [symbol("C", i) for i=0:sdim]

    inside_impl = Expr(:block)

    initializeCs = [:($(Ci[i+1]) *= 0.0) for i=1:sdim]

    for initC in initializeCs
        push!(inside_impl.args, initC)
    end

    C0ref = index_expr(:C0, reverse(aip1))

    setC0 = alphaloop(1, sdim, pdeg,
                      Expr(:block,
                           :($(C0ref) = coeffs[cur]),
                           :(cur += 1)))

    push!(inside_impl.args, :(cur=1))
    push!(inside_impl.args, setC0)

    for ell=sdim:-1:1
        push!(inside_impl.args, eval_step_impl(sdim, ell, pdeg, qdeg))
    end

    # write from last C to result
    push!(inside_impl.args, :(cur=1))
    result_src = index_expr(symbol("C",sdim),
                            reverse(i_symbs))
    push!(inside_impl.args,
          iloop(1,sdim, qdeg,
                Expr(:block,
                     :(result[cur] = $(result_src)),
                     :(cur += 1))))

    eval_func = Expr(:function,
                     :(evaluate(coeffs, result)),
                     inside_impl)

    # enclose this inside a function that initializes the C
    # values

    maker_body = Expr(:block)

    # initialize the C arrays
    for i=0:sdim
        Ci = symbol("C", i)
        zs = Expr(:call,
                  :zeros)
        for j=1:i
            push!(zs.args, qdeg)
        end
        for j=i+1:sdim
            push!(zs.args, pdeg+1)
        end
        push!(maker_body.args, :($(Ci) = $(zs)))
    end

    push!(maker_body.args, eval_func)
    push!(maker_body.args, :(return evaluate))

    maker = Expr(:function,
                 :(make(qpts)),
                 maker_body)

    println(maker)

    maker_f = eval(maker)
    return maker_f(qpts)

end

function moment_step_impl(sdim, ell, deg, q)
    alpha_symbs = [symbol("alpha",i) for i=1:sdim]
    i_symbs = [symbol("i",i) for i=1:sdim]

    aip1 = [:($(a)+1) for a in alpha_symbs]

    # build innermost loop nest
    Fin_args = vcat(reverse(aip1[1:ell-1]),
                    reverse(i_symbs[ell:sdim]))

    Fout_args = vcat(reverse(i_symbs[ell+1:sdim]),
                     reverse(aip1[1:ell]))

    Fin_ref = index_expr(symbol("F",sdim-ell), Fin_args)
    Fout_ref = index_expr(symbol("F",sdim-ell+1), Fout_args)

    result = Expr(:for,
                  :($(alpha_symbs[ell])=0:$(degminalphas(ell-1, deg))),
                  Expr(:block,
                       iloop(ell+1, sdim, q,
                             :($(Fout_ref) += w * $(Fin_ref))),
                       :(w *= r * ($(degminalphas(ell, deg)))
                         / (1.0 + $(alpha_symbs[ell])))
                       )
                  )

    # this gets rid of the artificial "begin/end" block in the ell=1 case
    if ell > 1
        result = Expr(:for,
                      :($(i_symbs[ell]) = 1:$(q)),
                      Expr(:block,
                           :(xi=qpts[$(i_symbs[ell]),$(sdim-ell+1)]),
                           :(omega = qwts[$(i_symbs[ell]),$(sdim-ell+1)]),
                           :(s=1-xi),
                           :(r=xi/s),
                           alphaloop(1, ell-1, deg,
                                     Expr(:block,
                                          :(w=omega * s^$(degminalphas(ell-1, deg))),
                                          result))))

    else
        result = Expr(:for,
                      :($(i_symbs[ell]) = 1:$(q)),
                      Expr(:block,
                           :(xi=qpts[$(i_symbs[ell]),$(sdim-ell+1)]),
                           :(omega = qwts[$(i_symbs[ell]),$(sdim-ell+1)]),
                           :(s=1-xi),
                           :(r=xi/s),
                           :(w=omega * s^$(degminalphas(ell-1, deg))),
                           result))
    end

    return result

end

# TODO: reverse storage order for integration back to what
# Ainswort et al have so that we have stride-1 access.
# TODO: Check sizes of arrays that are initialized.

function make_integrator(sdim, qpts, qwts, pdeg)
    # TODO: special case for 1d...
    qdeg = size(qpts, 1)

    alpha_symbs = [symbol("alpha",i) for i=1:sdim]
    aip1 = [:($(a)+1) for a in alpha_symbs]
    i_symbs = [symbol("i",i) for i=1:sdim]

    Fi = [symbol("F", i) for i=0:sdim]

    inside_impl = Expr(:block)

    initializeFs = [:($(Fi[i+1]) *= 0.0) for i=1:sdim]

    for initF in initializeFs
        push!(inside_impl.args, initF)
    end

    F0ref = index_expr(:F0, reverse(i_symbs))

    setF0 = iloop(1, sdim, qdeg,
                  Expr(:block,
                       :($(F0ref) = vals[cur]),
                       :(cur += 1)))

    push!(inside_impl.args, :(cur=1))
    push!(inside_impl.args, setF0)

    for ell=1:sdim
        push!(inside_impl.args, moment_step_impl(sdim, ell, pdeg, qdeg))
    end

    # write from last F to result
    push!(inside_impl.args, :(cur=1))
    result_src = index_expr(symbol("F",sdim),
                            reverse(aip1))
    push!(inside_impl.args,
          alphaloop(1,sdim, pdeg,
                    Expr(:block,
                         :(result[cur] = $(result_src)),
                         :(cur += 1))))

    eval_func = Expr(:function,
                     :(integrate(vals, result)),
                     inside_impl)

    # enclose this inside a function that initializes the F
    # values

    maker_body = Expr(:block)

    # initialize the F arrays
    for i=0:sdim
        Fi = symbol("F", i)
        zs = Expr(:call,
                  :zeros)
        for j=1:i
            push!(zs.args, qdeg)
        end
        for j=i+1:sdim
            push!(zs.args, pdeg+1)
        end
        push!(maker_body.args, :($(Fi) = $(zs)))
    end

    push!(maker_body.args, eval_func)
    push!(maker_body.args, :(return integrate))

    maker = Expr(:function,
                 :(make(qpts, qwts)),
                 maker_body)

    println(maker)

    maker_f = eval(maker)
    return maker_f(qpts, qwts)

end


# to run from the nearly-neutral/src folder:
# julia -L InfAlleles.jl run.jl ../../NearlyNeutralPaper/results21/Richness_figure3/ngens200K/nn_neut_Nmulist_fm45_N25_6400
#=
Estimates of theta = N_e mu based on Watterson "The homozygosity test of neutrality" (1978).

allele_freqs is the vector of allele frequecies of the infinite alleles model population.
=#

export watterson_homozygosity, p_homozygosity, watterson_theta, IQV, w_homoz

function watterson_homozygosity( allele_freqs::Population )
  if length(allele_freqs) == 0
    return 0.0   # Not sure that this is valid
  end
  n = sum(allele_freqs)
  sum_a_sq = 0
  for a in allele_freqs
    sum_a_sq += a^2
  end
  Float64(sum_a_sq)/Float64(n^2)
end
#=
function watterson_theta( allele_freqs::Config )
  1.0/watterson_homozygosity( allele_freqs ) - 1.0
end
=#

function watterson_theta( allele_freqs::Population )
  1.0/watterson_homozygosity( allele_freqs ) - 1.0
end

@doc """ function IQV()
Allan Wilcox's "index of quantitative variation" defined by formula (7)
  of Mark E. Madsen's "Neutral Cultural Transmission in Time Averaged
Archaeological Assemblages" arXiv:1024.2043v2 (2012).
A measure of "evenness".  IQV of a population with a single trait is 0,
while IQV of a population with an even distribution of traits is close to 1.0.
"""
function IQV( allele_freqs::Population )
  k = length(allele_freqs)
  if k == 1
    return 0.0
  end
  Fk = Float64(k)
  wh = watterson_homozygosity( allele_freqs )
  (1.0-wh)*Fk/(Fk-1.0)
end


# ---
# title: New columns on the fly
# cover: assets/new_columns_on_the_fly.png
# description: Transforming data before generating the plot
# author: "[Pietro Vertechi](https://github.com/piever)"
# ---

using AlgebraOfGraphics, CairoMakie
set_aog_theme!() #src

# Use a `Tuple` to pass combine several columns into a unique operation.

df = (x=rand(100), y=rand(100), z=rand(100), c=rand(["a", "b"], 100))
layers = linear() + mapping(color=:z)
plt = data(df) * layers * mapping(:x, (:x, :y, :z) => (+) => "x + y + z", layout=:c)
fg = draw(plt)

# save cover image #src
mkpath("assets") #src
save("assets/new_columns_on_the_fly.png", fg) #src


using StochasticDiffEq, ForwardDiff
using Test

u0=1/2
f(u,p,t) = u
g(u,p,t) = u
dt = 1//2^(4)
tspan = (0.0,1.0)
prob = SDEProblem{false}(f,g,u0,(0.0,1.0))
sol = @inferred solve(prob,EM(),dt=dt)

# issue #351
@inferred StochasticDiffEq.sde_interpolation(ForwardDiff.Dual(0.5), sol.interp, 1, Val{0}, prob.p, :left)


"""
Parse a wire definition into a list of line segments
wire_def should look like ["R234", "U56", "L78"] etc
"""
function parse_wire_def(wire_def)
    cursor = [0 0]
    directions = Dict(
        'U' => [ 0  1],
        'D' => [ 0 -1],
        'L' => [-1  0],
        'R' => [ 1  0]
    )

    line_segs = []
    for seg in wire_def
        direction = seg[1]
        new_cursor = cursor + directions[direction] * parse(Int32, seg[2:end])
        push!(line_segs, (cursor, new_cursor))
        cursor = new_cursor
    end
    return line_segs
end

"""
Test if two line segments intersect, and if so, return their intersection point
"""
function find_intersection(seg1, seg2)
    # We only need to support vertical vs horizontal segment intersections...
    # at least I think a vert-vert or horz-horz intersection doesn't count based on task definition!
    (sx1, sy1), (ex1, ey1) = seg1
    (sx2, sy2), (ex2, ey2) = seg2

    # We need to order our segments so start x / start y are lower than end x / end y in each case
    sx1, sy1, ex1, ey1 = min(sx1, ex1), min(sy1, ey1), max(sx1, ex1), max(sy1, ey1)
    sx2, sy2, ex2, ey2 = min(sx2, ex2), min(sy2, ey2), max(sx2, ex2), max(sy2, ey2)

    # Now let's arrange as horizontal first, then vertical, if possible
    if sx1 == ex1 && sy2 == ey2
        # Vert then horz. Swap them.
        sx1, sy1, ex1, ey1, sx2, sy2, ex2, ey2 = sx2, sy2, ex2, ey2, sx1, sy1, ex1, ey1
    elseif !(sy1 == ey1 && sx2 == ex2)
        # It's not horz then vert either: no intersections
        return nothing
    end

    # OK, now sy1 == ey1 and sx2 == ey2. Is there an intersection?
    return (sy1 in sy2:ey2 && sx2 in sx1:ex1) ? [sx2, sy1] : nothing
end

# Part 1: read and parse input (should be 2 definitions, one for each wire)
w = open(f -> [parse_wire_def(split(line, ',')) for line in readlines(f)], "day03_input.txt")

# Do a stupid all-segs-vs-all-segs test to find all intersection points
isects = []
for seg1 in w[1]
    for seg2 in w[2]
        isect = find_intersection(seg1, seg2)
        if !isnothing(isect)
            push!(isects, isect)
        end
    end
end

# Find lowest Manhattan distance to origin apart from the first (0,0) intersection
filter!(isect -> isect[1] != 0 || isect[2] != 0, isects)
dists = [abs(x) + abs(y) for (x, y) in isects]
println("Minimum distance: $(minimum(dists))")

# Part 2: distance to intersection
"""
Compute lowest distance to the given intersection for the wire defined by list of segments wire_segs
"""
function distance_to_intersection(wire_segs, intersection)
    dist = 0
    for wire_seg in wire_segs
        # Is this wire segment going to hit the intersection?
        (sx, sy), (ex, ey) = wire_seg
        ix, iy = intersection
        if ix == sx == ex && iy in min(sy,ey):max(sy,ey)
            # Vertical segment reaching the intersection
            return dist + abs(iy - sy)
        elseif iy == sy == ey && ix in min(sx,ex):max(sx,ex)
            # Horizontal segment reaching the intersection
            return dist + abs(ix - sx)
        else
            dist += abs(ex - sx) + abs(ey - sy)
        end
    end
    return nothing
end

# Compute distance to each intersection along wire
wdists = [distance_to_intersection(w[1], isect) + distance_to_intersection(w[2], isect) for isect in isects]
println("Lowest combined distance along wire: $(minimum(wdists))")


using Test, LinearAlgebra, DynamicPolynomials
using LearningAlgebraicVarieties
const LAV = LearningAlgebraicVarieties

### auxiliary_functions
@testset "Auxiliary Functions" begin
    data = rand(2,10)

    D = LAV.ScaledEuclidean(data)
    @test maximum(D) <= 1

    D = LAV.FubiniStudyDistances(data)
    @test size(D) == (10,10)

    D = zeros(10,10)
    LAV.FubiniStudyDistances!(D, data)
    @test maximum(D) > 0

    D = LAV.ScaledFubiniStudy(data)
    @test maximum(D) <= 1

    @polyvar x y
    f = x^2 + 2y^2 - 1

    D = LAV.EllipsoidDistances(data, f, 0.5)
    @test size(D) == (10,10)
    D = LAV.EllipsoidDistances(data, [f], 0.5)
    @test size(D) == (10,10)
end

### estimate_dimension
@testset "PCA" begin
    data_affine = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)
    data_linear = hcat(map(i -> [i[1];-i[1]], rand(10))...)

    d = LAV.EstimateDimensionPCA(data_affine, false)
    @test d == 1
    d = LAV.EstimateDimensionPCA(data_linear, false)
    @test d == 1

    d = LAV.EstimateDimensionPCA(data_affine, true)
    @test d == 1
    d = LAV.EstimateDimensionPCA(data_linear, true)
    @test d == 0

    ϵ_array = Array{Float64}(range(0.1, length = 25, stop = 0.9))

    d = LAV.EstimateDimensionNPCA(data_affine, ϵ_array, false)
    @test d[end] == 1
    d = LAV.EstimateDimensionNPCA(data_affine, false)
    @test d[end] == 1
    d = LAV.EstimateDimensionNPCA(data_linear, ϵ_array, false)
    @test d[end] == 1

    d = LAV.EstimateDimensionNPCA(data_affine, ϵ_array, true)
    @test d[end] == 1
    d = LAV.EstimateDimensionNPCA(data_affine, true)
    @test d[end] == 1
    d = LAV.EstimateDimensionNPCA(data_linear, ϵ_array, true)
    @test d[end] == 0
end

@testset "CorrSum" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)
    ϵ_array = Array{Float64}(range(0.1, length = 25, stop = 0.9))

    d = LAV.EstimateDimensionCorrSum(data, ϵ_array, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionCorrSum(data, ϵ_array, true)
    @test length(d) == 25

    d = LAV.EstimateDimensionCorrSum(data, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionCorrSum(data, true)
    @test length(d) == 25
end

@testset "BoxCounting" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)
    ϵ_array = Array{Float64}(range(0.1, length = 25, stop = 0.9))

    d = LAV.EstimateDimensionBoxCounting(data, ϵ_array, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionBoxCounting(data, ϵ_array, true)
    @test length(d) == 25

    d = LAV.EstimateDimensionBoxCounting(data, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionBoxCounting(data, true)
    @test length(d) == 25
end

@testset "Bickel-Levina" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)
    ϵ_array = Array{Float64}(range(0.1, length = 25, stop = 0.9))

    d = LAV.EstimateDimensionMLE(data, ϵ_array, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionMLE(data, ϵ_array, true)
    @test length(d) == 25

    d = LAV.EstimateDimensionMLE(data, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionMLE(data, true)
    @test length(d) == 25
end

@testset "ANOVA" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)
    ϵ_array = Array{Float64}(range(0.1, length = 25, stop = 0.9))

    d = LAV.EstimateDimensionANOVA(data, ϵ_array, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionANOVA(data, ϵ_array, true)
    @test length(d) == 25

    d = LAV.EstimateDimensionANOVA(data, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionANOVA(data, true)
    @test length(d) == 25
end

@testset "PHCurve" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)
    ϵ_array = Array{Float64}(range(0.1, length = 25, stop = 0.9))

    d = LAV.EstimateDimensionPHCurve(data, ϵ_array, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionPHCurve(data, ϵ_array, true)
    @test length(d) == 25

    d = LAV.EstimateDimensionPHCurve(data, false)
    @test length(d) == 25
    d = LAV.EstimateDimensionPHCurve(data, true)
    @test length(d) == 25
end

###estimate_equations
@testset "FindEquations" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)

    f = LAV.FindEquations(data, :with_svd, 1, false)
    @test abs(f[1]([1;0])) < 1e-15

    f = LAV.FindEquations(data, :with_svd, 2, true)
    @test f[1] == 0
end

@testset "Vandermonde" begin
    data = hcat(map(i -> [i[1];-i[1]+1], rand(10))...)

    M = LAV.MultivariateVandermondeMatrix(data, [[1,0], [1,1]])
    @test size(M.Vandermonde) == (10,2)

    M = LAV.MultivariateVandermondeMatrix(data, 1, false)
    @test size(M.Vandermonde) == (10,3)

    f = LAV.FindEquations(M, :with_svd)
    @test abs(f[1]([1;0])) < 1e-15

    f = LAV.FindEquations(M, :with_svd, 1e-12)
    @test abs(f[1]([1;0])) < 1e-15

    f = LAV.FindEquations(M, :with_qr)
    @test abs(f[1]([1;0])) < 1e-15

    f = LAV.FindEquations(M, :with_qr, 1e-12)
    @test abs(f[1]([1;0])) < 1e-15

    f = LAV.FindEquations(M, :with_rref)
    @test abs(f[1]([1;0])) < 1e-15

    f = LAV.FindEquations(M, :with_rref, 1e-12)
    @test abs(f[1]([1;0])) < 1e-15
end


using LinearAlgebra
using CompScienceMeshes
using SauterSchwab3D

const pI   = point(6,5,3)
const pII  = point(5,2,3)
const pIII = point(7,1,0)
const pIV  = point(3,4,0)

const qII  = point(3,8,4)
const qIII = point(7,5,6)

const P = simplex(pI,pII,pIII,pIV)
const Q = simplex(pI,qII,qIII)

const sing = SauterSchwab3D.singularity_detection(P,Q)

Accuracy2 = 16
cv_ref = CommonVertex5D(sing,SauterSchwab3D._legendre(Accuracy2,0.0,1.0))

function integrand(x,y)
   ((x-pI)'*(y-pIV))*return(exp(-im*1*norm(x-y))/(4pi*norm(x-y)))
end

function INTEGRAND(u,v)
   n1 = neighborhood(P,u)
   n2 = neighborhood(Q,v)
   x = cartesian(n1)
   y = cartesian(n2)
   output = integrand(x,y)*jacobian(n1)*jacobian(n2)
   return(output)
end

print("Ref: ")
ref = sauterschwab_parameterized(INTEGRAND, cv_ref)
println(ref)
println()

res_tp =[]
res_sp = []
res_gm = []
n1 = []
n2 = []
n3 = []

#Grauss tensor product
for i in 2:1:14
   Accuracy = i
   cv = CommonVertex5D(sing,SauterSchwab3D._legendre(Accuracy,0.0,1.0))

   int_tp = sauterschwab_parameterized(INTEGRAND, cv)

   num_pts = 2*length(cv.qps)^5
   push!(n1,num_pts)

   push!(res_tp,int_tp)
end

#ShunnHam simplex product
for i in 2:1:7
   Accuracy = i
   cv_s = CommonVertex5D_S(sing,(SauterSchwab3D._shunnham3D(Accuracy),
                            SauterSchwab3D._shunnham2D(Accuracy)))

   int_sp = sauterschwab_parameterized(INTEGRAND, cv_s)

   num_pts = 2*length(cv_s.qps[1])*length(cv_s.qps[2])
   push!(n2,num_pts)
   push!(res_sp,int_sp)
end

#GrundmannMoeller simplex product
for i in 2:1:12
   Accuracy = i
   cv_gm = CommonVertex5D_S(sing,(SauterSchwab3D._grundmannMoeller3D(2*Accuracy-1),
                             SauterSchwab3D._grundmannMoeller2D(2*Accuracy-1)))

   int_gm = sauterschwab_parameterized(INTEGRAND, cv_gm)

   num_pts = 2*length(cv_gm.qps[1])*length(cv_gm.qps[2])
   push!(n3,num_pts)
   push!(res_gm,int_gm)
end

err_tp = norm.(res_tp.-ref)/norm(ref)
err_sp = norm.(res_sp.-ref)/norm(ref)
err_gm = norm.(res_gm.-ref)/norm(ref)

println(err_tp)
println(err_sp)
println(err_gm)

using Plots
plot( yaxis=:log, xaxis=:log, fontfamily="Times" , width=600, height=400)
plot!(n1,err_tp, label="Gauss Tensor-Product",markershape=:circle)
plot!(n2,err_sp, label="Simplex Tensor-Product",markershape=:rect)
#plot!(n3,err_gm, label="Simplex-Product GM",markershape=:x)
plot!(xlims=(1e1,1e5),ylims=(1e-7,1))
p = plot!(xlabel="#Quad. points/Func. evals.", ylabel="Rel. Error.", title="Common Vertex 5D",legend=:bottomleft)
savefig(p,"CommonVetrtex5DPlot.png")

using BenchmarkTools

ref = -0.3263644363080338 + 0.46042867580530505im

cv = CommonVertex5D(sing,SauterSchwab3D._legendre(6,0.0,1.0))

int_tp = sauterschwab_parameterized(INTEGRAND, cv)

num_pts = 2*length(cv.qps)^5
println(num_pts)
err_tp = norm.(int_tp.-ref)/norm(ref)
println(err_tp)
@time for i in 1:100 sauterschwab_parameterized(INTEGRAND, cv) end

cv_s = CommonVertex5D_S(sing,(SauterSchwab3D._shunnham3D(6),
SauterSchwab3D._shunnham2D(6)))

int_sp = sauterschwab_parameterized(INTEGRAND, cv_s)

num_pts = 2*length(cv_s.qps[1])*length(cv_s.qps[2])
println(num_pts)
err_sp = norm.(int_sp.-ref)/norm(ref)
println(err_sp)
@time for i in 1:100 sauterschwab_parameterized(INTEGRAND, cv_s) end

#=
cv_gm = CommonVertex5D_S((SauterSchwab3D._grundmannMoeller3D(2*6-1),
SauterSchwab3D._grundmannMoeller2D(2*6-1)))

int_gm = sauterschwab_parameterized(INTEGRAND, cv_gm)

num_pts = 2*length(cv_gm.qps[1])*length(cv_gm.qps[2])
println(num_pts)
err_gm = norm.(int_gm.-ref)/norm(ref)
println(err_gm)
=#


using BenchmarkTools

"""
    lookandsay(n::String)

Perform the look and say algorithm once on the given string.
"""
function lookandsay(n::String)
    sequence = IOBuffer()
    i, c = 1, 1
    while i ≤ length(n)
        if i != length(n) && n[i] == n[i+1]
            c += 1
        else
            print(sequence, c, n[i])
            c = 1
        end
        i += 1
    end
    return newstring(take!(sequence))
end

"""
    lookandsayseq(n::Integer, startsequence::String)

Iteratively construct the nth look and say sequence.
"""
function lookandsayseq(n::Integer, startsequence::String)
    sequence = Array{String}(n)
    sequence[1] = startsequence
    if n > 1
        for i in 2:n
            sequence[i] = lookandsay(sequence[i-1])
        end
    end
    return sequence
end

N = parse(Int, ARGS[1])

println("--------------------------")
println(@sprintf "Look and say sequence: %d" N)
println("--------------------------")

@btime begin
    r = lookandsayseq(N, "1223334444")
end
    #println(r[N])


@enum OperatorType begin
    unary
    binary
end

struct Operator
    op::Symbol
    type::OperatorType
end

unary_operators=[
    Operator(:(sin),unary),
    Operator(:(cos),unary),
    Operator(:(tan),unary),
]
binary_operators=[
    Operator(:(+),binary),
    Operator(:(*),binary),
    Operator(:(-),binary),
    Operator(:(/),binary),
    Operator(:(^),binary),
]
all_operators=reduce(vcat,[
    unary_operators,
    binary_operators
])

function get_operator()
    return rand(all_operators)
end
function get_binary_operator()
    return rand(binary_operators)
end

function create_expression( x::Symbol, p::Symbol, depth=0, pcount=1 )
	expr1=x
    #expr2=Meta.parse(string(p)*"["*string(pcount)*"]")
    expr2=:( p[$pcount] )
	pcount+=1
    op=get_operator()
    if op.type == unary
        expr=Expr( :call, get_binary_operator().op, Expr( :call, op.op, expr1), expr2)
    elseif op.type == binary
        expr=Expr( :call, op.op, expr1, expr2)
	end
	depth-=1
	while depth>=0
        expr1=x
        expr2=:( p[$pcount] )
		op=get_operator()
		pcount+=1
		if op.type == unary
			expr3=Expr( :call, get_binary_operator().op, Expr( :call, op.op, expr1), expr2)
		elseif op.type == binary
			expr3=Expr( :call, op.op, expr1, expr2)
        end
        op=get_binary_operator()
        expr=Expr( :call, op.op, expr, expr3)
		depth-=1
	end
    return expr,pcount
end


function truncated(c, k, x)
    if x ≥ c
        (x - c)^k
    else
        zero(x)
    end
end


__precompile__(true)

module SimplexSplitting

using LinearAlgebra
using Printf
using Distributions
using Parameters

include("../even_sampling.jl")
include("complementary.jl")
include("simplex_split.jl")
include("simplex_subdivision.jl")
include("simplex_subdivision_single.jl")
include("heaviside.jl")
include("embed.jl")
include("triangulate.jl")
include("refine_triangulation.jl")
include("refine_triangulation_with_images.jl")
include("invariantset.jl")
include("centroids_radii.jl")
include("simplexvolumes.jl")
include("refine_recursive.jl")
include("refine_recursive_withimages.jl")
include("refine_t.jl")
include("refine_variable_k.jl")
include("orientations.jl")

export tensordecomposition, simplex_split, simplicial_subdivision_single,
simplicial_subdivision, embed, Embedding, embedding, embedding_ex, triangulate, Triangulation, triang_from_embedding, embedding_example, refine_triangulation, simplex_volumes,
refine_triangulation_images, invariantset, centroids_radii2, refine_recursive, refine_recursive_images, refine_variable_k!, refine_variable_k_new, refine_variable_k_newnew, query_refinement,
refine_t!,
gaussian_embedding, centroids_radii2, example_triangulation,
gaussian_embedding_arr,
random_embedding,
orientations,
Simplex,
find_simplex, find_imsimplex,
get_simplices, get_imagesimplices,
maybeintersecting_simplices, maybeintersecting_imsimplices
end # module


module TestMeasures

# using Revise
using Test
using MLJBase
import Distributions
using CategoricalArrays
import Random.seed!
seed!(1234)

@testset "built-in regressor measures" begin

    y    = [1, 2, 3, 4]
    yhat = [4, 3, 2, 1]
    w = [1, 2, 4, 3]
    @test isapprox(mav(yhat, y), 2)
    @test isapprox(mav(yhat, y, w), 9/5)
    @test isapprox(rms(yhat, y), sqrt(5))
    @test isapprox(rms(yhat, y, w), sqrt(21/5))
    @test isapprox(mean(l1(yhat, y)), 2)
    @test isapprox(mean(l1(yhat, y, w)), 9/5)
    @test isapprox(mean(l2(yhat, y)), 5)
    @test isapprox(mean(l2(yhat, y, w)), 21/5)

    yhat = y .+ 1
    @test isapprox(rmsl(yhat, y),
                   sqrt((log(1/2)^2 + log(2/3)^2 + log(3/4)^2 + log(4/5)^2)/4))
    @test isapprox(rmslp1(yhat, y),
                   sqrt((log(2/3)^2 + log(3/4)^2 + log(4/5)^2 + log(5/6)^2)/4))
    @test isapprox(rmsp(yhat, y), sqrt((1 + 1/4 + 1/9 + 1/16)/4))

end

@testset "built-in classifier measures" begin

    y    = categorical(collect("asdfasdfaaassdd"))
    yhat = categorical(collect("asdfaadfaasssdf"))
    w = 1:15
    @test misclassification_rate(yhat, y) ≈ 0.2
    @test misclassification_rate(yhat, y, w) ≈ 4/15
    y = categorical(collect("abb"))
    L = [y[1], y[2]]
    d1 = UnivariateFinite(L, [0.1, 0.9])
    d2 = UnivariateFinite(L, [0.4, 0.6])
    d3 = UnivariateFinite(L, [0.2, 0.8])
    yhat = [d1, d2, d3]
    @test mean(cross_entropy(yhat, y)) ≈ -(log(0.1) + log(0.6) + log(0.8))/3
    scores = BrierScore()(yhat, y)
    @test scores ≈ [-1.62, -0.32, -0.08]
    wscores = BrierScore()(yhat, y, [1, 2, 7])
    @test wscores ≈ scores .* [0.3, 0.6, 2.1]

end

@testset "MLJBase.value" begin

    yhat = rand(5)
    X = (weight=rand(5), x1 = rand(5))
    y = rand(5)
    w = rand(5)

    @test MLJBase.value(mav, yhat, nothing, y, nothing) ≈ mav(yhat, y)
    @test MLJBase.value(mav, yhat, nothing, y, w) ≈ mav(yhat, y, w)

    spooky(yhat, y) = abs.(yhat - y) |> mean
    @test MLJBase.value(spooky, yhat, nothing, y, nothing) ≈ mav(yhat, y)

    cool(yhat, y, w) = abs.(yhat - y) .* w ./ (sum(w)/length(y)) |> mean
    MLJBase.supports_weights(::Type{typeof(cool)}) = true
    @test MLJBase.value(cool, yhat, nothing, y, w) ≈ mav(yhat, y, w)

    funky(yhat, X, y) = X.weight .* abs.(yhat - y) ./ (sum(X.weight)/length(y)) |> mean
    MLJBase.is_feature_dependent(::Type{typeof(funky)}) = true
    @test MLJBase.value(funky, yhat, X, y, nothing) ≈ mav(yhat, y, X.weight)

    weird(yhat, X, y, w) = w .* X.weight .* abs.(yhat - y) ./ sum(w .* X.weight) |> sum
    MLJBase.is_feature_dependent(::Type{typeof(weird)}) = true
    MLJBase.supports_weights(::Type{typeof(weird)}) = true
    @test MLJBase.value(weird, yhat, X, y, w) ≈ mav(yhat, y, X.weight .* w)

end

# for when ROC is added as working dependency:
# y = ["n", "p", "n", "p", "n", "p"]
# yhat = [0.1, 0.2, 0.3, 0.6, 0.7, 0.8]
# @test auc("p")(yhat, y) ≈ 2/3

end
true