module scratch1_06092017_ortho
using FinEtools
using FinEtoolsDeforLinear
using Test

mutable struct MyIData
  c::FInt
  r::FFltVec
  s::FFltVec
end

function test()

  # println("Thick pipe with internal pressure: axially symmetric model")
  #=
  This is a simple modification of the full three-dimensional simulation of
  the tutorial pub_thick_pipe that implements the axially-symmetric model
  reduction procedure.

  An infinitely long thick walled cylindrical pipe
  with inner boundary radius of 3 mm and outer boundary radius of 9 mm is
  subjected to an internal pressure of 1.0 MPa. A wedge   with thickness of
  2 mm and a 90-degree angle sector is considered for the finite element
  analysis. The material properties are taken as  isotropic linear elastic
  with $E=1000$ MPa and $\nu=0.4999$ to represent nearly incompressible
  behavior. This problem has been proposed to by MacNeal and Harder as a
  test of an element's ability to represent the  response of a nearly
  incompressible material. The plane-strain condition is assumed in the
  axial direction of the pipe which together with the radial symmetry
  confines the material in all but the radial direction and therefore
  amplifies the numerical difficulties associated with the confinement of
  the nearly incompressible material.

  There is an analytical solution to this problem. Timoshenko and Goodier
  presented the original solution of Lame in their textbook. We are going
  to compare with  both the stress distribution (radial and hoop stresses)
  and the displacement of the inner  cylindrical surface.

  References:
  - Macneal RH, Harder RL (1985) A proposed standard set of problems to test
  finite element accuracy. Finite Elements in Analysis and Design 1: 3-20.
  - Timoshenko S. and Goodier J. N., Theory of Elasticity, McGraw-Hill, 2nd ed., 1951.

  =#

  # Internal radius of the pipe.
  a=3*phun("MM");
  ##
  # External radius of the pipe.
  b = 9*phun("MM");
  ##
  # Thickness of the slice.
  t = 2*phun("MM");

  ##
  # Geometrical tolerance.
  tolerance   = a/10000.;
  ##
  # Young's modulus and Poisson's ratio.
  E = 1000*phun("MEGA*PA");
  nu = 0.499;
  ##
  # Applied pressure on the internal surface.
  press =   1.0*phun("MEGA*PA");

  ##
  # Analytical solutions.   Radial stress:
  radial_stress(r)  = press*a.^2/(b^2-a^2).*(1-(b^2)./r.^2);
  ##
  # Circumferential (hoop) stress:
  hoop_stress(r) = press*a.^2/(b^2-a^2).*(1+(b^2)./r.^2);

  ##
  # Radial displacement:
  radial_displacement(r) = press*a^2*(1+nu)*(b^2+r.^2*(1-2*nu))/(E*(b^2-a^2).*r);;

  ##
  # Therefore the radial displacement of the loaded surface will be:
  urex  =  radial_displacement(a);

  ##
  # The mesh parameters: The numbers of element edges axially,
  # and through the thickness of the pipe wall (radially).

  na = 1; nt = 10;

  ##
  # Note that the material object needs to be created with the proper
  # model-dimension reduction in effect.  In this case that is the axial symmetry
  # assumption.
  MR = DeforModelRed2DAxisymm
  axisymmetric = true

  # 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  =   Q8block(b-a, t, nt, na);

  # Extract the boundary  and mark the finite elements on the
  # interior surface.
  bdryfes = meshboundary(fes);

  bcl = selectelem(fens, bdryfes, box=[0.,0.,-Inf,Inf], inflate=tolerance);
  internal_fenids= connectednodes(subset(bdryfes,bcl));
  # Now  shape the block  into  the actual wedge piece of the pipe.
  for i=1:count(fens)
    fens.xyz[i,:] = fens.xyz[i,:] + [a; 0.0];
  end

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

  # The plane-strain condition in the axial direction  is specified by selecting nodes
  # on the plane y=0 and y=t.
  l1 = selectnode(fens; box=[-Inf Inf 0.0 0.0], inflate = tolerance)
  setebc!(u, l1, true, 2, 0.0)
  l1 = selectnode(fens; box=[-Inf Inf t t], inflate = tolerance)
  setebc!(u, l1, true, 2, 0.0)

  applyebc!(u)
  numberdofs!(u)

  # The traction boundary condition is applied in the radial
  # direction.

  el1femm =  FEMMBase(IntegDomain(subset(bdryfes,bcl), GaussRule(1, 3), axisymmetric))
  fi = ForceIntensity([press; 0.0]);
  F2= distribloads(el1femm, geom, u, fi, 2);

  # Property and material
  material = MatDeforElastOrtho(MR,  E, nu)

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

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

  # Transfer the solution of the displacement to the nodes on the
  # internal cylindrical surface and convert to
  # cylindrical-coordinate displacements there.
  uv=u.values[internal_fenids,:]
  # Report the  relative displacement on the internal surface:
  # println("(Approximate/true displacement) at the internal surface: $( mean(uv[:,1])/urex*100  ) %")

  # 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, 1)

  # File =  "thick_pipe_sigmax.vtk"
  # vtkexportmesh(File, fens, fes; scalars=[("sigmax", fld.values)])

  # Produce a plot of the solution components in the cylindrical
  # coordinate system.

  function inspector(idat::MyIData, elnum, conn, xe,  out,  xq)
    push!(idat.r, xq[1])
    push!(idat.s, out[idat.c])
    return idat
  end

  idat = MyIData(1, FInt[], FInt[])
  idat = inspectintegpoints(femm, geom, u, collect(1:count(fes)),
  inspector, idat, :Cauchy)

  # using Plots
  # plotly()
  #
  # # Plot the analytical solution.
  # r = linearspace(a,b,100);
  # plot(r, radial_stress(r))
  # # Plot the computed  integration-point data
  # plot!(idat.r, idat.s, m=:circle, color=:red)
  # gui()

  @test  abs(idat.r[1]-0.003126794919243112)<1.0e-9
  @test  abs(idat.s[1]- -910911.9777008593)<1.0e-2

  ## Discussion
  #
  ##
  # The axially symmetric model is clearly very effective
  # computationally, as the size is much reduced compared to the 3-D
  # model.  In conjunction with  uniform or selective reduced integration
  # it can be very accurate as well.
end
end
using .scratch1_06092017_ortho
scratch1_06092017_ortho.test()

module mmLE11Q8mm
using FinEtools
using FinEtoolsDeforLinear
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);

  #     File  =   "mesh.vtk"
  # vtkexportmesh(File, fens, fes)

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

  # Apply EBC's
  l1 = selectnode(fens,box = [-Inf Inf 0 0],inflate = tolerance)
  setebc!(u, l1, true, 2, 00.0)
  l1 = selectnode(fens,box = [-Inf Inf 1.79  1.79],inflate = tolerance)
  setebc!(u, l1, true, 2, 00.0)
  applyebc!(u)
  numberdofs!(u)

  # Temperature field
  dT  = NodalField(reshape(fens.xyz[:,1]+fens.xyz[:,2],size(fens.xyz,1),1));

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

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

  K  = stiffness(femm, geom, u)
  F  =  thermalstrainloads(femm, geom, u, dT)
  #K = cholesky(K)
  U =   K\F
  scattersysvec!(u, U[:])

  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.443052182185006e8, -1.4106181545272605e7]) < 1.0e-2
  # @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 abs(sA[1] - (-93.8569)) < 1.0e-3

  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 .mmLE11Q8mm
mmLE11Q8mm.test()

module mmLE11Q8mmortho
using FinEtools
using FinEtoolsDeforLinear
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);

  #     File  =   "mesh.vtk"
  # vtkexportmesh(File, fens, fes)

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

  # Apply EBC's
  l1 = selectnode(fens,box = [-Inf Inf 0 0],inflate = tolerance)
  setebc!(u, l1, true, 2, 00.0)
  l1 = selectnode(fens,box = [-Inf Inf 1.79  1.79],inflate = tolerance)
  setebc!(u, l1, true, 2, 00.0)
  applyebc!(u)
  numberdofs!(u)

  # Temperature field
  dT  = NodalField(reshape(fens.xyz[:,1]+fens.xyz[:,2],size(fens.xyz,1),1));

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

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

  K  = stiffness(femm, geom, u)
  F  =  thermalstrainloads(femm, geom, u, dT)
  #K = cholesky(K)
  U =   K\F
  scattersysvec!(u, U[:])

  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.443052182185006e8, -1.4106181545272605e7]) < 1.0e-2
  # @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 abs(sA[1] - (-93.8569)) < 1.0e-3

  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 .mmLE11Q8mmortho
mmLE11Q8mmortho.test()

module mLE11Q8aximmm
using FinEtools
using FinEtoolsDeforLinear
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 = 2;                        # 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);

  #     File  =   "mesh.vtk"
  # vtkexportmesh(File, fens, fes)

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

  # Apply EBC's
  lbottom = selectnode(fens,box = [-Inf Inf 0 0],inflate = tolerance)
  setebc!(u, lbottom, true, 2, 00.0)
  ltop = selectnode(fens,box = [-Inf Inf 1.79  1.79],inflate = tolerance)
  setebc!(u, ltop, true, 2, 00.0)
  applyebc!(u)
  numberdofs!(u)

  # Temperature field
  dT  = NodalField(reshape(fens.xyz[:,1]+fens.xyz[:,2],size(fens.xyz,1),1));

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

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

  K  = stiffness(femm, geom, u)
  F  =  thermalstrainloads(femm, geom, u, dT)
  #K = cholesky(K)
  U =   K\F
  scattersysvec!(u, U[:])

  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.6338426447540134e8, -4.961956343464769e6]) < 1.e-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")

  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)

  fld =  fieldfromintegpoints(femm, geom, u, dT, :Pressure, 1)
  File  =   "LE11NAFEMS_Q8_pressure.vtk"
  vtkexportmesh(File, fens, fes; scalars = [("pressure", fld.values)],
  vectors = [("u", u.values)])
  # println("range of  pressure = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-1.1881819144904878e7, 7.555030948761216e7]) < 1.e-1
  # @async run(`"paraview.exe" $File`)
  try rm(File); catch end

  fld =  fieldfromintegpoints(femm, geom, u, dT, :vm, 1)
  File  =   "LE11NAFEMS_Q8_vm.vtk"
  vtkexportmesh(File, fens, fes; scalars = [("pressure", fld.values)],
  vectors = [("u", u.values)])
  # println("range of von Mises = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [3.221370699152578e7, 1.4437590830351183e8]) < 1.e-1
  # @async run(`"paraview.exe" $File`)
  try rm(File); catch end

  AE = AbaqusExporter("LE11NAFEMS_Q8_export_stress");
  HEADING(AE, "NAFEMS LE11 benchmark with Q8 elements.");
  COMMENT(AE, "sigmaA = -105 MPa ");
  PART(AE, "part1");
  END_PART(AE);
  ASSEMBLY(AE, "ASSEM1");
  INSTANCE(AE, "INSTNC1", "PART1");
  NODE(AE, fens.xyz);
  ELEMENT(AE, "cax8", "AllElements", 1, connasarray(fes))
  NSET_NSET(AE, "ltop", ltop)
  NSET_NSET(AE, "lbottom", lbottom)
  ORIENTATION(AE, "GlobalOrientation", vec([1. 0 0]), vec([0 1. 0]));
  SOLID_SECTION(AE, "elasticity", "GlobalOrientation", "AllElements");
  SURFACE_SECTION(AE, "TractionElements")
  END_INSTANCE(AE);
  END_ASSEMBLY(AE);
  MATERIAL(AE, "elasticity")
  ELASTIC(AE, Ea, nua)
  EXPANSION(AE, alphaa)
  STEP_PERTURBATION_STATIC(AE)
  BOUNDARY(AE, "ASSEM1.INSTNC1.ltop", 2)
  BOUNDARY(AE, "ASSEM1.INSTNC1.lbottom", 2)
  TEMPERATURE(AE, "ASSEM1.INSTNC1.", collect(1:count(fens)), vec(dT.values))
  END_STEP(AE)
  close(AE)
  nlines = 0
  open(AE.filename) do f
    s = readlines(f)
    nlines = length(s)
  end
  @test nlines == 963
  try rm(AE.filename); catch end

end
end
using .mLE11Q8aximmm
mLE11Q8aximmm.test()

module mLE11Q8aximorthom
using FinEtools
using FinEtoolsDeforLinear
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 = 2;                        # 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);

  #     File  =   "mesh.vtk"
  # vtkexportmesh(File, fens, fes)

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

  # Apply EBC's
  lbottom = selectnode(fens,box = [-Inf Inf 0 0],inflate = tolerance)
  setebc!(u, lbottom, true, 2, 00.0)
  ltop = selectnode(fens,box = [-Inf Inf 1.79  1.79],inflate = tolerance)
  setebc!(u, ltop, true, 2, 00.0)
  applyebc!(u)
  numberdofs!(u)

  # Temperature field
  dT  = NodalField(reshape(fens.xyz[:,1]+fens.xyz[:,2],size(fens.xyz,1),1));

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

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

  K  = stiffness(femm, geom, u)
  F  =  thermalstrainloads(femm, geom, u, dT)
  #K = cholesky(K)
  U =   K\F
  scattersysvec!(u, U[:])

  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.6338426447540134e8, -4.961956343464769e6]) < 1.e-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")

  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)

  fld =  fieldfromintegpoints(femm, geom, u, dT, :Pressure, 1)
  File  =   "LE11NAFEMS_Q8_pressure.vtk"
  vtkexportmesh(File, fens, fes; scalars = [("pressure", fld.values)],
  vectors = [("u", u.values)])
  # println("range of  pressure = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-1.1881819144904878e7, 7.555030948761216e7]) < 1.e-1
  # @async run(`"paraview.exe" $File`)
  try rm(File); catch end

  fld =  fieldfromintegpoints(femm, geom, u, dT, :vm, 1)
  File  =   "LE11NAFEMS_Q8_vm.vtk"
  vtkexportmesh(File, fens, fes; scalars = [("pressure", fld.values)],
  vectors = [("u", u.values)])
  # println("range of von Mises = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [3.221370699152578e7, 1.4437590830351183e8]) < 1.e-1
  # @async run(`"paraview.exe" $File`)
  try rm(File); catch end

  AE = AbaqusExporter("LE11NAFEMS_Q8_export_stress");
  HEADING(AE, "NAFEMS LE11 benchmark with Q8 elements.");
  COMMENT(AE, "sigmaA = -105 MPa ");
  PART(AE, "part1");
  END_PART(AE);
  ASSEMBLY(AE, "ASSEM1");
  INSTANCE(AE, "INSTNC1", "PART1");
  NODE(AE, fens.xyz);
  ELEMENT(AE, "cax8", "AllElements", 1, connasarray(fes))
  NSET_NSET(AE, "ltop", ltop)
  NSET_NSET(AE, "lbottom", lbottom)
  ORIENTATION(AE, "GlobalOrientation", vec([1. 0 0]), vec([0 1. 0]));
  SOLID_SECTION(AE, "elasticity", "GlobalOrientation", "AllElements");
  SURFACE_SECTION(AE, "TractionElements")
  END_INSTANCE(AE);
  END_ASSEMBLY(AE);
  MATERIAL(AE, "elasticity")
  ELASTIC(AE, Ea, nua)
  EXPANSION(AE, alphaa)
  STEP_PERTURBATION_STATIC(AE)
  BOUNDARY(AE, "ASSEM1.INSTNC1.ltop", 2)
  BOUNDARY(AE, "ASSEM1.INSTNC1.lbottom", 2)
  TEMPERATURE(AE, "ASSEM1.INSTNC1.", collect(1:count(fens)), vec(dT.values))
  END_STEP(AE)
  close(AE)
  nlines = 0
  open(AE.filename) do f
    s = readlines(f)
    nlines = length(s)
  end
  @test nlines == 963
  try rm(AE.filename); catch end

end
end
using .mLE11Q8aximorthom
mLE11Q8aximorthom.test()

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

  E = 1.0;
  nu = 1.0/3;
  width = 48.0; height = 44.0; thickness  = 1.0;
  free_height  = 16.0;
  Mid_edge  = [48.0, 52.0];# Location of tracked  deflection
  magn = 1.0/(free_height*thickness);# Density of applied load
  convutip = 23.97;
  n = 30;# number of elements per side
  tolerance = minimum([width, height])/n/1000.;#Geometrical tolerance

  fens,fes = T3block(width, height, n, n)

  # Reshape into a trapezoidal panel
  for i = 1:count(fens)
      fens.xyz[i,2] = fens.xyz[i,2]+(fens.xyz[i,1]/width)*(height -fens.xyz[i,2]/height*(height-free_height));
  end

  # Clamped edge of the membrane
  l1 = selectnode(fens; box=[0.,0.,-Inf, Inf], inflate = tolerance)
  ess1 = FDataDict("displacement"=>  0.0, "component"=> 1, "node_list"=>l1)
  ess2 = FDataDict("displacement"=>  0.0, "component"=> 2, "node_list"=>l1)

  # Traction on the opposite edge
  boundaryfes =  meshboundary(fes);
  Toplist  = selectelem(fens, boundaryfes, box= [width, width, -Inf, Inf ], inflate=  tolerance);
  el1femm = FEMMBase(IntegDomain(subset(boundaryfes, Toplist), GaussRule(1, 2), thickness))
  flux1 = FDataDict("traction_vector"=>[0.0,+magn],
      "femm"=>el1femm
      )

  # Make the region
  MR = DeforModelRed2DStrain
  material = MatDeforElastIso(MR,  0.0, E, nu, 0.0)
  region1 = FDataDict("femm"=>FEMMDeforLinear(MR,
      IntegDomain(fes, TriRule(1), thickness), material))

  modeldata = FDataDict("fens"=>fens,
   "regions"=>[region1],
   "essential_bcs"=>[ess1, ess2],
   "traction_bcs"=>[flux1]
   )

  # Call the solver
  modeldata = AlgoDeforLinearModule.linearstatics(modeldata)

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

  # Extract the solution
  nl = selectnode(fens, box=[Mid_edge[1],Mid_edge[1],Mid_edge[2],Mid_edge[2]],
            inflate=tolerance);
  theutip = u.values[nl,:]
  # println("displacement =$(theutip[2]) as compared to converged $convutip")
  @test abs(theutip[2]-21.35955642390279) < 1.0e-3

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew",
     "quantity"=>:Cauchy, "component"=>:xy)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of Cauchy_xy = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.07028875155067116, 0.1301698279821655]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-vm",
     "quantity"=>:vm, "component"=>1)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of vm = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [0.007503804468283987, 0.33798754356331173]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-pressure",
     "quantity"=>:pressure, "component"=>1)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of pressure = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.11777749217431245, 0.23457099031101358]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-princ1",
     "quantity"=>:princCauchy, "component"=>1)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of princCauchy Max = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.16098150217425994, 0.24838761904231466]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-princ3",
     "quantity"=>:princCauchy, "component"=>3)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of princCauchy Min = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.4523049370669106, 0.02980811337406548]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  AE = AbaqusExporter("Cookstress_algo_stress");
  HEADING(AE, "Cook trapezoidal panel, plane stress");
  COMMENT(AE, "Converged free mid-edge displacement = 23.97");
  PART(AE, "part1");
  END_PART(AE);
  ASSEMBLY(AE, "ASSEM1");
  INSTANCE(AE, "INSTNC1", "PART1");
  NODE(AE, fens.xyz);
  COMMENT(AE, "We are assuming three node triangles in plane-stress");
  COMMENT(AE, "CPE3 are pretty poor-accuracy elements, but here we don't care about it.");
  @test nodesperelem(modeldata["regions"][1]["femm"].integdomain.fes) == 3
  ELEMENT(AE, "CPE3", "AllElements", connasarray(modeldata["regions"][1]["femm"].integdomain.fes))
  NSET_NSET(AE, "clamped", modeldata["essential_bcs"][1]["node_list"])
  ORIENTATION(AE, "GlobalOrientation", vec([1. 0 0]), vec([0 1. 0]));
  SOLID_SECTION(AE, "elasticity", "GlobalOrientation", "AllElements", thickness);
  END_INSTANCE(AE);
  END_ASSEMBLY(AE);
  MATERIAL(AE, "elasticity")
  ELASTIC(AE, E, nu)
  STEP_PERTURBATION_STATIC(AE)
  BOUNDARY(AE, "ASSEM1.INSTNC1.clamped", 1)
  BOUNDARY(AE, "ASSEM1.INSTNC1.clamped", 2)
  bfes = modeldata["traction_bcs"][1]["femm"].integdomain.fes
  COMMENT(AE, "Concentrated loads: we are assuming that the elements on the boundary");
  COMMENT(AE, "have two nodes each and also that they are the same length.");
  COMMENT(AE, "Then the concentrated loads below will be correctly lumped.");
  nl = connectednodes(bfes)
  F = zeros(count(modeldata["fens"]))
  for ix = 1:count(bfes)
    for jx = 1:2
      F[bfes.conn[ix][jx]] += 1.0/n/2/thickness
    end
  end
  for ixxxx = 1:length(F)
    if F[ixxxx] != 0.0
      CLOAD(AE, "ASSEM1.INSTNC1.$(ixxxx)", 2, F[ixxxx])
    end
  end
  END_STEP(AE)
  close(AE)

  nlines = 0
  open(AE.filename) do f
    s = readlines(f)
    nlines = length(s)
  end
  @test nlines == 2886
  rm(AE.filename)
end
end
using .mmmCookmmstrainmmisommm
mmmCookmmstrainmmisommm.test()

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

  E = 1.0;
  nu = 1.0/3;
  width = 48.0; height = 44.0; thickness  = 1.0;
  free_height  = 16.0;
  Mid_edge  = [48.0, 52.0];# Location of tracked  deflection
  magn = 1.0/(free_height*thickness);# Density of applied load
  convutip = 23.97;
  n = 30;# number of elements per side
  tolerance = minimum([width, height])/n/1000.;#Geometrical tolerance

  fens,fes = T3block(width, height, n, n)

  # Reshape into a trapezoidal panel
  for i = 1:count(fens)
      fens.xyz[i,2] = fens.xyz[i,2]+(fens.xyz[i,1]/width)*(height -fens.xyz[i,2]/height*(height-free_height));
  end

  # Clamped edge of the membrane
  l1 = selectnode(fens; box=[0.,0.,-Inf, Inf], inflate = tolerance)
  ess1 = FDataDict("displacement"=>  0.0, "component"=> 1, "node_list"=>l1)
  ess2 = FDataDict("displacement"=>  0.0, "component"=> 2, "node_list"=>l1)

  # Traction on the opposite edge
  boundaryfes =  meshboundary(fes);
  Toplist  = selectelem(fens, boundaryfes, box= [width, width, -Inf, Inf ], inflate=  tolerance);
  el1femm = FEMMBase(IntegDomain(subset(boundaryfes, Toplist), GaussRule(1, 2), thickness))
  flux1 = FDataDict("traction_vector"=>[0.0,+magn],
      "femm"=>el1femm
      )

  # Make the region
  MR = DeforModelRed2DStrain
  material = MatDeforElastOrtho(MR,  0.0, E, nu, 0.0)
  region1 = FDataDict("femm"=>FEMMDeforLinear(MR,
      IntegDomain(fes, TriRule(1), thickness), material))

  modeldata = FDataDict("fens"=>fens,
   "regions"=>[region1],
   "essential_bcs"=>[ess1, ess2],
   "traction_bcs"=>[flux1]
   )

  # Call the solver
  modeldata = AlgoDeforLinearModule.linearstatics(modeldata)

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

  # Extract the solution
  nl = selectnode(fens, box=[Mid_edge[1],Mid_edge[1],Mid_edge[2],Mid_edge[2]],
            inflate=tolerance);
  theutip = u.values[nl,:]
  # println("displacement =$(theutip[2]) as compared to converged $convutip")
  @test abs(theutip[2]-21.35955642390279) < 1.0e-3

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew",
     "quantity"=>:Cauchy, "component"=>:xy)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of Cauchy_xy = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.07028875155067116, 0.1301698279821655]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-vm",
     "quantity"=>:vm, "component"=>1)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of vm = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [0.007503804468283987, 0.33798754356331173]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-pressure",
     "quantity"=>:pressure, "component"=>1)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of pressure = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.11777749217431245, 0.23457099031101358]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-princ1",
     "quantity"=>:princCauchy, "component"=>1)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of princCauchy Max = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.16098150217425994, 0.24838761904231466]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  modeldata["postprocessing"] = FDataDict("file"=>"cookstress-ew-princ3",
     "quantity"=>:princCauchy, "component"=>3)
  modeldata = AlgoDeforLinearModule.exportstresselementwise(modeldata)
  fld = modeldata["postprocessing"]["exported"][1]["field"]
  # println("range of princCauchy Min = $((minimum(fld.values), maximum(fld.values)))")
  @test norm([minimum(fld.values), maximum(fld.values)] - [-0.4523049370669106, 0.02980811337406548]) < 1.0e-5
  # File = modeldata["postprocessing"]["exported"][1]["file"]
  # @async run(`"paraview.exe" $File`)
  try rm(modeldata["postprocessing"]["exported"][1]["file"]); catch end

  AE = AbaqusExporter("Cookstress_algo_stress");
  HEADING(AE, "Cook trapezoidal panel, plane stress");
  COMMENT(AE, "Converged free mid-edge displacement = 23.97");
  PART(AE, "part1");
  END_PART(AE);
  ASSEMBLY(AE, "ASSEM1");
  INSTANCE(AE, "INSTNC1", "PART1");
  NODE(AE, fens.xyz);
  COMMENT(AE, "We are assuming three node triangles in plane-stress");
  COMMENT(AE, "CPE3 are pretty poor-accuracy elements, but here we don't care about it.");
@test  nodesperelem(modeldata["regions"][1]["femm"].integdomain.fes) == 3
  ELEMENT(AE, "CPE3", "AllElements", connasarray(modeldata["regions"][1]["femm"].integdomain.fes))
  NSET_NSET(AE, "clamped", modeldata["essential_bcs"][1]["node_list"])
  ORIENTATION(AE, "GlobalOrientation", vec([1. 0 0]), vec([0 1. 0]));
  SOLID_SECTION(AE, "elasticity", "GlobalOrientation", "AllElements", thickness);
  END_INSTANCE(AE);
  END_ASSEMBLY(AE);
  MATERIAL(AE, "elasticity")
  ELASTIC(AE, E, nu)
  STEP_PERTURBATION_STATIC(AE)
  BOUNDARY(AE, "ASSEM1.INSTNC1.clamped", 1)
  BOUNDARY(AE, "ASSEM1.INSTNC1.clamped", 2)
  bfes = modeldata["traction_bcs"][1]["femm"].integdomain.fes
  COMMENT(AE, "Concentrated loads: we are assuming that the elements on the boundary");
  COMMENT(AE, "have two nodes each and also that they are the same length.");
  COMMENT(AE, "Then the concentrated loads below will be correctly lumped.");
  nl = connectednodes(bfes)
  F = zeros(count(modeldata["fens"]))
  for ix = 1:count(bfes)
    for jx = 1:2
      F[bfes.conn[ix][jx]] += 1.0/n/2/thickness
    end
  end
  for ixxxx = 1:length(F)
    if F[ixxxx] != 0.0
      CLOAD(AE, "ASSEM1.INSTNC1.$(ixxxx)", 2, F[ixxxx])
    end
  end
  END_STEP(AE)
  close(AE)

  nlines = 0
  open(AE.filename) do f
    s = readlines(f)
    nlines = length(s)
  end
  @test nlines == 2886
  rm(AE.filename)
end
end
using .mmmCookmmstrainmorthommm
mmmCookmmstrainmorthommm.test()


# # Navier--Stokes equations
#
# In this example, we numerically solve the incompressible Navier--Stokes
# equations
#
# ```math
# ∂_t \bm{v} + (\bm{v} ⋅ \bm{∇}) \bm{v} = -\frac{1}{ρ} \bm{∇} p + ν ∇^2 \bm{v},
# \quad \bm{∇} ⋅ \bm{v} = 0,
# ```
#
# where ``\bm{v}(\bm{x}, t)`` and ``p(\bm{x}, t)`` are respectively the velocity
# and pressure fields, ``ν`` is the fluid kinematic viscosity and ``ρ`` is the
# fluid density.
#
# We solve the above equations a 3D periodic domain using a standard Fourier
# pseudo-spectral method.

# ## First steps
#
# We start by loading the required packages, initialising MPI and setting the
# simulation parameters.

using MPI
using PencilFFTs

MPI.Init()
comm = MPI.COMM_WORLD
procid = MPI.Comm_rank(comm) + 1

## Simulation parameters
Ns = (64, 64, 64)  # = (Nx, Ny, Nz)
Ls = (2π, 2π, 2π)  # = (Lx, Ly, Lz)

## Collocation points ("global" = over all processes).
## We include the endpoint (length = N + 1) for convenience.
xs_global = map((N, L) -> range(0, L; length = N + 1), Ns, Ls)  # = (x, y, z)

# Let's check the number of MPI processes over which we're running our
# simulation:
MPI.Comm_size(comm)

# We can now create a partitioning of the domain based on the number of grid
# points (`Ns`) and on the number of MPI processes.
# There are different ways to do this.
# For simplicity, here we do it automatically following the [PencilArrays.jl
# docs](https://jipolanco.github.io/PencilArrays.jl/stable/Pencils/#pencil-high-level):

pen = Pencil(Ns, comm)

# The subdomain associated to the local MPI process can be obtained using
# [`range_local`](https://jipolanco.github.io/PencilArrays.jl/dev/Pencils/#PencilArrays.Pencils.range_local-Tuple{Pencil,%20LogicalOrder}):

range_local(pen)

# We now construct a distributed vector field that follows the decomposition
# configuration we just created:

v⃗₀ = (
    PencilArray{Float64}(undef, pen),  # vx
    PencilArray{Float64}(undef, pen),  # vy
    PencilArray{Float64}(undef, pen),  # vz
)
summary(v⃗₀[1])

# We still need to fill this array with interesting values that represent a
# physical velocity field.

# ## Initial condition

# Let's set the initial condition in physical space.
# In this example, we choose the [Taylor--Green
# vortex](https://en.wikipedia.org/wiki/Taylor%E2%80%93Green_vortex)
# configuration as an initial condition:
#
# ```math
# \begin{aligned}
# v_x(x, y, z) &=  u₀ \sin(k₀ x) \cos(k₀ y) \cos(k₀ z) \\
# v_y(x, y, z) &= -u₀ \cos(k₀ x) \sin(k₀ y) \cos(k₀ z) \\
# v_z(x, y, z) &= 0
# \end{aligned}
# ```
#
# where ``u₀`` and ``k₀`` are two parameters setting the amplitude and the
# period of the velocity field.
#
# To set the initial condition, each MPI process needs to know which portion of
# the physical grid it has been attributed.
# For this, PencilArrays.jl includes a
# [`localgrid`](https://jipolanco.github.io/PencilArrays.jl/dev/LocalGrids/#PencilArrays.LocalGrids.localgrid)
# helper function:

grid = localgrid(pen, xs_global)

# We can use this to initialise the velocity field:

u₀ = 1.0
k₀ = 2π / Ls[1]  # should be integer if L = 2π (to preserve periodicity)

@. v⃗₀[1] =  u₀ * sin(k₀ * grid.x) * cos(k₀ * grid.y) * cos(k₀ * grid.z)
@. v⃗₀[2] = -u₀ * cos(k₀ * grid.x) * sin(k₀ * grid.y) * cos(k₀ * grid.z)
@. v⃗₀[3] =  0
nothing # hide

# Let's plot a 2D slice of the velocity field managed by the local MPI process:

using GLMakie

## Compute the norm of a vector field represented by a tuple of arrays.
function vecnorm(v⃗::NTuple)
    vnorm = similar(v⃗[1])
    for n ∈ eachindex(v⃗[1])
        w = zero(eltype(vnorm))
        for v ∈ v⃗
            w += v[n]^2
        end
        vnorm[n] = sqrt(w)
    end
    vnorm
end

let fig = Figure(resolution = (700, 600))
    ax = Axis3(fig[1, 1]; aspect = :data, xlabel = "x", ylabel = "y", zlabel = "z")
    vnorm = vecnorm(v⃗₀)
    ct = contour!(
        ax, grid.x, grid.y, grid.z, vnorm;
        alpha = 0.2, levels = 4,
        colormap = :viridis, colorrange = (0.0, 1.0),
    )
    cb = Colorbar(fig[1, 2], ct; label = "Velocity magnitude")
    fig
end

# ## Velocity in Fourier space
#
# In the Fourier pseudo-spectral method, the periodic velocity field is
# discretised in space as a truncated Fourier series
#
# ```math
# \bm{v}(\bm{x}, t) =
# ∑_{\bm{k}} \hat{\bm{v}}_{\bm{k}}(t) \, e^{i \bm{k} ⋅ \bm{x}},
# ```
#
# where ``\bm{k} = (k_x, k_y, k_z)`` are the discrete wave numbers.
#
# The wave numbers can be obtained using the
# [`fftfreq`](https://juliamath.github.io/AbstractFFTs.jl/dev/api/#AbstractFFTs.fftfreq)
# function.
# Since we perform a real-to-complex transform along the first dimension, we use
# [`rfftfreq`](https://juliamath.github.io/AbstractFFTs.jl/dev/api/#AbstractFFTs.rfftfreq) instead for ``k_x``:

using AbstractFFTs: fftfreq, rfftfreq

ks_global = (
    rfftfreq(Ns[1], 2π * Ns[1] / Ls[1]),  # kx | real-to-complex
     fftfreq(Ns[2], 2π * Ns[2] / Ls[2]),  # ky | complex-to-complex
     fftfreq(Ns[3], 2π * Ns[3] / Ls[3]),  # kz | complex-to-complex
)

ks_global[1]'
#
ks_global[2]'
#
ks_global[3]'

# To transform the velocity field to Fourier space, we first create a
# real-to-complex FFT plan to be applied to one of the velocity components:

plan = PencilFFTPlan(v⃗₀[1], Transforms.RFFT())

# See [`PencilFFTPlan`](@ref) for details on creating plans and on optional
# keyword arguments.
#
# We can now apply this plan to the three velocity components to obtain the
# respective Fourier coefficients ``\hat{\bm{v}}_{\bm{k}}``:

v̂s = plan .* v⃗₀
summary(v̂s[1])

# Note that, in Fourier space, the domain decomposition is performed along the
# directions ``x`` and ``y``:

pencil(v̂s[1])

# This is because the 3D FFTs are performed one dimension at a time, with the
# ``x`` direction first and the ``z`` direction last.
# To efficiently perform an FFT along a given direction (taking advantage of
# serial FFT implementations like FFTW), all the data along that direction must
# be contained locally within a single MPI process.
# For that reason, data redistributions (or *transpositions*) among MPI
# processes are performed behind the scenes during each FFT computation.
# Such transpositions require important communications between MPI processes,
# and are usually the most time-consuming aspect of massively-parallel
# simulations using this kind of methods.
#
# To solve the Navier--Stokes equations in Fourier space, we will
# also need the respective wave numbers ``\bm{k}`` associated to the local MPI
# process.
# Similarly to the local grid points, these are obtained using the `localgrid`
# function:

grid_fourier = localgrid(v̂s[1], ks_global)

# As an example, let's first use this to compute and plot the vorticity
# associated to the initial condition.
# The vorticity is defined as the curl of the velocity,
# ``\bm{ω} = \bm{∇} × \bm{v}``.
# In Fourier space, this becomes ``\hat{\bm{ω}} = i \bm{k} × \hat{\bm{v}}``.

using StaticArrays: SVector
using LinearAlgebra: ×

function curl_fourier!(
        ω̂s::NTuple{N, <:PencilArray}, v̂s::NTuple{N, <:PencilArray}, grid_fourier,
    ) where {N}
    @inbounds for I ∈ eachindex(grid_fourier)
        ## We use StaticArrays for the cross product between small vectors.
        ik⃗ = im * SVector(grid_fourier[I])
        v⃗ = SVector(getindex.(v̂s, Ref(I)))  # = (v̂s[1][I], v̂s[2][I], ...)
        ω⃗ = ik⃗ × v⃗
        for n ∈ eachindex(ω⃗)
            ω̂s[n][I] = ω⃗[n]
        end
    end
    ω̂s
end

ω̂s = similar.(v̂s)
curl_fourier!(ω̂s, v̂s, grid_fourier);

# We finally transform back to physical space and plot the result:

ωs = plan .\ ω̂s

let fig = Figure(resolution = (700, 600))
    ax = Axis3(fig[1, 1]; aspect = :data, xlabel = "x", ylabel = "y", zlabel = "z")
    ω_norm = vecnorm(ωs)
    ct = contour!(
        ax, grid.x, grid.y, grid.z, ω_norm;
        alpha = 0.1, levels = 0.8:0.2:2.0,
        colormap = :viridis, colorrange = (0.8, 2.0),
    )
    cb = Colorbar(fig[1, 2], ct; label = "Vorticity magnitude")
    fig
end

# ## Computing the non-linear term
#
# One can show that, in Fourier space, the incompressible Navier--Stokes
# equations can be written as
#
# ```math
# ∂_t \hat{\bm{v}}_{\bm{k}} =
# - \mathcal{P}_{\bm{k}} \! \left[ \widehat{(\bm{v} ⋅ \bm{∇}) \bm{v}} \right]
# - ν |\bm{k}|^2 \hat{\bm{v}}_{\bm{k}}
# \quad \text{ with } \quad
# \mathcal{P}_{\bm{k}}(\hat{\bm{F}}_{\bm{k}}) = \left( I - \frac{\bm{k} ⊗
# \bm{k}}{|\bm{k}|^2} \right) \hat{\bm{F}}_{\bm{k}},
# ```
#
# where ``\mathcal{P}_{\bm{k}}`` is a projection operator allowing to preserve the
# incompressibility condition ``\bm{∇} ⋅ \bm{v} = 0``.
# This operator encodes the action of the pressure gradient term, which serves
# precisely to enforce incompressibility.
# Note that, because of this, the pressure gradient dissapears from the
# equations.
#
# Now that we have the wave numbers ``\bm{k}``, computing the linear viscous
# term in Fourier space is straighforward once we have the Fourier coefficients
# ``\hat{\bm{v}}_{\bm{k}}`` of the velocity field.
# What is slightly more challenging (and much more costly) is the computation of
# the non-linear term in Fourier space, ``\hat{\bm{F}}_{\bm{k}} =
# \left[ \widehat{(\bm{v} ⋅ \bm{∇}) \bm{v}} \right]_{\bm{k}}``.
# In the pseudo-spectral method, the quadratic nonlinearity is computed
# by collocation in physical space (i.e. this term is evaluated at grid points),
# while derivatives are computed in Fourier space.
# This requires transforming fields back and forth between both spaces.
#
# Below we implement a function that computes the non-linear term in Fourier
# space based on its convective form ``(\bm{v} ⋅ \bm{∇}) \bm{v} = \bm{∇} ⋅
# (\bm{v} ⊗ \bm{v})``.
# Note that this equivalence uses the incompressibility condition ``\bm{∇} ⋅ \bm{v} = 0``.

using LinearAlgebra: mul!, ldiv!  # for applying FFT plans in-place

## Compute non-linear term in Fourier space from velocity field in physical
## space. Optional keyword arguments may be passed to avoid allocations.
function ns_nonlinear!(
        F̂s, vs, plan, grid_fourier;
        vbuf = similar(vs[1]), v̂buf = similar(F̂s[1]),
    )
    ## Compute F_i = ∂_j (v_i v_j) for each i.
    ## In Fourier space: F̂_i = im * k_j * FFT(v_i * v_j)
    w, ŵ = vbuf, v̂buf
    @inbounds for (i, F̂i) ∈ enumerate(F̂s)
        F̂i .= 0
        vi = vs[i]
        for (j, vj) ∈ enumerate(vs)
            w .= vi .* vj     # w = v_i * v_j in physical space
            mul!(ŵ, plan, w)  # same in Fourier space
            ## Add derivative in Fourier space
            for I ∈ eachindex(grid_fourier)
                k⃗ = grid_fourier[I]  # = (kx, ky, kz)
                kj = k⃗[j]
                F̂i[I] += im * kj * ŵ[I]
            end
        end
    end
    F̂s
end

# As an example, let's use this function on our initial velocity field:

F̂s = similar.(v̂s)
ns_nonlinear!(F̂s, v⃗₀, plan, grid_fourier);

# Strictly speaking, computing the non-linear term by collocation can lead to
# [aliasing
# errors](https://en.wikipedia.org/wiki/Aliasing#Sampling_sinusoidal_functions),
# as the quadratic term excites Fourier modes that fall beyond the range of
# resolved wave numbers.
# The typical solution is to apply Orzsag's 2/3 rule to zero-out the Fourier
# coefficients associated to the highest wave numbers.
# We define a function that applies this procedure below.

function dealias_twothirds!(ŵs::Tuple, grid_fourier, ks_global)
    ks_max = maximum.(abs, ks_global)  # maximum stored wave numbers (kx_max, ky_max, kz_max)
    ks_lim = (2 / 3) .* ks_max
    @inbounds for I ∈ eachindex(grid_fourier)
        k⃗ = grid_fourier[I]
        if any(abs.(k⃗) .> ks_lim)
            for ŵ ∈ ŵs
                ŵ[I] = 0
            end
        end
    end
    ŵs
end

## We can apply this on the previously computed non-linear term:
dealias_twothirds!(F̂s, grid_fourier, ks_global);

# Finally, we implement the projection associated to the incompressibility
# condition:

function project_divergence_free!(ûs, grid_fourier)
    @inbounds for I ∈ eachindex(grid_fourier)
        k⃗ = grid_fourier[I]
        k² = sum(abs2, k⃗)
        iszero(k²) && continue  # avoid division by zero
        û = getindex.(ûs, Ref(I))  # (ûs[1][I], ûs[2][I], ...)
        for i ∈ eachindex(û)
            ŵ = û[i]
            for j ∈ eachindex(û)
                ŵ -= k⃗[i] * k⃗[j] * û[j] / k²
            end
            ûs[i][I] = ŵ
        end
    end
    ûs
end

# We can verify the correctness of the projection operator by checking that the
# initial velocity field is not modified by it, since it is already
# incompressible:

v̂s_proj = project_divergence_free!(copy.(v̂s), grid_fourier)
v̂s_proj .≈ v̂s  # the last one may be false because v_z = 0 initially

# ## Putting it all together
#
# To perform the time integration of the Navier--Stokes equations, we will use
# the timestepping routines implemented in the DifferentialEquations.jl suite.
# For simplicity, we use here an explicit Runge--Kutta scheme.
# In this case, we just need to write a function that computes the right-hand
# side of the Navier--Stokes equations in Fourier space:

function ns_rhs!(
        dvs::NTuple{N, <:PencilArray}, vs::NTuple{N, <:PencilArray}, p, t,
    ) where {N}
    ## 1. Compute non-linear term and dealias it
    (; plan, cache, ks_global, grid_fourier) = p
    F̂s = cache.F̂s
    ns_nonlinear!(F̂s, vs, plan, grid_fourier; vbuf = dvs[1], v̂buf = cache.v̂s[1])
    dealias_twothirds!(F̂s, grid_fourier, ks_global)

    ## 2. Project onto divergence-free space
    project_divergence_free!(F̂s, grid_fourier)

    ## 3. Transform velocity to Fourier space
    v̂s = cache.v̂s
    map((v, v̂) -> mul!(v̂, plan, v), vs, v̂s)

    ## 4. Add viscous term (and multiply projected non-linear term by -1)
    ν = p.ν
    for n ∈ eachindex(v̂s)
        v̂ = v̂s[n]
        F̂ = F̂s[n]
        @inbounds for I ∈ eachindex(grid_fourier)
            k⃗ = grid_fourier[I]  # = (kx, ky, kz)
            k² = sum(abs2, k⃗)
            F̂[I] = -F̂[I] - ν * k² * v̂[I]
        end
    end

    ## 5. Transform RHS back to physical space
    map((dv, dv̂) -> ldiv!(dv, plan, dv̂), dvs, F̂s)

    nothing
end

# For the time-stepping, we load OrdinaryDiffEq.jl from the
# DifferentialEquations.jl suite and set-up the simulation.
# Since DifferentialEquations.jl can't directly deal with tuples of arrays, we
# convert the input data to the
# [`ArrayPartition`](https://github.com/SciML/RecursiveArrayTools.jl#arraypartition)
# type and write an interface function to make things work with our functions
# defined above.

using OrdinaryDiffEq
using RecursiveArrayTools: ArrayPartition

ns_rhs!(dv::ArrayPartition, v::ArrayPartition, args...) = ns_rhs!(dv.x, v.x, args...)
vs_init_ode = ArrayPartition(v⃗₀)
summary(vs_init_ode)

# We now define solver parameters and temporary variables, and initialise the
# problem:

params = (;
    ν = 5e-3,  # kinematic viscosity
    plan, grid_fourier, ks_global,
    cache = (
        v̂s = similar.(v̂s),
        F̂s = similar.(v̂s),
    )
)

tspan = (0.0, 10.0)
prob = ODEProblem(ns_rhs!, vs_init_ode, tspan, params)
integrator = init(prob, RK4(); dt = 1e-3, save_everystep = false);

# We finally solve the problem over time and plot the vorticity associated to
# the solution.
# It is also useful to look at the energy spectrum ``E(k)``, to see if the small
# scales are correctly resolved.
# To obtain a turbulent flow, the viscosity ``ν`` must be small enough to allow
# the transient appearance of an energy cascade towards the small scales (i.e.
# from small to large ``k``), while high enough to allow the small-scale motions
# to be correctly resolved.

function energy_spectrum!(Ek, ks, v̂s, grid_fourier)
    Nk = length(Ek)
    @assert Nk == length(ks)
    Ek .= 0
    for I ∈ eachindex(grid_fourier)
        k⃗ = grid_fourier[I]  # = (kx, ky, kz)
        knorm = sqrt(sum(abs2, k⃗))
        i = searchsortedfirst(ks, knorm)
        i > Nk && continue
        v⃗ = getindex.(v̂s, Ref(I))  # = (v̂s[1][I], v̂s[2][I], ...)
        factor = k⃗[1] == 0 ? 1 : 2  # account for Hermitian symmetry and r2c transform
        Ek[i] += factor * sum(abs2, v⃗) / 2
    end
    MPI.Allreduce!(Ek, +, get_comm(v̂s[1]))  # sum across all processes
    Ek
end

ks = rfftfreq(Ns[1], 2π * Ns[1] / Ls[1])
Ek = similar(ks)
v̂s = plan .* integrator.u.x
energy_spectrum!(Ek, ks, v̂s, grid_fourier)
Ek ./= scale_factor(plan)^2  # rescale energy

curl_fourier!(ω̂s, v̂s, grid_fourier)
ldiv!.(ωs, plan, ω̂s)
ω⃗_plot = Observable(ωs)
k_plot = @view ks[2:end]
E_plot = Observable(@view Ek[2:end])
t_plot = Observable(integrator.t)

fig = let
    fig = Figure(resolution = (1200, 600))
    ax = Axis3(
        fig[1, 1][1, 1]; title = @lift("t = $(round($t_plot, digits = 3))"),
        aspect = :data, xlabel = "x", ylabel = "y", zlabel = "z",
    )
    ω_mag = @lift vecnorm($ω⃗_plot)
    ω_mag_norm = @lift $ω_mag ./ maximum($ω_mag)
    ct = contour!(
        ax, grid.x, grid.y, grid.z, ω_mag_norm;
        alpha = 0.3, levels = 3,
        colormap = :viridis, colorrange = (0.0, 1.0),
    )
    cb = Colorbar(fig[1, 1][1, 2], ct; label = "Normalised vorticity magnitude")
    ax_sp = Axis(
        fig[1, 2];
        xlabel = "k", ylabel = "E(k)", xscale = log2, yscale = log10,
        title = "Kinetic energy spectrum",
    )
    ylims!(ax_sp, 1e-8, 1e0)
    scatterlines!(ax_sp, k_plot, E_plot)
    ks_slope = exp.(range(log(2.5), log(25.0), length = 3))
    E_fivethirds = @. 0.3 * ks_slope^(-5/3)
    @views lines!(ax_sp, ks_slope, E_fivethirds; color = :black, linestyle = :dot)
    text!(ax_sp, L"k^{-5/3}"; position = (ks_slope[2], E_fivethirds[2]), align = (:left, :bottom))
    fig
end

using Printf # hide
with_xvfb = ENV["DISPLAY"] == ":99" # hide
nstep = 0  # hide
const tmpdir = mktempdir()  # hide
filename_frame(procid, nstep) = joinpath(tmpdir, @sprintf("proc%d_%04d.png", procid, nstep)) # hide
record(fig, "vorticity_proc$procid.mp4"; framerate = 10) do io
    with_xvfb && recordframe!(io) # hide
    while integrator.t < 20
        dt = 0.001
        step!(integrator, dt)
        t_plot[] = integrator.t
        mul!.(v̂s, plan, integrator.u.x)  # current velocity in Fourier space
        curl_fourier!(ω̂s, v̂s, grid_fourier)
        ldiv!.(ω⃗_plot[], plan, ω̂s)
        ω⃗_plot[] = ω⃗_plot[]  # to force updating the plot
        energy_spectrum!(Ek, ks, v̂s, grid_fourier)
        Ek ./= scale_factor(plan)^2  # rescale energy
        E_plot[] = E_plot[]
        global nstep += 1  # hide
        with_xvfb ?  # hide
        save(filename_frame(procid, nstep), fig) :  # hide
        recordframe!(io)
    end
end;

if with_xvfb  # hide
    run(pipeline(`ffmpeg -y -r 10 -i $tmpdir/proc$(procid)_%04d.png -c:v libx264 -vf "fps=25,format=yuv420p" vorticity_proc$procid.mp4`; stdout = "ffmpeg.out", stderr = "ffmpeg.err"))   # hide
end              # hide
nothing          # hide

# ```@raw html
# <figure class="video_container">
#   <video controls="true" allowfullscreen="true">
#     <source src="../vorticity_proc1.mp4" type="video/mp4">
#   </video>
# </figure>
# ```


let n
for n in [2, 4, 16]
    H = matrixdepot("hadamard", Int, n)
    @test H*H' == n * Matrix(1.0I, n, n)
end

@test_throws ArgumentError matrixdepot("hadamard", 0)
end
println("'hadamard' passed test...")


function BFS!(G, s)
   v = 1:size(G.AdjMatrix, 1)
   for u in v
     if u != s
        G.V[u].color = 'W'
		G.V[u].d = typemax(Int)
		G.V[u].pred = -1
     end
   end
   G.V[s].color = 'G'
   G.V[s].d = 0
   G.V[s].pred = -1

   Q = Int[]
   push!(Q, s)
   while ~isempty(Q)
        u = Q[1]
	deleteat!(Q, 1)
        for v in findall(G.AdjMatrix[u,:] .==1)
	   if G.V[v].color=='W'
	      G.V[v].color = 'G'
	      G.V[v].d = G.V[u].d + 1
	      G.V[v].pred = u
	      push!(Q, v)
	   end
	end
	G.V[u].color = 'B'
   end
end

function getPath(G, idStart, idEnd)
	path = Int[]
	temp = idEnd;
	while (temp != idStart)
		path = [temp; path]
		temp = G.V[temp].pred;
	end
	return [idStart; path]
end


export @ring, deg, monoms, exponent, matrixof, prodvec, prodset

import DynamicPolynomials: maxdegree, monomials
import LinearAlgebra: norm

function buildpolvar(::Type{PV}, arg, var) where PV
    :($(esc(arg)) = $var)
end

"""
```
@ring args...
```
Defines the arguments as variables and output their array.

Example
-------
```
X = @ring x1 x2
```
"""
macro ring(args...)
    X = DynamicPolynomials.PolyVar{true}[DynamicPolynomials.PolyVar{true}(string(arg)) for arg in args]
    V = [buildpolvar(PolyVar{true}, args[i], X[i]) for i in 1:length(X)]
    push!(V, :(TMP = $X) )
    Base.reduce((x,y) -> :($x; $y), V; init = :() )
end

#----------------------------------------------------------------------
"""
```
deg(p:Polynomial) -> Int64
```
Degree of a polynomial
"""
function deg(p::Polynomial{B,T}) where {B,T}
    maxdegree(p.x)
end

#----------------------------------------------------------------------
function deg(t::Term{B,T})  where {B,T}
    deg(t.x)
end
#----------------------------------------------------------------------
function deg(m::Monomial{C}) where C
    sum(m.z)
end
#----------------------------------------------------------------------
function deg(v::PolyVar{T}) where T
    1
end
#----------------------------------------------------------------------
function MultivariatePolynomials.variables(m::Monomial{C}) where C
    m.vars
end
#----------------------------------------------------------------------
function coeff(t::Term{B,T}) where {B,T}
    exponent(t.α)
end
#----------------------------------------------------------------------
function Base.one(::Type{Monomial{true}})
    Monomial{true}()
end

#----------------------------------------------------------------------
"""
```
exponent(m::Monomial) -> Array{Int64,1}
```
Get the exponent of a monomial as an array of Int64
"""
function Base.exponent(m::Monomial)
    return m.z
end

function Base.exponent(t::Term{B,T}) where {B,T}
    return t.x.z
end

# function DynamicPolynomials.monomial(m::Monomial)
#     return m
# end

# function DynamicPolynomials.monomial(t::Term{B,T}) where {B,T}
#     return t.x
# end
#----------------------------------------------------------------------
"""
```
 inv(m :: Monomial{true})
```
 return the inverse monomial with opposite exponents.
"""
function Base.inv(m:: Monomial{true})
    Monomial(m.vars,-m.z)
end

function Base.inv(v:: PolyVar{true})
    inv(Monomial(v))
end

function inv!(m:: Monomial{true})
    m.z=-m.z
end
#----------------------------------------------------------------------
function isprimal(m::Monomial{true})
    return !any(t->t<0, m.z)
end
#-----------------------------------------------------------------------
"""
```
monoms(V, d::Int64) -> Vector{Monomial}
monoms(V, rg::UnitRangeInt64) -> Vector{Monomial}
```
List of all monomials in the variables V up to degree d of from degree d1 to d2,
ordered by increasing degree.
"""
function monoms(V::Vector{PolyVar{true}}, rg::UnitRange{Int64})
    L = DynamicPolynomials.Monomial{true}[]
    for i in rg
        append!(L, DynamicPolynomials.monomials(V,i))
    end
    L
end

#-----------------------------------------------------------------------
"""
```
monoms(V, d::Int64) -> Vector{Monomial}
```
List of all monomials in the variables V up to degree d of from degree d1 to d2,
ordered by increasing degree.
"""
function monoms(V::Vector{PolyVar{true}}, d ::Int64)
    if (d>0)
        monoms(V,0:d)
    else
        L = monoms(V, 0:-d)
        for i in 1:length(L)
            inv!(L[i])
        end
        L
    end
end

#-----------------------------------------------------------------------
"""
Evaluate a polynomial p at a point x;

## Example
```
julia> X = @ring x1 x2;

julia> p = x1^2+x1*x2;

julia> p([1.0,0.5])
1.5
```
"""
function (p::Polynomial{B,T})(x::AbstractVector, X=variables(p)) where {B,T}
    return p([X[i]=>x[i] for i in 1:length(x)]...)
    # r = zero(x[1]);
    # for m in p
    #    t=m.α
    #    for i in 1:length(m.x.z)
    #    	 t*=x[i]^m.x.z[i]
    #    end
    #    r+=t
    # end
    # r
end

#----------------------------------------------------------------------
function LinearAlgebra.norm(p::Polynomial{B,T}, x::Float64) where {B,T}
    if (x == Inf)
        r = - Inf
        for t in p
            r = max(r, abs(t.α))
        end
    else
        r = Inf
        for t in p
            r = min(r, abs(t.α))
        end
    end
    r
end

function LinearAlgebra.norm(pol::Polynomial{B,T}, p::Int64=2) where {B,T}
    r=sum(abs(t.α)^p for t in pol)
    exp(log(r)/p)
end

#----------------------------------------------------------------------
"""
 Coefficient matrix of the polynomials in P with respect to the monomial vector L
"""
function matrixof(P::Vector{Polynomial{B,C}}, L ) where {B,C}

    M = fill(zero(C), length(P), length(L))
    idx = Dict{Monomial{true},Int64}()
    for i in 1:length(L)
        idx[L[i]] = i
    end

    for i in 1:length(P)
        for t in P[i]
            j = get(idx,monomial(t),0)
            if j!=0 M[i,j]=coefficient(t) end
        end
    end
    M
end

"""
 Product-wise vector of X by Y ordered by row: [x1*y1, x1*y2, ..., x2*y1, ....]
"""
function prodvec(X, Y)
    res = typeof(X[1])[]
    [x*y for x in X for y in Y]
end

"""
 Product-wise vector of X by Y ordered by row with no repetition:
    if xi*yj appears as xi'*yj' with i'<i or i'=i and j'<j it is not inserted.
"""
function prodset(X, Y)
    res = typeof(X[1])[]
    for x in X
        for y in Y
            m = x*y
            if !in(m,res) push!(res, m) end
        end
    end
    res
end


using BenchmarkTools: @btime

input = open("a1.txt") do file
  [parse(Int32, line) for line in eachline(file)]
end

ascending = @btime sort(input)
numbers = @btime Set(ascending)

function find_2020_complement()
  for (i,n) in enumerate(ascending)
    c = 2020 - n
    if c <= n && ascending[i+1] != c
      break
    end
    if in(c, numbers)
      return (n,c)
    end
  end
end

part1 = @btime find_2020_complement()
show(part1)
show(part1[1]*part1[2])
print("\n")

function find_2020_triple()
  for (i,n) in enumerate(ascending)
    for j in i+1:length(ascending)
      c = 2020 - n - ascending[j]
      if c <= ascending[j] && ascending[j+1] != c
        break
      end
      if in(c, numbers)
        return (n, ascending[j], c)
      end
    end
  end
end

part2 = @btime find_2020_triple()
show(part2)
show(part2[1]*part2[2]*part2[3])

function find_2020_triple_brute()
  for i in 1:length(ascending)
    for j in i+1:length(ascending)
      for k in j+1:length(ascending)
        ascending[i] + ascending[j] + ascending[k] == 2020 && return (ascending[i], ascending[j], ascending[k])
      end
    end
  end
end

@btime find_2020_triple_brute()


# Copyright (c) 2021
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

using LinearAlgebra, Convex, SCS;

"""
    IRLS(A::Matrix{Float64}, b::Vector{Float64}; maxiter=100, epsilon=0.01)

Find the solution to Ax=b using Iterative Recursive Least Squares.

### Input

- `A`       -- Matrix: Ax=b
- `b`       -- Vector: Ax=b
- `maxiter` -- (optional) number of optmization iterations
- `epsilon` -- (optional) threshold to stop optimizing

### Output

Solution to Ax=b (Vector{Float64})

### Example

julia> A = randn(10, 100);
julia> b = randn(10);
julia> x = IRLS(A, b, maxiter=5, epsilon=.01);
julia> println(x)

### Algorithm

This function implements Andrew's monotone chain convex hull algorithm to
Enhancing Sparsity by Reweighted L1 Minimization

    Emmanuel J. Candes, Michael B. Wakin, and Stephen P. Boyd, "Enhancing
        Sparsity by Reweighted L1 Minimization," J Fourier Anal Appl (2008)
        14: 877–905.

"""
function IRWLS(A::Matrix{Float64},
               b::Vector{Float64};
               maxiter=100,
               epsilon=0.01)
    # get the second column dim of A
    local p, x, xhat, w;
    _, p = size(A);
    w = ones(p);
    w_old = w;
    o = ones(p);
    x = zeros(p);

    solver = () -> SCS.Optimizer(verbose=0)

    for i = 1:maxiter
        # create a diagonal matrix from the vector w
        W = diagm(w);

        # solve the convex optimization task
        xhat = Variable(p);
        prob = minimize(norm(W*xhat, 1), A*xhat==b)
        solve!(prob, solver)
        xhat = evaluate(xhat);

        # check the stop condition then move on
        w = 1.0./(epsilon*o + abs.(xhat))
        if norm(w - w_old, 2) <= 1e-6
            break;
        end
        w = w_old;
    end
    x = xhat;
    i = abs.(x) .< epsilon;
    x[i] = zeros(sum(i));
    return x;
end

# A = randn(10, 50); b = randn(10);
# x = IRWLS(A, b);
# println(x)


"""
# Dynamic programming

- Shortest paths in dags, revisited
- Longest increasing subsequences
    - Recursion? No, thanks.
    - Programming?
- Edit distance
    - A dynamic programming solution
    - The underlying dag
    - Common subproblems
    - Of mice and men
- Knapsack
    - Knapsack with repetition
    - Knapsack without repetition
    - Memoization
- Chain matrix multiplication
- Shortest paths
    - Shortest reliable paths
    - All-pairs shortest paths
    - The traveling salesman problem
- Independent sets in trees
    - On time and memory
"""
module Chapter6

export
    lcs

"""
    lcs(A, B)

Find the longest common subsequence of two sequences `A` and `B`.

# Examples
```jldoctest
julia> lcs("This is a test", "This is not a test")
"This is a test"
```
"""
function lcs(A, B)
    T = zeros(length(A) + 1, length(B) + 1)

    for i ∈ 1:(length(A) + 1), j ∈ 1:(length(B) + 1)
        if i == 1 || j == 1
            T[i, j] = 0
        elseif A[i - 1] == B[j - 1]
            T[i, j] = T[i - 1, j - 1] + 1
        else
            T[i, j] = max(T[i - 1, j], T[i, j - 1])
        end
    end

    return lcs(A, B, T)
end

function lcs(a, b, T)
    result = []
    i, j = size(T)
    while i > 1 && j > 1
        if a[i - 1] == b[j - 1]
            pushfirst!(result, a[i - 1])
            i -= 1
            j -= 1
        elseif T[i - 1, j] > T[i, j - 1]
            i -= 1
        else
            j -= 1
        end
    end
    return join(result)
end

end # module


using Smolyak

const verbose = true
const print_str = "mean abs diff: %.3e\nmax abs diff: %.3e\nmin abs diff: %.3e"
@eval print_func(x, y, z) = @printf($print_str, x, y, z)

# L2-norm squared
f1(x) = squeeze(sum(x .^ 2, 2), 2)
f1_p(x) = 2 * x

f2(x) = sin(x[:,1]) + cos(x[:, 2])
f2_p(x) = [cos(x[:, 1]) -sin(x[:, 2])]

function test_interp2d_derivs(d::Int, mu::Int, f::Function, f_prime::Function, bds::Real=1)
    sg = SmolyakGrid(d, mu, -bds, bds)

    f_on_grid = f(sg.grid)
    si = SmolyakInterp(sg, f_on_grid)

    srand(42)
    test_points = randn(100, d)

    # Make sure it is bounded by -bds, bds
    test_points = (bds - 0.05) * test_points/maximum(abs(test_points))

    true_vals = f(test_points)
    true_vals_prime = f_prime(test_points)
    i_vals = si(test_points)

    mean_ad = mean(abs(i_vals - true_vals))
    max_ad = maximum(abs(i_vals - true_vals))
    min_ad = minimum(abs(i_vals - true_vals))

    if verbose
        println("Interpolation results\n" * "#" ^ 21)
        print_func(mean_ad, max_ad, min_ad)
    end

    # return i_vals_prime
end

test_interp2d_derivs(15, 3, f2, f2_p, -2)

@time test_interp2d_derivs(15, 3, f2, f2_p, -2)


@test begin
    c = [Segment(Point(0, 0, 0), Point(1, 1, 1))]

    counter = BoxCounter(c)
    sizes, counts = [], []

    state = start(counter, 8)

    rate = Inf
    while !done(counter, state)
        state = next(counter, state)
        size, count = last(state)
        push!(sizes, size)
        push!(counts, count)

        if length(sizes) > 1
            rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
        end
        println((size, count, rate))
    end
    # abs(rate - 1) < 1E-2
    true
end

@test begin
    a = collect(linspace(0, 2, 10000))
    c = [Segment(Point(a[i], a[i], a[i]), Point(a[i+1], a[i+1], a[i+1]))
         for i in 1:length(a)-1]

    counter = BoxCounter(c)
    sizes, counts = [], []

    state = start(counter, 8)

    rate = Inf
    while !done(counter, state)
        state = next(counter, state)
        size, count = last(state)
        push!(sizes, size)
        push!(counts, count)

        if length(sizes) > 1
            rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
        end
        println((size, count, rate))
    end
    # abs(rate - 1) < 1E-2
    true
end

# # Check here that bbox counting gets approx the right dims
include("curves.jl")

@test begin
    θ = linspace(0, 2*pi, 10000)
    x = sin.(θ)
    y = cos.(θ)
    z = 0.5 + 0.5*x
    # Lift
    c = [Segment(Point(x[i], y[i], z[i]), Point(x[i+1], y[i+1], z[i+1])) for i in 1:10000-1]

    counter = BoxCounter(c)
    sizes, counts = [], []

    state = start(counter, 13)

    rate = Inf
    while !done(counter, state)
        state = next(counter, state)
        size, count = last(state)
        push!(sizes, size)
        push!(counts, count)

        if length(sizes) > 1
            rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
        end
        println((size, count, rate))
    end
    # abs(rate - 1) < 1E-2
    true
end

# # Diagonal of one piece
# @test begin
#     c = [Segment(Point(0, 0), Point(1, 1))]

#     counter = BoxCounter(c)
#     sizes, counts = [], []

#     state = start(counter, 13)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#         end
#         # println((size, count, rate))
#     end
#     abs(rate - 1) < 1E-3
# end

# # Diagonal of more pieces
# @test begin
#     x = collect(linspace(0, 1, 10000))
#     c = [Segment(Point(x[i], x[i]), Point(x[i+1], x[i+1])) for i in 1:length(x)-1]

#     counter = BoxCounter(c)
#     sizes, counts = [], []

#     state = start(counter, 13)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#         end
#         # println((size, count, rate))
#     end
#     abs(rate - 1) < 1E-3
# end

# # Circle
# @test begin
#     c = unit_circle(10000)

#     counter = BoxCounter(c)
#     sizes, counts = [], []

#     state = start(counter, 13)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#         end
#         # println((size, count, rate))
#     end
#     abs(rate - 1) < 1E-3
# end

# # Koch
# @test begin
#     c = koch_flake(9)

#     counter = BoxCounter(c)
#     sizes, counts, rates = [], [], []

#     state = start(counter, 13)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#             push!(rates, rate)
#         end
#         # println((size, count, rate))
#     end
#     approx = mean(rates[end-5:end])
#     exact = log(4)/log(3)
#     @show (approx, exact)
#     abs(approx - exact) < 1E-2
# end

# @test begin
#     c = koch_quadratic_1(8)

#     counter = BoxCounter(c)
#     sizes, counts, rates = [], [], []

#     state = start(counter, 13)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#             push!(rates, rate)
#         end
#         # println((size, count, rate))
#     end
#     approx = mean(rates[end-5:end])
#     exact = log(5)/log(3)
#     @show (approx, exact)
#     abs(approx - exact) < 1E-2
# end

# @test begin
#     c = koch_quadratic_2(7)

#     counter = BoxCounter(c)
#     sizes, counts, rates = [], [], []

#     state = start(counter, 10)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#             push!(rates, rate)
#         end
#         # println((size, count, rate))
#     end
#     approx = round(mean(rates[end-3:end]), 2)
#     exact = 1.5
#     @show (approx, exact)
#     approx == exact
# end

# @test begin
#     c = dragon_curve(17)

#     counter = BoxCounter(c)
#     sizes, counts, rates = [], [], []

#     state = start(counter, 13)

#     rate = Inf
#     while !done(counter, state)
#         state = next(counter, state)
#         size, count = last(state)
#         push!(sizes, size)
#         push!(counts, count)

#         if length(sizes) > 1
#             rate = -log(counts[end]/counts[end-1])/log(sizes[end]/sizes[end-1])
#             push!(rates, rate)
#         end
#         # println((size, count, rate))
#     end
#     approx = mean(rates[end-5:end])
#     exact = 1.5236270
#     @show (approx, exact)
#     abs(approx - exact) < 1E-2
# end


#------------------------------------------------------------------------------
# LTE Estimation of the Least Absolute Deviations for the Censored Regression model
#------------------------------------------------------------------------------
# Julien Pascal
# https://julienpascal.github.io/
#
# based on:
# - J. L. Powell (1984), "Least Absolute Deviations estimation for the censored regression model", Journal of Econometrics 25, 303-325
# - V. Chernozhukov and H. Hong (2003) "An MCMC approach to classical estimation" Journal of Econometrics 115, 293 - 346
#
# This script: Estimate the LAD for the censored regression model with a LTE approach
#------------------------------------------------------------------------------
using Distributed
using RobustPmap
using Test
using Random
using Statistics
using Plots

using CSV
using GLM
using DataFrames

addprocs(3)
@everywhere using AffineInvariantMCMC
@everywhere using Distributions
@everywhere using LinearAlgebra
#------------------------------------------------------------------------------
# Paths
#------------------------------------------------------------------------------
path_to_main = pwd()

#------------------------------------------------------------------------------
# Generate data
#------------------------------------------------------------------------------
# true parameters
# produces approximately 40% of censoring rate:
beta = transpose([1 2 3 4]) #beta as a column vector
number_regressors = size(beta,1)
M = 1000 #number of people in the sample

#Generate a 3*M matrix of observations:
d = MvNormal(zeros(3), I(3))
X = transpose(rand(d, M)) #transpose to have the usual matrix representation

#Add a first column of one for the constant term
X = hcat(ones(M),X)
#Generate error terms:
u = rand(Normal(0,1), M).*((X[:, 2]).^2)
#Generate Y star:
Y_star = X*beta .+ u
#Generate truncation:
Y = max.(0.0, Y_star)

#Censoring rate:
println("Censoring rate = $(100*sum(Y .== 0)/M) %")

h1 = histogram(Y_star) # Histogram
title!(h1, "Non-Truncated Distribution", legend=:none)
h2 = histogram(Y) # Histogram
title!(h2, "Truncated Distribution", legend=:none)
h = plot(h1, h2)
savefig(h, joinpath(path_to_main,"output/censoring.png"))

#-------------------------------------------------------------------------------
# MCMC Estimation
#-------------------------------------------------------------------------------
@everywhere begin
	numdims = 4
	numwalkers = 100
	thinning = 10
	numsamples_perwalker = 1000
	burnin = Int((1/10)*numsamples_perwalker)
	lb = 0 .* ones(numdims) #lower bound
	ub = 5 .* ones(numdims) #upper bound
	# Uniform prior
	d_prior = Product(Uniform.(lb, ub))
	# Normal
	# d_prior = MvNormal(zeros(numdims), 0.1 .* I(numdims))
end

# Log-likelihood
@everywhere function Ln(theta, Y, X)
	Ln_theta = - sum(abs.(Y .- max.(0.0, X*theta)))
	return Ln_theta
end

# Log quasi-posterior: Log(likelihood) + log(prior)
@everywhere function quasi_posterior(theta, Y, X, d_prior)
	 return Ln(theta, Y, X) + log(pdf(d_prior, theta))
end

# initial draw for MCMC is OLS
data = DataFrame(y = Y[:,1], x1 = X[:,2], x2 = X[:,3], x3 = X[:,4])
OLS = glm(@formula(y ~ x1 + x2 + x3), data, Normal(), IdentityLink())
theta0 = coef(OLS)

# Option 1: slightly perturb the initial draws for the walkers
x0 = ones(numdims, numwalkers).*theta0 .+ rand(numdims, numwalkers) .* 1.0
# Option 2: introduce a bias to see how fast the chains converge
#x0 = ones(numdims, numwalkers).*theta0 .+ ones(numdims, numwalkers) .* 1.0

# See https://github.com/madsjulia/AffineInvariantMCMC.jl
#=
chain, llhoodvals = AffineInvariantMCMC.sample(x -> Ln(x, Y, X), numwalkers, x0, burnin, 1)
chain, llhoodvals = AffineInvariantMCMC.sample(x -> Ln(x, Y, X), numwalkers, chain[:, :, end], numsamples_perwalker, thinning)
flatchain, flatllhoodvals = AffineInvariantMCMC.flattenmcmcarray(chain, llhoodvals)
=#

chain, llhoodvals = AffineInvariantMCMC.sample(x -> quasi_posterior(x, Y, X, d_prior), numwalkers, x0, burnin, 1)
chain, llhoodvals = AffineInvariantMCMC.sample(x -> quasi_posterior(x, Y, X, d_prior), numwalkers, chain[:, :, end], numsamples_perwalker, thinning)
flatchain, flatllhoodvals = AffineInvariantMCMC.flattenmcmcarray(chain, llhoodvals)

#-------------------------------------------------------------------------------
# Plot Draws
#-------------------------------------------------------------------------------
#Use the function "squeeze" to go from Array{Float64,2} to Array{Float64,1}: necessary for plotting
p1 = plot(flatchain[1,:], ylabel="theta0", xlabel="T", legend=:none)
p2 = plot(flatchain[2,:], ylabel="theta1", xlabel="T", legend=:none)
p3 = plot(flatchain[3,:], ylabel="theta2", xlabel="T", legend=:none)
p4 = plot(flatchain[4,:], ylabel="theta3", xlabel="T", legend=:none)
p5 = plot(p1, p2, p3, p4)
savefig(p5, joinpath(path_to_main,"output/chains.png"))
display(p5)

#-------------------------------------------------------------------------------
# Plot Histograms
#-------------------------------------------------------------------------------
hh1 = histogram(flatchain[1,burnin:end], title="theta0", legend=:none)
hh2 = histogram(flatchain[2,burnin:end], title="theta1", legend=:none)
hh3 = histogram(flatchain[3,burnin:end], title="theta2", legend=:none)
hh4 = histogram(flatchain[4,burnin:end], title="theta3", legend=:none)
hh5 = plot(hh1, hh2, hh3, hh4)
savefig(hh5, joinpath(path_to_main,"output/histograms.png"))
display(hh5)

#-------------------------------------------------------------------------------
# Use the sample to find quasi-posterior summary statistics
#-------------------------------------------------------------------------------
# append true value as well
result_beta0 = append!(quantile(flatchain[1,burnin:end],[0.10, 0.5, 0.90]), beta[1])
result_beta1 = append!(quantile(flatchain[2,burnin:end],[0.10, 0.5, 0.90]), beta[2])
result_beta2 = append!(quantile(flatchain[3,burnin:end],[0.10, 0.5, 0.90]), beta[3])
result_beta3 = append!(quantile(flatchain[4,burnin:end],[0.10, 0.5, 0.90]), beta[4])
results = DataFrame(variable = ["P10"; "Median"; "P90"; "True value"], beta0 = result_beta0,
						beta1 = result_beta1, beta2 = result_beta2, beta3 = result_beta3)

CSV.write(joinpath(path_to_main,"output/output_table.csv"), results)

#-------------------------------------------------------------------------------
# Example with GMM
#-------------------------------------------------------------------------------
# Let's assume now that we observe Y_star
@everywhere begin
	numdims = 4
	numwalkers = 100
	thinning = 10
	numsamples_perwalker = 2000
	burnin = Int((1/10)*numsamples_perwalker)
	lb = 0 .* ones(numdims) #lower bound
	ub = 5 .* ones(numdims) #upper bound
	# Uniform prior
	d_prior = Product(Uniform.(lb, ub))
end

# Log-likelihood
@everywhere function Ln_GMM(theta, Y, X)
	m = (1/sqrt(size(X,1))).*(transpose(X)*(Y .- X*theta))
	Ln_theta = - 0.5*transpose(m)*I(size(theta,1))*m
	return Ln_theta[1]
end

chain, llhoodvals = AffineInvariantMCMC.sample(x -> Ln_GMM(x, Y_star, X), numwalkers, x0, burnin, 1)
chain, llhoodvals = AffineInvariantMCMC.sample(x -> Ln_GMM(x, Y_star, X), numwalkers, chain[:, :, end], numsamples_perwalker, thinning)
flatchain, flatllhoodvals = AffineInvariantMCMC.flattenmcmcarray(chain, llhoodvals)

#-------------------------------------------------------------------------------
# Plot Draws
#-------------------------------------------------------------------------------
#Use the function "squeeze" to go from Array{Float64,2} to Array{Float64,1}: necessary for plotting
p1 = plot(flatchain[1,:], ylabel="theta0", xlabel="T", legend=:none)
p2 = plot(flatchain[2,:], ylabel="theta1", xlabel="T", legend=:none)
p3 = plot(flatchain[3,:], ylabel="theta2", xlabel="T", legend=:none)
p4 = plot(flatchain[4,:], ylabel="theta3", xlabel="T", legend=:none)
p5 = plot(p1, p2, p3, p4)
savefig(p5, joinpath(path_to_main,"output/chains_GMM.png"))
display(p5)

hh1 = histogram(flatchain[1,burnin:end], title="theta0", legend=:none)
hh2 = histogram(flatchain[2,burnin:end], title="theta1", legend=:none)
hh3 = histogram(flatchain[3,burnin:end], title="theta2", legend=:none)
hh4 = histogram(flatchain[4,burnin:end], title="theta3", legend=:none)
hh5 = plot(hh1, hh2, hh3, hh4)
savefig(hh5, joinpath(path_to_main,"output/histograms_GMM.png"))
display(hh5)

# Compare to traditional OLS
data = DataFrame(y = Y_star[:,1], x1 = X[:,2], x2 = X[:,3], x3 = X[:,4])
GMM = glm(@formula(y ~ x1 + x2 + x3), data, Normal(), IdentityLink())
stds_GMM = stderror(GMM)
coefs_GMM = coef(GMM)

result_beta0 = append!(quantile(flatchain[1,burnin:end],[0.05, 0.10, 0.5, 0.90, 0.95]), std(flatchain[1,burnin:end]), beta[1])
result_beta1 = append!(quantile(flatchain[2,burnin:end],[0.05, 0.10, 0.5, 0.90, 0.95]), std(flatchain[2,burnin:end]), beta[2])
result_beta2 = append!(quantile(flatchain[3,burnin:end],[0.05, 0.10, 0.5, 0.90, 0.95]), std(flatchain[3,burnin:end]), beta[3])
result_beta3 = append!(quantile(flatchain[4,burnin:end],[0.05, 0.10, 0.5, 0.90, 0.95]), std(flatchain[4,burnin:end]), beta[4])
results = DataFrame(variable = ["P5"; "P10"; "Median"; "P90"; "P95"; "std"; "True value"], beta0 = result_beta0,
						beta1 = result_beta1, beta2 = result_beta2, beta3 = result_beta3)

CSV.write(joinpath(path_to_main,"output/output_table_GMM.csv"), results)