import FastGaussQuadrature

function points(filter::String, n::Integer)
  if (filter == "zolo")
    return zoloPts(n)
  end

  return gaussPts(n)
end

"FEAST Implementation of Gauss Filters as per the Güttel paper
For multiplier=2, we have quadrupel degrees, for multiplier=1, we have half-degrees
S determines the shape of the ellipse. S->Inf means the unit circle"
function gaussPts( n; multiplier = 2 , S = 1e6 )
  n = multiplier*n;

  # Obtain GL nodes
  x, omega = FastGaussQuadrature.gausslegendre(n);

  # Map GL nodes from (-1,1) to (0,pi)
  a = 0;
  b = pi;
  theta = (a*(1-x)+b*(1+x))/2;
  omega = omega * (b-a) / 2;

  # Compute mapped nodes and weights according to Güttel, above (3.3)
  z = (S*exp.(im * theta) + 1/S * exp.( - im * theta )) / (S+1/S);
  w = (-omega / 2 / pi) .* (S * exp.(im.*theta) - 1/S * exp.(-im .* theta))/(S+1/S);

  # Take only half of the points due to symmetry
  Z = z[1:Integer(end/2)];
  W = w[1:Integer(end/2)];

  return -conj([Z,W])      # map to other quadrant
end

"FEAST Implementation of Elliptic Filters"
function zoloPts(n)
  n = n*2;
  W = zeros(Complex{Float64},n)
  Z = zeros(Complex{Float64},n)

  #=
  if( n == 1)
    W = (0.0000000000000000e0) + (9.9800399400799011e-1)*1im;
    Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im;
  end
  =#

  if( n == 2)
    W[1] = (-4.0933604666346268e-2) + (1.8306055366585177e-3)*1im;
    Z[1] = (-9.9900149850137365e-1) + (4.4676682867128663e-2)*1im;
    W[2] = (4.0933604666346268e-2) + (1.8306055366585177e-3)*1im;
    Z[2] = (9.9900149850137365e-1) + (4.4676682867128663e-2)*1im;
  end

  #  if( n == 3 )
  #    W[1] = (-9.4403480868925395e-3) + (1.1819628351771121e-4)*1im;
  #    Z[1] = (-9.9992162986865651e-1) + (1.2519349855705481e-2)*1im;
  #    W[2] = (0.0000000000000000e0) + (7.4467858236516826e-1)*1im;
  #    Z[2] = (0.0000000000000000e0) + (1.0000000000000000e0)*1im;
  #    W[3] = (9.4403480868925395e-3) + (1.1819628351771121e-4)*1im;
  #    Z[3] = (9.9992162986865651e-1) + (1.2519349855705481e-2)*1im;
  #  end

  if( n == 4)
    W[1] = (-4.0449611415998383e-3) + (2.6445705300228226e-5)*1im;
    Z[1] = (-9.9997862836826079e-1) + (6.5377983092055223e-3)*1im;
    W[2] = (-1.6550649051159086e-1) + (5.0629517778791600e-2)*1im;
    Z[2] = (-9.5625772508631735e-1) + (2.9252549156054936e-1)*1im;
    W[3] = (1.6550649051159086e-1) + (5.0629517778791600e-2)*1im ;
    Z[3] = (9.5625772508631735e-1) + (2.9252549156054936e-1)*1im ;
    W[4] = (4.0449611415998383e-3) + (2.6445705300228226e-5)*1im ;
    Z[4] = (9.9997862836826079e-1) + (6.5377983092055223e-3)*1im ;
  end

  #  if( n == 5 )
  #        W[1] = (-2.3046399755639315e-3) + (1.0035321417280235e-5)*1im;
  #        Z[1] = (-9.9999051974059949e-1) + (4.3543574641737573e-3)*1im;
  #        W[2] = (-4.5688379331906018e-2) + (4.3789522408392290e-3)*1im;
  #        Z[2] = (-9.9543837740544383e-1) + (9.5406691528514928e-2)*1im;
  #        W[3] = (0.0000000000000000e0) + (4.8097001541667800e-1)*1im  ;
  #        Z[3] = (0.0000000000000000e0) + (1.0000000000000000e0)*1im   ;
  #        W[4] = (4.5688379331906018e-2) + (4.3789522408392290e-3)*1im ;
  #        Z[4] = (9.9543837740544383e-1) + (9.5406691528514928e-2)*1im ;
  #        W[5] = (2.3046399755639315e-3) + (1.0035321417280235e-5)*1im ;
  #        Z[5] = (9.9999051974059949e-1) + (4.3543574641737573e-3)*1im ;
  #  end

  if( n == 6)
    W[1] = (-1.5423558061466516e-3) + (5.0401387941688375e-6)*1im;
    Z[1] = (-9.9999466072397813e-1) + (3.2678010245045879e-3)*1im;
    W[2] = (-1.7985701250813335e-2) + (8.0434461022422783e-4)*1im;
    Z[2] = (-9.9900149850137365e-1) + (4.4676682867128663e-2)*1im;
    W[3] = (-1.7924652624997453e-1) + (1.0970398051397839e-1)*1im;
    Z[3] = (-8.5293349191434553e-1) + (5.2201959577280344e-1)*1im;
    W[4] = (1.7924652624997453e-1) + (1.0970398051397839e-1)*1im ;
    Z[4] = (8.5293349191434553e-1) + (5.2201959577280344e-1)*1im ;
    W[5] = (1.7985701250813335e-2) + (8.0434461022422783e-4)*1im ;
    Z[5] = (9.9900149850137365e-1) + (4.4676682867128663e-2)*1im ;
    W[6] = (1.5423558061466516e-3) + (5.0401387941688375e-6)*1im ;
    Z[6] = (9.9999466072397813e-1) + (3.2678010245045879e-3)*1im ;
  end

  #=
  case(7)
  select case(e)
  !------------------------   n =  7, S = 1.0e06, rate = 2.17e-2  ------------------------

  case(0)
  W = (-2.1237706090261987e-2) + (0.0000000000000000e0)*1im
  case(1)
  W = (-1.1399193084610077e-3) + (2.9915174055686861e-6)*1im
  Z = (-9.9999655648000429e-1) + (2.6243147931738105e-3)*1im
  case(2)
  W = (-8.9861107855352777e-3) + (2.3319989914114602e-4)*1im
  Z = (-9.9966343892137466e-1) + (2.5942414765997054e-2)*1im
  case(3)
  W = (-7.5755925208015523e-2) + (1.7497105287481524e-2)*1im
  Z = (-9.7434899659958563e-1) + (2.2504229119296795e-1)*1im
  case(4)
  W = (0.0000000000000000e0) + (3.4547899051275072e-1)*1im
  Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
  case(5)
  W = (7.5755925208015523e-2) + (1.7497105287481524e-2)*1im
  Z = (9.7434899659958563e-1) + (2.2504229119296795e-1)*1im
  case(6)
  W = (8.9861107855352777e-3) + (2.3319989914114602e-4)*1im
  Z = (9.9966343892137466e-1) + (2.5942414765997054e-2)*1im
  case(7)
  W = (1.1399193084610077e-3) + (2.9915174055686861e-6)*1im
  Z = (9.9999655648000429e-1) + (2.6243147931738105e-3)*1im
end select
=#

if (n == 8)
  W[1] = (-8.9892014626439772e-4) + (1.9770010320296091e-6)*1im;
  Z[1] = (-9.9999758153396057e-1) + (2.1993013049440135e-3)*1im;
  W[2] = (-5.2457912271928649e-3) + (9.0422169329207065e-5)*1im;
  Z[2] = (-9.9985147448075562e-1) + (1.7234528675274002e-2)*1im;
  W[3] = (-3.4625385252140740e-2) + (4.0175404307143140e-3)*1im;
  Z[3] = (-9.9333587640998278e-1) + (1.1525552757595411e-1)*1im;
  W[4] = (-1.5051737271560608e-1) + (1.3687697801045523e-1)*1im;
  Z[4] = (-7.3983485714849262e-1) + (6.7278851368618764e-1)*1im;
  W[5] = (1.5051737271560608e-1) + (1.3687697801045523e-1)*1im ;
  Z[5] = (7.3983485714849262e-1) + (6.7278851368618764e-1)*1im ;
  W[6] = (3.4625385252140740e-2) + (4.0175404307143140e-3)*1im ;
  Z[6] = (9.9333587640998278e-1) + (1.1525552757595411e-1)*1im ;
  W[7] = (5.2457912271928649e-3) + (9.0422169329207065e-5)*1im ;
  Z[7] = (9.9985147448075562e-1) + (1.7234528675274002e-2)*1im ;
  W[8] = (8.9892014626439772e-4) + (1.9770010320296091e-6)*1im ;
  Z[8] = (9.9999758153396057e-1) + (2.1993013049440135e-3)*1im ;
end

#=
case(9)
select case(e)
!------------------------   n =  9, S = 1.0e06, rate = 5.83e-3  ------------------------
case(0)
W = (-5.7991882972451281e-3) + (0.0000000000000000e0)*1im
case(1)
W = (-7.4104052419255275e-4) + (1.4058775564853451e-6)*1im
Z = (-9.9999820038372844e-1) + (1.8971634891048340e-3)*1im
case(2)
W = (-3.4078286502537015e-3) + (4.2667142950419761e-5)*1im
Z = (-9.9992162986865651e-1) + (1.2519349855705481e-2)*1im
case(3)
W = (-1.8278566233392055e-2) + (1.2481296809871830e-3)*1im
Z = (-9.9767678352825928e-1) + (6.8125146669250583e-2)*1im
case(4)
W = (-8.9685989000547653e-2) + (3.4297391012017266e-2)*1im
Z = (-9.3403206804331040e-1) + (3.5718915978335170e-1)*1im
case(5)
W = (0.0000000000000000e0) + (2.6881816060764269e-1)*1im
Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
case(6)
W = (8.9685989000547653e-2) + (3.4297391012017266e-2)*1im
Z = (9.3403206804331040e-1) + (3.5718915978335170e-1)*1im
case(7)
W = (1.8278566233392055e-2) + (1.2481296809871830e-3)*1im
Z = (9.9767678352825928e-1) + (6.8125146669250583e-2)*1im
case(8)
W = (3.4078286502537015e-3) + (4.2667142950419761e-5)*1im
Z = (9.9992162986865651e-1) + (1.2519349855705481e-2)*1im
case(9)
W = (7.4104052419255275e-4) + (1.4058775564853451e-6)*1im
Z = (9.9999820038372844e-1) + (1.8971634891048340e-3)*1im
end select
=#

if(n==10)

  W[1] = (-6.3052218358594105e-4) + (1.0535055580202612e-6)*1im;
  Z[1] = (-9.9999860413950714e-1) + (1.6708438099384962e-3)*1im;
  W[2] = (-2.3906025222613010e-3) + (2.3134572353828454e-5)*1im;
  Z[2] = (-9.9995317824297625e-1) + (9.6768446184941678e-3)*1im;
  W[3] = (-1.0809177648659182e-2) + (4.8340087836508665e-4)*1im;
  Z[3] = (-9.9900149850137365e-1) + (4.4676682867128663e-2)*1im;
  W[4] = (-4.7956018043409793e-2) + (9.9110250490149158e-3)*1im;
  Z[4] = (-9.7930459779150147e-1) + (2.0239195820097600e-1)*1im;
  W[5] = (-1.1904157484648165e-1) + (1.4249242081357993e-1)*1im;
  Z[5] = (-6.4113075594177837e-1) + (7.6743166066140622e-1)*1im;
  Z = [Z[1:Integer(end/2)];-conj(Z[1:Integer(end/2)])];
  W = [W[1:Integer(end/2)];-conj(W[1:Integer(end/2)])];
  #        W = (1.1904157484648165e-1) + (1.4249242081357993e-1)*1im
  #        Z = (6.4113075594177837e-1) + (7.6743166066140622e-1)*1im
  #        W = (4.7956018043409793e-2) + (9.9110250490149158e-3)*1im
  #        Z = (9.7930459779150147e-1) + (2.0239195820097600e-1)*1im
  #        W = (1.0809177648659182e-2) + (4.8340087836508665e-4)*1im
  #        Z = (9.9900149850137365e-1) + (4.4676682867128663e-2)*1im
  #        W = (2.3906025222613010e-3) + (2.3134572353828454e-5)*1im
  #        Z = (9.9995317824297625e-1) + (9.6768446184941678e-3)*1im
  #        W = (6.3052218358594105e-4) + (1.0535055580202612e-6)*1im
  #        Z = (9.9999860413950714e-1) + (1.6708438099384962e-3)*1im
end

#=
case(11)
select case(e)
!------------------------   n = 11, S = 1.0e06, rate = 1.59e-3  ------------------------
case(0)
W = (-1.5829197980627985e-3) + (0.0000000000000000e0)*1im
case(1)
W = (-5.4916092156993054e-4) + (8.2078370994785510e-7)*1im
Z = (-9.9999888306607199e-1) + (1.4946125278547568e-3)*1im
case(2)
W = (-1.7758360089787139e-3) + (1.3891886287560548e-5)*1im
Z = (-9.9996940384898791e-1) + (7.8224910290555765e-3)*1im
case(3)
W = (-6.9646144107369046e-3) + (2.2031308203370500e-4)*1im
Z = (-9.9950004532269476e-1) + (3.1617390783098893e-2)*1im
case(4)
W = (-2.7405037741489228e-2) + (3.4688676881267449e-3)*1im
Z = (-9.9208403387564725e-1) + (1.2557575295025525e-1)*1im
case(5)
W = (-9.1577068203742937e-2) + (4.9080286015786748e-2)*1im
Z = (-8.8139525357848125e-1) + (4.7237951582316168e-1)*1im
case(6)
W = (0.0000000000000000e0) + (2.1994897763355334e-1)*1im
Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
case(7)
W = (9.1577068203742937e-2) + (4.9080286015786748e-2)*1im
Z = (8.8139525357848125e-1) + (4.7237951582316168e-1)*1im
case(8)
W = (2.7405037741489228e-2) + (3.4688676881267449e-3)*1im
Z = (9.9208403387564725e-1) + (1.2557575295025525e-1)*1im
case(9)
W = (6.9646144107369046e-3) + (2.2031308203370500e-4)*1im
Z = (9.9950004532269476e-1) + (3.1617390783098893e-2)*1im
case(10)
W = (1.7758360089787139e-3) + (1.3891886287560548e-5)*1im
Z = (9.9996940384898791e-1) + (7.8224910290555765e-3)*1im
case(11)
W = (5.4916092156993054e-4) + (8.2078370994785510e-7)*1im
Z = (9.9999888306607199e-1) + (1.4946125278547568e-3)*1im
end select
=#

if(n == 12)
  W[1] = (-4.8687164580549528e-4) + (6.5885540028861806e-7)*1im;
  Z[1] = (-9.9999908436863361e-1) + (1.3532412550538205e-3)*1im;
  W[2] = (-1.3784196581295379e-3) + (9.0120223119170229e-6)*1im;
  Z[2] = (-9.9997862836826079e-1) + (6.5377983092055223e-3)*1im;
  W[3] = (-4.7923676064111223e-3) + (1.1357135190625232e-4)*1im;
  Z[3] = (-9.9971931159972960e-1) + (2.3691728821737014e-2)*1im;
  W[4] = (-1.6899924452230135e-2) + (1.4262499855059792e-3)*1im;
  Z[4] = (-9.9645774817202859e-1) + (8.4094923199501848e-2)*1im;
  W[5] = (-5.6400393497717298e-2) + (1.7253249201870668e-2)*1im;
  Z[5] = (-9.5625772508631735e-1) + (2.9252549156054936e-1)*1im;
  W[6] = (-9.3578819652718012e-2) + (1.3830296710345763e-1)*1im;
  Z[6] = (-5.6039535450830469e-1) + (8.2822523907781964e-1)*1im;
  Z = [Z[1:Integer(end/2)];-conj(Z[1:Integer(end/2)])];
  W = [W[1:Integer(end/2)];-conj(W[1:Integer(end/2)])];
end

#=

case(13)
select case(e)
!------------------------   n = 13, S = 1.0e06, rate = 4.32e-4  ------------------------
case(0)
W = (-4.3205406996127405e-4) + (0.0000000000000000e0)*1im
case(1)
W = (-4.3767780889597465e-4) + (5.4147060148906031e-7)*1im
Z = (-9.9999923473788310e-1) + (1.2371433417836721e-3)*1im
case(2)
W = (-1.1075297345821931e-3) + (6.2076540794421900e-6)*1im
Z = (-9.9998429261104993e-1) + (5.6048667404373113e-3)*1im
case(3)
W = (-3.4716788620964018e-3) + (6.4409116887187564e-5)*1im
Z = (-9.9982794254032570e-1) + (1.8549536802280383e-2)*1im
case(4)
W = (-1.1122499655942791e-2) + (6.6672796922738465e-4)*1im
Z = (-9.9820818130463496e-1) + (5.9836667491538521e-2)*1im
case(5)
W = (-3.4914922416108364e-2) + (6.7980844778368485e-3)*1im
Z = (-9.8156757516467719e-1) + (1.9111539808538686e-1)*1im
case(6)
W = (-8.6881729971417979e-2) + (5.9724667568325514e-2)*1im
Z = (-8.2407080158656099e-1) + (5.6648681712153093e-1)*1im
case(7)
W = (0.0000000000000000e0) + (1.8611110496879130e-1)*1im
Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
case(8)
W = (8.6881729971417979e-2) + (5.9724667568325514e-2)*1im
Z = (8.2407080158656099e-1) + (5.6648681712153093e-1)*1im
case(9)
W = (3.4914922416108364e-2) + (6.7980844778368485e-3)*1im
Z = (9.8156757516467719e-1) + (1.9111539808538686e-1)*1im
case(10)
W = (1.1122499655942791e-2) + (6.6672796922738465e-4)*1im
Z = (9.9820818130463496e-1) + (5.9836667491538521e-2)*1im
case(11)
W = (3.4716788620964018e-3) + (6.4409116887187564e-5)*1im
Z = (9.9982794254032570e-1) + (1.8549536802280383e-2)*1im
case(12)
W = (1.1075297345821931e-3) + (6.2076540794421900e-6)*1im
Z = (9.9998429261104993e-1) + (5.6048667404373113e-3)*1im
case(13)
W = (4.3767780889597465e-4) + (5.4147060148906031e-7)*1im
Z = (9.9999923473788310e-1) + (1.2371433417836721e-3)*1im
end select

=#

if(n==14)

  W[1] = (-3.9783815941610621e-4) + (4.5352749686171894e-7)*1im;
  Z[1] = (-9.9999935022357100e-1) + (1.1399791383407472e-3)*1im;
  W[2] = (-9.1483445273240691e-4) + (4.4839989288394089e-6)*1im;
  Z[2] = (-9.9998798819824153e-1) + (4.9013731987690712e-3)*1im;
  W[3] = (-2.6203167326490271e-3) + (3.9392030922682924e-5)*1im;
  Z[3] = (-9.9988701896134347e-1) + (1.5031610445923209e-2)*1im;
  W[4] = (-7.7209116615673213e-3) + (3.4528949382600389e-4)*1im;
  Z[4] = (-9.9900149850137365e-1) + (4.4676682867128663e-2)*1im;
  W[5] = (-2.2593959355968814e-2) + (3.0058501258880678e-3)*1im;
  Z[5] = (-9.9126623989398643e-1) + (1.3187585695053436e-1)*1im;
  W[6] = (-6.0452583497664004e-2) + (2.4667289940949678e-2)*1im;
  Z[6] = (-9.2588640299049352e-1) + (3.7780202323085216e-1)*1im;
  W[7] = (-7.4349151462284435e-2) + (1.3043927963654731e-1)*1im;
  Z[7] = (-4.9519682077916088e-1) + (8.6878081740460389e-1)*1im;
  Z = [Z[1:Integer(end/2)];-conj(Z[1:Integer(end/2)])];
  W = [W[1:Integer(end/2)];-conj(W[1:Integer(end/2)])];

  #        W = (7.4349151462284435e-2) + (1.3043927963654731e-1)*1im
  #        Z = (4.9519682077916088e-1) + (8.6878081740460389e-1)*1im
  #        W = (6.0452583497664004e-2) + (2.4667289940949678e-2)*1im
  #        Z = (9.2588640299049352e-1) + (3.7780202323085216e-1)*1im
  #        W = (2.2593959355968814e-2) + (3.0058501258880678e-3)*1im
  #        Z = (9.9126623989398643e-1) + (1.3187585695053436e-1)*1im
  #        W = (7.7209116615673213e-3) + (3.4528949382600389e-4)*1im
  #        Z = (9.9900149850137365e-1) + (4.4676682867128663e-2)*1im
  #        W = (2.6203167326490271e-3) + (3.9392030922682924e-5)*1im
  #        Z = (9.9988701896134347e-1) + (1.5031610445923209e-2)*1im
  #        W = (9.1483445273240691e-4) + (4.4839989288394089e-6)*1im
  #        Z = (9.9998798819824153e-1) + (4.9013731987690712e-3)*1im
  #        W = (3.9783815941610621e-4) + (4.5352749686171894e-7)*1im
  #        Z = (9.9999935022357100e-1) + (1.1399791383407472e-3)*1im
end

#=
case(15)
select case(e)
!------------------------   n = 15, S = 1.0e06, rate = 1.18e-4  ------------------------
case(0)
W = (-1.1792784382658184e-4) + (0.0000000000000000e0)*1im
case(1)
W = (-3.6490219268281893e-4) + (3.8584221393928272e-7)*1im
Z = (-9.9999944096863402e-1) + (1.0573847074050477e-3)*1im
case(2)
W = (-7.7287550656539642e-4) + (3.3654081358322242e-6)*1im
Z = (-9.9999051974059949e-1) + (4.3543574641737573e-3)*1im
case(3)
W = (-2.0447659312819748e-3) + (2.5601146431954891e-5)*1im
Z = (-9.9992162986865651e-1) + (1.2519349855705481e-2)*1im
case(4)
W = (-5.5985191613083579e-3) + (1.9423338474605119e-4)*1im
Z = (-9.9939871608767983e-1) + (3.4672846469494513e-2)*1im
case(5)
W = (-1.5321885281304451e-2) + (1.4685091672664223e-3)*1im
Z = (-9.9543837740544383e-1) + (9.5406691528514928e-2)*1im
case(6)
W = (-4.0349347191103273e-2) + (1.0819051287289454e-2)*1im
Z = (-9.6588107005927215e-1) + (2.5898601989519698e-1)*1im
case(7)
W = (-7.9372797116653598e-2) + (6.6361266089171284e-2)*1im
Z = (-7.6718747568358492e-1) + (6.4142293157810371e-1)*1im
case(8)
W = (0.0000000000000000e0) + (1.6129631883911560e-1)*1im
Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
case(9)
W = (7.9372797116653598e-2) + (6.6361266089171284e-2)*1im
Z = (7.6718747568358492e-1) + (6.4142293157810371e-1)*1im
case(10)
W = (4.0349347191103273e-2) + (1.0819051287289454e-2)*1im
Z = (9.6588107005927215e-1) + (2.5898601989519698e-1)*1im
case(11)
W = (1.5321885281304451e-2) + (1.4685091672664223e-3)*1im
Z = (9.9543837740544383e-1) + (9.5406691528514928e-2)*1im
case(12)
W = (5.5985191613083579e-3) + (1.9423338474605119e-4)*1im
Z = (9.9939871608767983e-1) + (3.4672846469494513e-2)*1im
case(13)
W = (2.0447659312819748e-3) + (2.5601146431954891e-5)*1im
Z = (9.9992162986865651e-1) + (1.2519349855705481e-2)*1im
case(14)
W = (7.7287550656539642e-4) + (3.3654081358322242e-6)*1im
Z = (9.9999051974059949e-1) + (4.3543574641737573e-3)*1im
case(15)
W = (3.6490219268281893e-4) + (3.8584221393928272e-7)*1im
Z = (9.9999944096863402e-1) + (1.0573847074050477e-3)*1im
end select
=#

if(n==16)
  W[1] = (-3.3720271377405092e-4) + (3.3256791542695477e-7)*1im;
  Z[1] = (-9.9999951365073736e-1) + (9.8625467737379399e-4)*1im;
  W[2] = (-6.6519220239713778e-4) + (2.6062953723407513e-6)*1im;
  Z[2] = (-9.9999232430038310e-1) + (3.9180786512652823e-3)*1im;
  W[3] = (-1.6400960309060023e-3) + (1.7485654672736178e-5)*1im;
  Z[3] = (-9.9994317254293952e-1) + (1.0660754418012596e-2)*1im;
  W[4] = (-4.2082918803256564e-3) + (1.1690372030085671e-4)*1im;
  Z[4] = (-9.9961437632196248e-1) + (2.7768663101670359e-2)*1im;
  W[5] = (-1.0834902847825161e-2) + (7.8006735739269233e-4)*1im;
  Z[5] = (-9.9741833534204793e-1) + (7.1809918002307196e-2)*1im;
  W[6] = (-2.7404178905123672e-2) + (5.1408358392403394e-3)*1im;
  Z[6] = (-9.8285560240079251e-1) + (1.8437696393360870e-1)*1im;
  W[7] = (-6.1233481833219840e-2) + (3.1256621441574164e-2)*1im;
  Z[7] = (-8.9067323792710495e-1) + (4.5464401815095595e-1)*1im;
  W[8] = (-5.9982325962616087e-2) + (1.2163640524928188e-1)*1im;
  Z[8] = (-4.4227617016573273e-1) + (8.9687891563105204e-1)*1im;
  Z = [Z[1:Integer(end/2)];-conj(Z[1:Integer(end/2)])];
  W = [W[1:Integer(end/2)];-conj(W[1:Integer(end/2)])];

  #        W = (5.9982325962616087e-2) + (1.2163640524928188e-1)*1im
  #        Z = (4.4227617016573273e-1) + (8.9687891563105204e-1)*1im
  #        W = (6.1233481833219840e-2) + (3.1256621441574164e-2)*1im
  #        Z = (8.9067323792710495e-1) + (4.5464401815095595e-1)*1im
  #        W = (2.7404178905123672e-2) + (5.1408358392403394e-3)*1im
  #        Z = (9.8285560240079251e-1) + (1.8437696393360870e-1)*1im
  #        W = (1.0834902847825161e-2) + (7.8006735739269233e-4)*1im
  #        Z = (9.9741833534204793e-1) + (7.1809918002307196e-2)*1im
  #        W = (4.2082918803256564e-3) + (1.1690372030085671e-4)*1im
  #        Z = (9.9961437632196248e-1) + (2.7768663101670359e-2)*1im
  #        W = (1.6400960309060023e-3) + (1.7485654672736178e-5)*1im
  #        Z = (9.9994317254293952e-1) + (1.0660754418012596e-2)*1im
  #        W = (6.6519220239713778e-4) + (2.6062953723407513e-6)*1im
  #        Z = (9.9999232430038310e-1) + (3.9180786512652823e-3)*1im
  #        W = (3.3720271377405092e-4) + (3.3256791542695477e-7)*1im
  #        Z = (9.9999951365073736e-1) + (9.8625467737379399e-4)*1im
end

#=
case(17)
select case(e)
!------------------------   n = 17, S = 1.0e06, rate = 3.22e-5  ------------------------
case(0)
W = (-3.2188041018121893e-5) + (0.0000000000000000e0)*1im
case(1)
W = (-3.1356830506300385e-4) + (2.8983656153429189e-7)*1im
Z = (-9.9999957281917840e-1) + (9.2431675345324153e-4)*1im
case(2)
W = (-5.8146393205053479e-4) + (2.0715601964910134e-6)*1im
Z = (-9.9999365377560656e-1) + (3.5626406656038721e-3)*1im
case(3)
W = (-1.3460694593148770e-3) + (1.2444383027989803e-5)*1im
Z = (-9.9995726792672457e-1) + (9.2445833070427582e-3)*1im
case(4)
W = (-3.2596721764997800e-3) + (7.4412679397007801e-5)*1im
Z = (-9.9973953682146444e-1) + (2.2822324947379177e-2)*1im
case(5)
W = (-7.9431247622769856e-3) + (4.4442256920342060e-4)*1im
Z = (-9.9843843074930583e-1) + (5.5863225854435435e-2)*1im
case(6)
W = (-1.9193908884236641e-2) + (2.6371573117971421e-3)*1im
Z = (-9.9069280131322413e-1) + (1.3611676394242028e-1)*1im
case(7)
W = (-4.3784116406463298e-2) + (1.5064213717605706e-2)*1im
Z = (-9.4559743883263248e-1) + (3.2533902881942373e-1)*1im
case(8)
W = (-7.1148642777824761e-2) + (6.9872895653441694e-2)*1im
Z = (-7.1347440722420030e-1) + (7.0068129005709578e-1)*1im
case(9)
W = (0.0000000000000000e0) + (1.4232028316042250e-1)*1im
Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
case(10)
W = (7.1148642777824761e-2) + (6.9872895653441694e-2)*1im
Z = (7.1347440722420030e-1) + (7.0068129005709578e-1)*1im
case(11)
W = (4.3784116406463298e-2) + (1.5064213717605706e-2)*1im
Z = (9.4559743883263248e-1) + (3.2533902881942373e-1)*1im
case(12)
W = (1.9193908884236641e-2) + (2.6371573117971421e-3)*1im
Z = (9.9069280131322413e-1) + (1.3611676394242028e-1)*1im
case(13)
W = (7.9431247622769856e-3) + (4.4442256920342060e-4)*1im
Z = (9.9843843074930583e-1) + (5.5863225854435435e-2)*1im
case(14)
W = (3.2596721764997800e-3) + (7.4412679397007801e-5)*1im
Z = (9.9973953682146444e-1) + (2.2822324947379177e-2)*1im
case(15)
W = (1.3460694593148770e-3) + (1.2444383027989803e-5)*1im
Z = (9.9995726792672457e-1) + (9.2445833070427582e-3)*1im
case(16)
W = (5.8146393205053479e-4) + (2.0715601964910134e-6)*1im
Z = (9.9999365377560656e-1) + (3.5626406656038721e-3)*1im
case(17)
W = (3.1356830506300385e-4) + (2.8983656153429189e-7)*1im
Z = (9.9999957281917840e-1) + (9.2431675345324153e-4)*1im
end select

=#

if( n == 18)
  W[1] = (-2.9315283498838234e-4) + (2.5500445397162234e-7)*1im;
  Z[1] = (-9.9999962166450085e-1) + (8.6986829755370020e-4)*1im;
  W[2] = (-5.1496929311266781e-4) + (1.6828261686953906e-6)*1im;
  Z[2] = (-9.9999466072397813e-1) + (3.2678010245045879e-3)*1im;
  W[3] = (-1.1264034015766962e-3) + (9.1672826634493694e-6)*1im;
  Z[3] = (-9.9996688370298181e-1) + (8.1382736097585021e-3)*1im;
  W[4] = (-2.5895909782401231e-3) + (4.9639523202098925e-5)*1im;
  Z[4] = (-9.9981632789817620e-1) + (1.9165345501885981e-2)*1im;
  W[5] = (-6.0051538188240234e-3) + (2.6855850880543866e-4)*1im;
  Z[5] = (-9.9900149850137365e-1) + (4.4676682867128663e-2)*1im;
  W[6] = (-1.3875670060744669e-2) + (1.4477284130137483e-3)*1im;
  Z[6] = (-9.9460106547849614e-1) + (1.0377244600104668e-1)*1im;
  W[7] = (-3.1151203067143453e-2) + (7.6552207011467777e-3)*1im;
  Z[7] = (-9.7110718365323745e-1) + (2.3864374673784650e-1)*1im;
  W[8] = (-5.9847706053290449e-2) + (3.6628501070755985e-2)*1im;
  Z[8] = (-8.5293349191434553e-1) + (5.2201959577280344e-1)*1im;
  W[9] = (-4.9161393686735257e-2) + (1.1303165332996631e-1)*1im;
  Z[9] = (-3.9884344260344645e-1) + (9.1701903376769189e-1)*1im;
  Z = [Z[1:Integer(end/2)];-conj(Z[1:Integer(end/2)])];
  W = [W[1:Integer(end/2)];-conj(W[1:Integer(end/2)])];

  #        W = (4.9161393686735257e-2) + (1.1303165332996631e-1)*1im
  #        Z = (3.9884344260344645e-1) + (9.1701903376769189e-1)*1im
  #        W = (5.9847706053290449e-2) + (3.6628501070755985e-2)*1im
  #        Z = (8.5293349191434553e-1) + (5.2201959577280344e-1)*1im
  #        W = (3.1151203067143453e-2) + (7.6552207011467777e-3)*1im
  #        Z = (9.7110718365323745e-1) + (2.3864374673784650e-1)*1im
  #        W = (1.3875670060744669e-2) + (1.4477284130137483e-3)*1im
  #        Z = (9.9460106547849614e-1) + (1.0377244600104668e-1)*1im
  #        W = (6.0051538188240234e-3) + (2.6855850880543866e-4)*1im
  #        Z = (9.9900149850137365e-1) + (4.4676682867128663e-2)*1im
  #        W = (2.5895909782401231e-3) + (4.9639523202098925e-5)*1im
  #        Z = (9.9981632789817620e-1) + (1.9165345501885981e-2)*1im
  #        W = (1.1264034015766962e-3) + (9.1672826634493694e-6)*1im
  #        Z = (9.9996688370298181e-1) + (8.1382736097585021e-3)*1im
  #        W = (5.1496929311266781e-4) + (1.6828261686953906e-6)*1im
  #        Z = (9.9999466072397813e-1) + (3.2678010245045879e-3)*1im
  #        W = (2.9315283498838234e-4) + (2.5500445397162234e-7)*1im
  #        Z = (9.9999962166450085e-1) + (8.6986829755370020e-4)*1im
end

#=

case(19)
select case(e)
!------------------------   n = 19, S = 1.0e+06, rate = 8.79e-6  ------------------------
case(0)
W = (-8.7856263509822341e-6) + (0.0000000000000000e0)*1im
case(1)
W = (-2.7533033809645487e-4) + (2.2621346757974878e-7)*1im
Z = (-9.9999966248073835e-1) + (8.2160721114975059e-4)*1im
case(2)
W = (-4.6118603432212222e-4) + (1.3925261592991639e-6)*1im
Z = (-9.9999544150519182e-1) + (3.0194318731327206e-3)*1im
case(3)
W = (-9.5833058171406892e-4) + (6.9532666952665401e-6)*1im
Z = (-9.9997367914835145e-1) + (7.2554124975633188e-3)*1im
case(4)
W = (-2.1020598481729306e-3) + (3.4453656580025595e-5)*1im
Z = (-9.9986570402423880e-1) + (1.6388224922586798e-2)*1im
case(5)
W = (-4.6611742142131079e-3) + (1.7059346848525883e-4)*1im
Z = (-9.9933093531757289e-1) + (3.6574331399287265e-2)*1im
case(6)
W = (-1.0327323497992511e-2) + (8.4288136091312184e-4)*1im
Z = (-9.9668591057932443e-1) + (8.1346147128569815e-2)*1im
case(7)
W = (-2.2535809698160335e-2) + (4.1214125318933632e-3)*1im
Z = (-9.8368506773852704e-1) + (1.7989910368940024e-1)*1im
case(8)
W = (-4.5528622674970827e-2) + (1.9161285626193562e-2)*1im
Z = (-9.2169806995615144e-1) + (3.8790806622072871e-1)*1im
case(9)
W = (-6.3225124338941188e-2) + (7.1180657114887469e-2)*1im
Z = (-6.6409053886766345e-1) + (7.4765216256388667e-1)*1im
case(10)
W = (0.0000000000000000e0) + (1.2733920084458664e-1)*1im
Z = (0.0000000000000000e0) + (1.0000000000000000e0)*1im
case(11)
W = (6.3225124338941188e-2) + (7.1180657114887469e-2)*1im
Z = (6.6409053886766345e-1) + (7.4765216256388667e-1)*1im
case(12)
W = (4.5528622674970827e-2) + (1.9161285626193562e-2)*1im
Z = (9.2169806995615144e-1) + (3.8790806622072871e-1)*1im
case(13)
W = (2.2535809698160335e-2) + (4.1214125318933632e-3)*1im
Z = (9.8368506773852704e-1) + (1.7989910368940024e-1)*1im
case(14)
W = (1.0327323497992511e-2) + (8.4288136091312184e-4)*1im
Z = (9.9668591057932443e-1) + (8.1346147128569815e-2)*1im
case(15)
W = (4.6611742142131079e-3) + (1.7059346848525883e-4)*1im
Z = (9.9933093531757289e-1) + (3.6574331399287265e-2)*1im
case(16)
W = (2.1020598481729306e-3) + (3.4453656580025595e-5)*1im
Z = (9.9986570402423880e-1) + (1.6388224922586798e-2)*1im
case(17)
W = (9.5833058171406892e-4) + (6.9532666952665401e-6)*1im
Z = (9.9997367914835145e-1) + (7.2554124975633188e-3)*1im
case(18)
W = (4.6118603432212222e-4) + (1.3925261592991639e-6)*1im
Z = (9.9999544150519182e-1) + (3.0194318731327206e-3)*1im
case(19)
W = (2.7533033809645487e-4) + (2.2621346757974878e-7)*1im
Z = (9.9999966248073835e-1) + (8.2160721114975059e-4)*1im

end select
=#

if( n == 20 )
  W[1] = (-2.5962796229929012e-4) + (2.0212559742093714e-7)*1im;
  Z[1] = (-9.9999969695333402e-1) + (7.7851990354738988e-4)*1im;
  W[2] = (-4.1698448757787214e-4) + (1.1706511996116833e-6)*1im;
  Z[2] = (-9.9999605921566304e-1) + (2.8074103982325071e-3)*1im;
  W[3] = (-8.2705236048838100e-4) + (5.4072170850776506e-6)*1im;
  Z[3] = (-9.9997862836826079e-1) + (6.5377983092055223e-3)*1im;
  W[4] = (-1.7381938336616146e-3) + (2.4737279114863458e-5)*1im;
  Z[4] = (-9.9989874616333840e-1) + (1.4230158853077478e-2)*1im;
  W[5] = (-3.7009135179021358e-3) + (1.1308608306421491e-4)*1im;
  Z[5] = (-9.9953348408535481e-1) + (3.0542007009883753e-2)*1im;
  W[6] = (-7.8878951883124531e-3) + (5.1628614143891432e-4)*1im;
  Z[6] = (-9.9786481263071369e-1) + (6.5313212395890802e-2)*1im;
  W[7] = (-1.6672937540863027e-2) + (2.3430845492115548e-3)*1im;
  Z[7] = (-9.9026924750858347e-1) + (1.3916471333920749e-1)*1im;
  W[8] = (-3.3840259241556050e-2) + (1.0351956600694706e-2)*1im;
  Z[8] = (-9.5625772508631735e-1) + (2.9252549156054936e-1)*1im;
  W[9] = (-5.7167528994478772e-2) + (4.0725963384848053e-2)*1im;
  Z[9] = (-8.1446048593714426e-1) + (5.8021902489235122e-1)*1im;
  W[10] = (-4.0893404149440093e-2) + (1.0505409220020073e-1)*1im;
  Z[10] = (-3.6274701643022350e-1) + (9.3188765528413942e-1)*1im;
  Z = [Z[1:Integer(end/2)];-conj(Z[1:Integer(end/2)])];
  W = [W[1:Integer(end/2)];-conj(W[1:Integer(end/2)])];

  #        W = (4.0893404149440093e-2) + (1.0505409220020073e-1)*1im
  #        Z = (3.6274701643022350e-1) + (9.3188765528413942e-1)*1im
  #        W = (5.7167528994478772e-2) + (4.0725963384848053e-2)*1im
  #        Z = (8.1446048593714426e-1) + (5.8021902489235122e-1)*1im
  #        W = (3.3840259241556050e-2) + (1.0351956600694706e-2)*1im
  #        Z = (9.5625772508631735e-1) + (2.9252549156054936e-1)*1im
  #        W = (1.6672937540863027e-2) + (2.3430845492115548e-3)*1im
  #        Z = (9.9026924750858347e-1) + (1.3916471333920749e-1)*1im
  #        W = (7.8878951883124531e-3) + (5.1628614143891432e-4)*1im
  #        Z = (9.9786481263071369e-1) + (6.5313212395890802e-2)*1im
  #        W = (3.7009135179021358e-3) + (1.1308608306421491e-4)*1im
  #        Z = (9.9953348408535481e-1) + (3.0542007009883753e-2)*1im
  #        W = (1.7381938336616146e-3) + (2.4737279114863458e-5)*1im
  #        Z = (9.9989874616333840e-1) + (1.4230158853077478e-2)*1im
  #        W = (8.2705236048838100e-4) + (5.4072170850776506e-6)*1im
  #        Z = (9.9997862836826079e-1) + (6.5377983092055223e-3)*1im
  #        W = (4.1698448757787214e-4) + (1.1706511996116833e-6)*1im
  #        Z = (9.9999605921566304e-1) + (2.8074103982325071e-3)*1im
  #        W = (2.5962796229929012e-4) + (2.0212559742093714e-7)*1im
  #        Z = (9.9999969695333402e-1) + (7.7851990354738988e-4)*1im

end

#=

case(24)
select case(e)
!------------------------   n = 24, S = 1.0e+06, rate = 3.42e-7  ------------------------
case(0)
W = (3.4195439124751204e-7) + (0.0000000000000000e0)*1im
case(1)
W = (-2.1181634653955156e-4) + (1.3642042022693250e-7)*1im
Z = (-9.9999979259955241e-1) + (6.4405034909712736e-4)*1im
case(2)
W = (-2.9967697189998575e-4) + (6.5908154932765347e-7)*1im
Z = (-9.9999758153396057e-1) + (2.1993013049440135e-3)*1im
case(3)
W = (-5.1183808502514039e-4) + (2.3886747768399971e-6)*1im
Z = (-9.9998911040415039e-1) + (4.6668054508287246e-3)*1im
case(4)
W = (-9.3627144787552391e-4) + (8.4923098454392950e-6)*1im
Z = (-9.9995886691232916e-1) + (9.0699770347401325e-3)*1im
case(5)
W = (-1.7488125466066549e-3) + (3.0144437200358861e-5)*1im
Z = (-9.9985147448075562e-1) + (1.7234528675274002e-2)*1im
case(6)
W = (-3.2850345253919943e-3) + (1.0695910797948011e-4)*1im
Z = (-9.9947036029338032e-1) + (3.2542263212942395e-2)*1im
case(7)
W = (-6.1723524218736843e-3) + (3.7914247632396840e-4)*1im
Z = (-9.9811874967746239e-1) + (6.1310370593392005e-2)*1im
case(8)
W = (-1.1543217321753032e-2) + (1.3393451640454283e-3)*1im
Z = (-9.9333587640998278e-1) + (1.1525552757595411e-1)*1im
case(9)
W = (-2.1198732713608778e-2) + (4.6742873692502919e-3)*1im
Z = (-9.7654221667714647e-1) + (2.1532602965569464e-1)*1im
case(10)
W = (-3.6499594635495881e-2) + (1.5642032244570850e-2)*1im
Z = (-9.1915096715797229e-1) + (3.9390544496435209e-1)*1im
case(11)
W = (-5.0178640072982915e-2) + (4.5631281558717415e-2)*1im
Z = (-7.3983485714849262e-1) + (6.7278851368618764e-1)*1im
case(12)
W = (-2.9413295974037672e-2) + (9.1338388378113380e-2)*1im
Z = (-3.0652417782216373e-1) + (9.5186287269251479e-1)*1im
case(13)
W = (2.9413295974037672e-2) + (9.1338388378113380e-2)*1im
Z = (3.0652417782216373e-1) + (9.5186287269251479e-1)*1im
case(14)
W = (5.0178640072982915e-2) + (4.5631281558717415e-2)*1im
Z = (7.3983485714849262e-1) + (6.7278851368618764e-1)*1im
case(15)
W = (3.6499594635495881e-2) + (1.5642032244570850e-2)*1im
Z = (9.1915096715797229e-1) + (3.9390544496435209e-1)*1im
case(16)
W = (2.1198732713608778e-2) + (4.6742873692502919e-3)*1im
Z = (9.7654221667714647e-1) + (2.1532602965569464e-1)*1im
case(17)
W = (1.1543217321753032e-2) + (1.3393451640454283e-3)*1im
Z = (9.9333587640998278e-1) + (1.1525552757595411e-1)*1im
case(18)
W = (6.1723524218736843e-3) + (3.7914247632396840e-4)*1im
Z = (9.9811874967746239e-1) + (6.1310370593392005e-2)*1im
case(19)
W = (3.2850345253919943e-3) + (1.0695910797948011e-4)*1im
Z = (9.9947036029338032e-1) + (3.2542263212942395e-2)*1im
case(20)
W = (1.7488125466066549e-3) + (3.0144437200358861e-5)*1im
Z = (9.9985147448075562e-1) + (1.7234528675274002e-2)*1im
case(21)
W = (9.3627144787552391e-4) + (8.4923098454392950e-6)*1im
Z = (9.9995886691232916e-1) + (9.0699770347401325e-3)*1im
case(22)
W = (5.1183808502514039e-4) + (2.3886747768399971e-6)*1im
Z = (9.9998911040415039e-1) + (4.6668054508287246e-3)*1im
case(23)
W = (2.9967697189998575e-4) + (6.5908154932765347e-7)*1im
Z = (9.9999758153396057e-1) + (2.1993013049440135e-3)*1im
case(24)
W = (2.1181634653955156e-4) + (1.3642042022693250e-7)*1im
Z = (9.9999979259955241e-1) + (6.4405034909712736e-4)*1im

end select

case(32)
select case(e)
!------------------------   n = 32, S = 1.0e+06, rate = 1.90e-9  ------------------------

case(0)
W = (1.8979331439794578e-9) + (0.0000000000000000e0)*1im
case(1)
W = (-1.5550096942091167e-4) + (7.4568333949205092e-8)*1im
Z = (-9.9999988502256720e-1) + (4.7953608040659319e-4)*1im
case(2)
W = (-1.9125886486417976e-4) + (2.9623720666278892e-7)*1im
Z = (-9.9999880048610534e-1) + (1.5488790625862625e-3)*1im
case(3)
W = (-2.7099661183330458e-4) + (8.0605312562603178e-7)*1im
Z = (-9.9999557649285342e-1) + (2.9743898072795362e-3)*1im
case(4)
W = (-4.1304701073769696e-4) + (2.0998990288487514e-6)*1im
Z = (-9.9998707711605239e-1) + (5.0838568916089094e-3)*1im
case(5)
W = (-6.5006154636645475e-4) + (5.4362011418772597e-6)*1im
Z = (-9.9996503534011050e-1) + (8.3623021502164611e-3)*1im
case(6)
W = (-1.0364859003141988e-3) + (1.4059533908081803e-5)*1im
Z = (-9.9990801328136669e-1) + (1.3563368892360707e-2)*1im
case(7)
W = (-1.6609433872528264e-3) + (3.6353506595416335e-5)*1im
Z = (-9.9976055985963364e-1) + (2.1882023424535879e-2)*1im
case(8)
W = (-2.6660466676895595e-3) + (9.3974723940552110e-5)*1im
Z = (-9.9937934228484449e-1) + (3.5226839402814472e-2)*1im
case(9)
W = (-4.2788366843057764e-3) + (2.4277947085922267e-4)*1im
Z = (-9.9839418530232105e-1) + (5.6648484132539159e-2)*1im
case(10)
W = (-6.8532505272229727e-3) + (6.2623118524491391e-4)*1im
Z = (-9.9585106320729500e-1) + (9.0998131348397709e-2)*1im
case(11)
W = (-1.0911580563159749e-2) + (1.6088283108259130e-3)*1im
Z = (-9.8930445916784426e-1) + (1.4586530454710303e-1)*1im
case(12)
W = (-1.7104615708036029e-2) + (4.0907396646800102e-3)*1im
Z = (-9.7257239010253027e-1) + (2.3260039984972461e-1)*1im
case(13)
W = (-2.5757868072832625e-2) + (1.0133058423378140e-2)*1im
Z = (-9.3058053398093854e-1) + (3.6608724339390908e-1)*1im
case(14)
W = (-3.5010080480968125e-2) + (2.3540889306474175e-2)*1im
Z = (-8.2984664904974115e-1) + (5.5799152239161820e-1)*1im
case(15)
W = (-3.6601603226060431e-2) + (4.7261950405979622e-2)*1im
Z = (-6.1229562589776565e-1) + (7.9062890568614008e-1)*1im
case(16)
W = (-1.7142800732740431e-2) + (7.1497358882311499e-2)*1im
Z = (-2.3315991108837755e-1) + (9.7243840723269459e-1)*1im
case(17)
W = (1.7142800732740431e-2) + (7.1497358882311499e-2)*1im
Z = (2.3315991108837755e-1) + (9.7243840723269459e-1)*1im
case(18)
W = (3.6601603226060431e-2) + (4.7261950405979622e-2)*1im
Z = (6.1229562589776565e-1) + (7.9062890568614008e-1)*1im
case(19)
W = (3.5010080480968125e-2) + (2.3540889306474175e-2)*1im
Z = (8.2984664904974115e-1) + (5.5799152239161820e-1)*1im
case(20)
W = (2.5757868072832625e-2) + (1.0133058423378140e-2)*1im
Z = (9.3058053398093854e-1) + (3.6608724339390908e-1)*1im
case(21)
W = (1.7104615708036029e-2) + (4.0907396646800102e-3)*1im
Z = (9.7257239010253027e-1) + (2.3260039984972461e-1)*1im
case(22)
W = (1.0911580563159749e-2) + (1.6088283108259130e-3)*1im
Z = (9.8930445916784426e-1) + (1.4586530454710303e-1)*1im
case(23)
W = (6.8532505272229727e-3) + (6.2623118524491391e-4)*1im
Z = (9.9585106320729500e-1) + (9.0998131348397709e-2)*1im
case(24)
W = (4.2788366843057764e-3) + (2.4277947085922267e-4)*1im
Z = (9.9839418530232105e-1) + (5.6648484132539159e-2)*1im
case(25)
W = (2.6660466676895595e-3) + (9.3974723940552110e-5)*1im
Z = (9.9937934228484449e-1) + (3.5226839402814472e-2)*1im
case(26)
W = (1.6609433872528264e-3) + (3.6353506595416335e-5)*1im
Z = (9.9976055985963364e-1) + (2.1882023424535879e-2)*1im
case(27)
W = (1.0364859003141988e-3) + (1.4059533908081803e-5)*1im
Z = (9.9990801328136669e-1) + (1.3563368892360707e-2)*1im
case(28)
W = (6.5006154636645475e-4) + (5.4362011418772597e-6)*1im
Z = (9.9996503534011050e-1) + (8.3623021502164611e-3)*1im
case(29)
W = (4.1304701073769696e-4) + (2.0998990288487514e-6)*1im
Z = (9.9998707711605239e-1) + (5.0838568916089094e-3)*1im
case(30)
W = (2.7099661183330458e-4) + (8.0605312562603178e-7)*1im
Z = (9.9999557649285342e-1) + (2.9743898072795362e-3)*1im
case(31)
W = (1.9125886486417976e-4) + (2.9623720666278892e-7)*1im
Z = (9.9999880048610534e-1) + (1.5488790625862625e-3)*1im
case(32)
W = (1.5550096942091167e-4) + (7.4568333949205092e-8)*1im
Z = (9.9999988502256720e-1) + (4.7953608040659319e-4)*1im

end select

case(40)
select case(e)
!------------------------   n = 40, S = 1.0e+06, rate = 1.05e-11  ------------------------
case(0)
W = (1.0534184635702104e-11) + (0.0000000000000000e0)*1im
case(1)
W = (-1.2316275157188810e-4) + (4.7089605228083172e-8)*1im
Z = (-9.9999992690943984e-1) + (3.8233638973867931e-4)*1im
case(2)
W = (-1.4116668350821769e-4) + (1.6980940530390962e-7)*1im
Z = (-9.9999927651656551e-1) + (1.2028991418483869e-3)*1im
case(3)
W = (-1.7980618314001243e-4) + (3.9544892959663819e-7)*1im
Z = (-9.9999758153396057e-1) + (2.1993013049440135e-3)*1im
case(4)
W = (-2.4472892912057994e-4) + (8.6076449501894848e-7)*1im
Z = (-9.9999381465333870e-1) + (3.5171942033385439e-3)*1im
case(5)
W = (-3.4542323313400009e-4) + (1.8477702426206758e-6)*1im
Z = (-9.9998569283677707e-1) + (5.3492169287681366e-3)*1im
case(6)
W = (-4.9660191456992288e-4) + (3.9546355655326282e-6)*1im
Z = (-9.9996829370430018e-1) + (7.9631392120442512e-3)*1im
case(7)
W = (-7.2034321732435960e-4) + (8.4580968155409520e-6)*1im
Z = (-9.9993107266903825e-1) + (1.1740950172220682e-2)*1im
case(8)
W = (-1.0492875279641152e-3) + (1.8086662320217425e-5)*1im
Z = (-9.9985147448075562e-1) + (1.7234528675274002e-2)*1im
case(9)
W = (-1.5313128096202157e-3) + (3.8671524949083791e-5)*1im
Z = (-9.9968127428812481e-1) + (2.5245788513552012e-2)*1im
case(10)
W = (-2.2362336254057352e-3) + (8.2667929440118144e-5)*1im
Z = (-9.9931740213171416e-1) + (3.6942249481074656e-2)*1im
case(11)
W = (-3.2650710840920827e-3) + (1.7664539309457733e-4)*1im
Z = (-9.9853971624832527e-1) + (5.4022542282959551e-2)*1im
case(12)
W = (-4.7619077018430604e-3) + (3.7712311473102285e-4)*1im
Z = (-9.9687868716768624e-1) + (7.8948610316017617e-2)*1im
case(13)
W = (-6.9259303930526263e-3) + (8.0360709842735071e-4)*1im
Z = (-9.9333587640998278e-1) + (1.1525552757595411e-1)*1im
case(14)
W = (-1.0012930025861545e-2) + (1.7055229265479789e-3)*1im
Z = (-9.8580171086824264e-1) + (1.6791362913487898e-1)*1im
case(15)
W = (-1.4290148409187728e-2) + (3.5889567295828485e-3)*1im
Z = (-9.6987971939271034e-1) + (2.4358433839374305e-1)*1im
case(16)
W = (-1.9841360316044478e-2) + (7.4183032266655303e-3)*1im
Z = (-9.3667338807504108e-1) + (3.5020417483522881e-1)*1im
case(17)
W = (-2.5990647961833875e-2) + (1.4779087936364021e-2)*1im
Z = (-8.6928879621566224e-1) + (4.9430455063040313e-1)*1im
case(18)
W = (-3.0107184043793259e-2) + (2.7378768935233638e-2)*1im
Z = (-7.3983485714849262e-1) + (6.7278851368618764e-1)*1im
case(19)
W = (-2.6715352778149914e-2) + (4.4418199008234377e-2)*1im
Z = (-5.1540949947995840e-1) + (8.5694401675128040e-1)*1im
case(20)
W = (-1.1155359122546567e-2) + (5.8353572671816387e-2)*1im
Z = (-1.8776815977090994e-1) + (9.8221337711122936e-1)*1im
case(21)
W = (1.1155359122546567e-2) + (5.8353572671816387e-2)*1im
Z = (1.8776815977090994e-1) + (9.8221337711122936e-1)*1im
case(22)
W = (2.6715352778149914e-2) + (4.4418199008234377e-2)*1im
Z = (5.1540949947995840e-1) + (8.5694401675128040e-1)*1im
case(23)
W = (3.0107184043793259e-2) + (2.7378768935233638e-2)*1im
Z = (7.3983485714849262e-1) + (6.7278851368618764e-1)*1im
case(24)
W = (2.5990647961833875e-2) + (1.4779087936364021e-2)*1im
Z = (8.6928879621566224e-1) + (4.9430455063040313e-1)*1im
case(25)
W = (1.9841360316044478e-2) + (7.4183032266655303e-3)*1im
Z = (9.3667338807504108e-1) + (3.5020417483522881e-1)*1im
case(26)
W = (1.4290148409187728e-2) + (3.5889567295828485e-3)*1im
Z = (9.6987971939271034e-1) + (2.4358433839374305e-1)*1im
case(27)
W = (1.0012930025861545e-2) + (1.7055229265479789e-3)*1im
Z = (9.8580171086824264e-1) + (1.6791362913487898e-1)*1im
case(28)
W = (6.9259303930526263e-3) + (8.0360709842735071e-4)*1im
Z = (9.9333587640998278e-1) + (1.1525552757595411e-1)*1im
case(29)
W = (4.7619077018430604e-3) + (3.7712311473102285e-4)*1im
Z = (9.9687868716768624e-1) + (7.8948610316017617e-2)*1im
case(30)
W = (3.2650710840920827e-3) + (1.7664539309457733e-4)*1im
Z = (9.9853971624832527e-1) + (5.4022542282959551e-2)*1im
case(31)
W = (2.2362336254057352e-3) + (8.2667929440118144e-5)*1im
Z = (9.9931740213171416e-1) + (3.6942249481074656e-2)*1im
case(32)
W = (1.5313128096202157e-3) + (3.8671524949083791e-5)*1im
Z = (9.9968127428812481e-1) + (2.5245788513552012e-2)*1im
case(33)
W = (1.0492875279641152e-3) + (1.8086662320217425e-5)*1im
Z = (9.9985147448075562e-1) + (1.7234528675274002e-2)*1im
case(34)
W = (7.2034321732435960e-4) + (8.4580968155409520e-6)*1im
Z = (9.9993107266903825e-1) + (1.1740950172220682e-2)*1im
case(35)
W = (4.9660191456992288e-4) + (3.9546355655326282e-6)*1im
Z = (9.9996829370430018e-1) + (7.9631392120442512e-3)*1im
case(36)
W = (3.4542323313400009e-4) + (1.8477702426206758e-6)*1im
Z = (9.9998569283677707e-1) + (5.3492169287681366e-3)*1im
case(37)
W = (2.4472892912057994e-4) + (8.6076449501894848e-7)*1im
Z = (9.9999381465333870e-1) + (3.5171942033385439e-3)*1im
case(38)
W = (1.7980618314001243e-4) + (3.9544892959663819e-7)*1im
Z = (9.9999758153396057e-1) + (2.1993013049440135e-3)*1im
case(39)
W = (1.4116668350821769e-4) + (1.6980940530390962e-7)*1im
Z = (9.9999927651656551e-1) + (1.2028991418483869e-3)*1im
case(40)
W = (1.2316275157188810e-4) + (4.7089605228083172e-8)*1im
Z = (9.9999992690943984e-1) + (3.8233638973867931e-4)*1im

end select

case(48)
select case(e)
!------------------------   n = 48, S = 1.0e+06, rate = 5.86e-14  ------------------------
case(0)
W = (5.8564264548977008e-14) + (0.0000000000000000e0)*1im
case(1)
W = (-1.0207666216851158e-4) + (3.2463391730449853e-8)*1im
Z = (-9.9999994942861647e-1) + (3.1802950252369703e-4)*1im
case(2)
W = (-1.1240090501800907e-4) + (1.1085597222977934e-7)*1im
Z = (-9.9999951365073736e-1) + (9.8625467737379399e-4)*1im
case(3)
W = (-1.3409354176612142e-4) + (2.3523148062411967e-7)*1im
Z = (-9.9999846133468917e-1) + (1.7542315281065331e-3)*1im
case(4)
W = (-1.6934841332539216e-4) + (4.5718038200357800e-7)*1im
Z = (-9.9999635598178738e-1) + (2.6996338911749935e-3)*1im
case(5)
W = (-2.2173073497403943e-4) + (8.6876512741129979e-7)*1im
Z = (-9.9999232430038310e-1) + (3.9180786512652823e-3)*1im
case(6)
W = (-2.9653714734123929e-4) + (1.6407037867488601e-6)*1im
Z = (-9.9998469398327960e-1) + (5.5327930710187233e-3)*1im
case(7)
W = (-4.0133002060141742e-4) + (3.0931700215852137e-6)*1im
Z = (-9.9997030010278387e-1) + (7.7070689855653837e-3)*1im
case(8)
W = (-5.4669867904386235e-4) + (5.8285515797031256e-6)*1im
Z = (-9.9994317254293952e-1) + (1.0660754418012596e-2)*1im
case(9)
W = (-7.4732098511848374e-4) + (1.0981144405960075e-5)*1im
Z = (-9.9989206046724421e-1) + (1.4692427116338712e-2)*1im
case(10)
W = (-1.0234234700723219e-3) + (2.0687031904371676e-5)*1im
Z = (-9.9979576857513042e-1) + (2.0209431938186778e-2)*1im
case(11)
W = (-1.4027639654332564e-3) + (3.8967906914869193e-5)*1im
Z = (-9.9961437632196248e-1) + (2.7768663101670359e-2)*1im
case(12)
W = (-1.9232760208135970e-3) + (7.3391372929623125e-5)*1im
Z = (-9.9927271804559314e-1) + (3.8131810470687581e-2)*1im
case(13)
W = (-2.6364847596615638e-3) + (1.3818198636717554e-4)*1im
Z = (-9.9862934321886543e-1) + (5.2339610834027481e-2)*1im
case(14)
W = (-3.6116342963176664e-3) + (2.6002245345124118e-4)*1im
Z = (-9.9741833534204793e-1) + (7.1809918002307196e-2)*1im
case(15)
W = (-4.9399091552901342e-3) + (4.8877131695888886e-4)*1im
Z = (-9.9514075291324122e-1) + (9.8462591329232213e-2)*1im
case(16)
W = (-6.7365640264612303e-3) + (9.1691183433771899e-4)*1im
Z = (-9.9086381972995408e-1) + (1.3486619572808120e-1)*1im
case(17)
W = (-9.1347263363820881e-3) + (1.7136119529180878e-3)*1im
Z = (-9.8285560240079251e-1) + (1.8437696393360870e-1)*1im
case(18)
W = (-1.2254995789377469e-2) + (3.1800985784853704e-3)*1im
Z = (-9.6794166373051727e-1) + (2.5117510946468846e-1)*1im
case(19)
W = (-1.6114837644635151e-2) + (5.8251138870511737e-3)*1im
Z = (-9.4044447327612435e-1) + (3.3994733810458494e-1)*1im
case(20)
W = (-2.0411160688551314e-2) + (1.0418873853406705e-2)*1im
Z = (-8.9067323792710495e-1) + (4.5464401815095595e-1)*1im
case(21)
W = (-2.4109735989028633e-2) + (1.7861402690121105e-2)*1im
Z = (-8.0351990684851848e-1) + (5.9527788409964322e-1)*1im
case(22)
W = (-2.4987503287012616e-2) + (2.8488189217690068e-2)*1im
Z = (-6.5940595627400689e-1) + (7.5178706082930324e-1)*1im
case(23)
W = (-1.9994108730100320e-2) + (4.0545468570332463e-2)*1im
Z = (-4.4227617016573273e-1) + (8.9687891563105204e-1)*1im
case(24)
W = (-7.8174922097638110e-3) + (4.9162005002073170e-2)*1im
Z = (-1.5704185299553061e-1) + (9.8759194833075170e-1)*1im
case(25)
W = (7.8174922097638110e-3) + (4.9162005002073170e-2)*1im
Z = (1.5704185299553061e-1) + (9.8759194833075170e-1)*1im
case(26)
W = (1.9994108730100320e-2) + (4.0545468570332463e-2)*1im
Z = (4.4227617016573273e-1) + (8.9687891563105204e-1)*1im
case(27)
W = (2.4987503287012616e-2) + (2.8488189217690068e-2)*1im
Z = (6.5940595627400689e-1) + (7.5178706082930324e-1)*1im
case(28)
W = (2.4109735989028633e-2) + (1.7861402690121105e-2)*1im
Z = (8.0351990684851848e-1) + (5.9527788409964322e-1)*1im
case(29)
W = (2.0411160688551314e-2) + (1.0418873853406705e-2)*1im
Z = (8.9067323792710495e-1) + (4.5464401815095595e-1)*1im
case(30)
W = (1.6114837644635151e-2) + (5.8251138870511737e-3)*1im
Z = (9.4044447327612435e-1) + (3.3994733810458494e-1)*1im
case(31)
W = (1.2254995789377469e-2) + (3.1800985784853704e-3)*1im
Z = (9.6794166373051727e-1) + (2.5117510946468846e-1)*1im
case(32)
W = (9.1347263363820881e-3) + (1.7136119529180878e-3)*1im
Z = (9.8285560240079251e-1) + (1.8437696393360870e-1)*1im
case(33)
W = (6.7365640264612303e-3) + (9.1691183433771899e-4)*1im
Z = (9.9086381972995408e-1) + (1.3486619572808120e-1)*1im
case(34)
W = (4.9399091552901342e-3) + (4.8877131695888886e-4)*1im
Z = (9.9514075291324122e-1) + (9.8462591329232213e-2)*1im
case(35)
W = (3.6116342963176664e-3) + (2.6002245345124118e-4)*1im
Z = (9.9741833534204793e-1) + (7.1809918002307196e-2)*1im
case(36)
W = (2.6364847596615638e-3) + (1.3818198636717554e-4)*1im
Z = (9.9862934321886543e-1) + (5.2339610834027481e-2)*1im
case(37)
W = (1.9232760208135970e-3) + (7.3391372929623125e-5)*1im
Z = (9.9927271804559314e-1) + (3.8131810470687581e-2)*1im
case(38)
W = (1.4027639654332564e-3) + (3.8967906914869193e-5)*1im
Z = (9.9961437632196248e-1) + (2.7768663101670359e-2)*1im
case(39)
W = (1.0234234700723219e-3) + (2.0687031904371676e-5)*1im
Z = (9.9979576857513042e-1) + (2.0209431938186778e-2)*1im
case(40)
W = (7.4732098511848374e-4) + (1.0981144405960075e-5)*1im
Z = (9.9989206046724421e-1) + (1.4692427116338712e-2)*1im
case(41)
W = (5.4669867904386235e-4) + (5.8285515797031256e-6)*1im
Z = (9.9994317254293952e-1) + (1.0660754418012596e-2)*1im
case(42)
W = (4.0133002060141742e-4) + (3.0931700215852137e-6)*1im
Z = (9.9997030010278387e-1) + (7.7070689855653837e-3)*1im
case(43)
W = (2.9653714734123929e-4) + (1.6407037867488601e-6)*1im
Z = (9.9998469398327960e-1) + (5.5327930710187233e-3)*1im
case(44)
W = (2.2173073497403943e-4) + (8.6876512741129979e-7)*1im
Z = (9.9999232430038310e-1) + (3.9180786512652823e-3)*1im
case(45)
W = (1.6934841332539216e-4) + (4.5718038200357800e-7)*1im
Z = (9.9999635598178738e-1) + (2.6996338911749935e-3)*1im
case(46)
W = (1.3409354176612142e-4) + (2.3523148062411967e-7)*1im
Z = (9.9999846133468917e-1) + (1.7542315281065331e-3)*1im
case(47)
W = (1.1240090501800907e-4) + (1.1085597222977934e-7)*1im
Z = (9.9999951365073736e-1) + (9.8625467737379399e-4)*1im
case(48)
W = (1.0207666216851158e-4) + (3.2463391730449853e-8)*1im
Z = (9.9999994942861647e-1) + (3.1802950252369703e-4)*1im

end select

case(56)
select case(e)
!------------------------   n = 56, S = 1.0e+06, rate = 3.33e-16  ------------------------
case(0)
W = (3.3306690738754696e-16) + (0.0000000000000000e0)*1im
case(1)
W = (-8.7205817070324285e-5) + (2.3745715284134577e-8)*1im
Z = (-9.9999996292769566e-1) + (2.7229507390174712e-4)*1im
case(2)
W = (-9.3671614351072461e-5) + (7.8410128732040906e-8)*1im
Z = (-9.9999964965324450e-1) + (8.3707430265302118e-4)*1im
case(3)
W = (-1.0708258208781082e-4) + (1.5676024389248878e-7)*1im
Z = (-9.9999892847206506e-1) + (1.4639175939086017e-3)*1im
case(4)
W = (-1.2843298795715179e-4) + (2.8246352114045597e-7)*1im
Z = (-9.9999758153396057e-1) + (2.1993013049440135e-3)*1im
case(5)
W = (-1.5930565536197383e-4) + (4.9349124722739312e-7)*1im
Z = (-9.9999520196512837e-1) + (3.0977486537930887e-3)*1im
case(6)
W = (-2.0198918524432522e-4) + (8.5358791379195321e-7)*1im
Z = (-9.9999107096619733e-1) + (4.2258712566424312e-3)*1im
case(7)
W = (-2.5964733455728753e-4) + (1.4715244324426047e-6)*1im
Z = (-9.9998394069450114e-1) + (5.6673056293498336e-3)*1im
case(8)
W = (-3.3655289344432129e-4) + (2.5339479248362398e-6)*1im
Z = (-9.9997165736303506e-1) + (7.5289089929910773e-3)*1im
case(9)
W = (-4.3840291264360994e-4) + (4.3617417606661609e-6)*1im
Z = (-9.9995051075810371e-1) + (9.9486699918864014e-3)*1im
case(10)
W = (-5.7273754900334433e-4) + (7.5068928472465222e-6)*1im
Z = (-9.9991411384269779e-1) + (1.3105912336512029e-2)*1im
case(11)
W = (-7.4949109140293982e-4) + (1.2919044514441024e-5)*1im
Z = (-9.9985147448075562e-1) + (1.7234528675274002e-2)*1im
case(12)
W = (-9.8171039842577979e-4) + (2.2231801069936816e-5)*1im
Z = (-9.9974367821792698e-1) + (2.2640182492421911e-2)*1im
case(13)
W = (-1.2864813335720538e-3) + (3.8254542936105366e-5)*1im
Z = (-9.9955818427461929e-1) + (2.9722655494190512e-2)*1im
case(14)
W = (-1.6861033154932578e-3) + (6.5816167525760470e-5)*1im
Z = (-9.9923902416215127e-1) + (3.9004776521238349e-2)*1im
case(15)
W = (-2.2095347979132953e-3) + (1.1320927252413921e-4)*1im
Z = (-9.9868997900096679e-1) + (5.1169579273710977e-2)*1im
case(16)
W = (-2.8940726679749628e-3) + (1.9465226644598720e-4)*1im
Z = (-9.9774576269728499e-1) + (6.7107324634595802e-2)*1im
case(17)
W = (-3.7870681227707027e-3) + (3.3445787196232090e-4)*1im
Z = (-9.9612282741186675e-1) + (8.7973363633478865e-2)*1im
case(18)
W = (-4.9470931378947352e-3) + (5.7400507030525071e-4)*1im
Z = (-9.9333587640998278e-1) + (1.1525552757595411e-1)*1im
case(19)
W = (-6.4430868535296079e-3) + (9.8314819657128148e-4)*1im
Z = (-9.8855763469680935e-1) + (1.5084363719643493e-1)*1im
case(20)
W = (-8.3481034040599009e-3) + (1.6781503300194228e-3)*1im
Z = (-9.8038756649065151e-1) + (1.9707922130589609e-1)*1im
case(21)
W = (-1.0720459860124735e-2) + (2.8477287489747280e-3)*1im
Z = (-9.6648274241621845e-1) + (2.5673158865169970e-1)*1im
case(22)
W = (-1.3558373424360122e-2) + (4.7846871211399715e-3)*1im
Z = (-9.4300389145487351e-1) + (3.3278170127121637e-1)*1im
case(23)
W = (-1.6705709101320253e-2) + (7.9063190596150665e-3)*1im
Z = (-9.0388246359439139e-1) + (4.2778089252154988e-1)*1im
case(24)
W = (-1.9687576811665372e-2) + (1.2710819929445210e-2)*1im
Z = (-8.4011858758926650e-1) + (5.4240276436151758e-1)*1im
case(25)
W = (-2.1505131459852337e-2) + (1.9556263525166893e-2)*1im
Z = (-7.3983485714849262e-1) + (6.7278851368618764e-1)*1im
case(26)
W = (-2.0591627499276469e-2) + (2.8132163898937537e-2)*1im
Z = (-5.9064294946629659e-1) + (8.0693302463448213e-1)*1im
case(27)
W = (-1.5387226218949800e-2) + (3.6764252330077289e-2)*1im
Z = (-3.8608554905294024e-1) + (9.2246297964335111e-1)*1im
case(28)
W = (-5.7751262738652926e-3) + (4.2418103286963021e-2)*1im
Z = (-1.3490312400076723e-1) + (9.9085879273226096e-1)*1im
case(29)
W = (5.7751262738652926e-3) + (4.2418103286963021e-2)*1im
Z = (1.3490312400076723e-1) + (9.9085879273226096e-1)*1im
case(30)
W = (1.5387226218949800e-2) + (3.6764252330077289e-2)*1im
Z = (3.8608554905294024e-1) + (9.2246297964335111e-1)*1im
case(31)
W = (2.0591627499276469e-2) + (2.8132163898937537e-2)*1im
Z = (5.9064294946629659e-1) + (8.0693302463448213e-1)*1im
case(32)
W = (2.1505131459852337e-2) + (1.9556263525166893e-2)*1im
Z = (7.3983485714849262e-1) + (6.7278851368618764e-1)*1im
case(33)
W = (1.9687576811665372e-2) + (1.2710819929445210e-2)*1im
Z = (8.4011858758926650e-1) + (5.4240276436151758e-1)*1im
case(34)
W = (1.6705709101320253e-2) + (7.9063190596150665e-3)*1im
Z = (9.0388246359439139e-1) + (4.2778089252154988e-1)*1im
case(35)
W = (1.3558373424360122e-2) + (4.7846871211399715e-3)*1im
Z = (9.4300389145487351e-1) + (3.3278170127121637e-1)*1im
case(36)
W = (1.0720459860124735e-2) + (2.8477287489747280e-3)*1im
Z = (9.6648274241621845e-1) + (2.5673158865169970e-1)*1im
case(37)
W = (8.3481034040599009e-3) + (1.6781503300194228e-3)*1im
Z = (9.8038756649065151e-1) + (1.9707922130589609e-1)*1im
case(38)
W = (6.4430868535296079e-3) + (9.8314819657128148e-4)*1im
Z = (9.8855763469680935e-1) + (1.5084363719643493e-1)*1im
case(39)
W = (4.9470931378947352e-3) + (5.7400507030525071e-4)*1im
Z = (9.9333587640998278e-1) + (1.1525552757595411e-1)*1im
case(40)
W = (3.7870681227707027e-3) + (3.3445787196232090e-4)*1im
Z = (9.9612282741186675e-1) + (8.7973363633478865e-2)*1im
case(41)
W = (2.8940726679749628e-3) + (1.9465226644598720e-4)*1im
Z = (9.9774576269728499e-1) + (6.7107324634595802e-2)*1im
case(42)
W = (2.2095347979132953e-3) + (1.1320927252413921e-4)*1im
Z = (9.9868997900096679e-1) + (5.1169579273710977e-2)*1im
case(43)
W = (1.6861033154932578e-3) + (6.5816167525760470e-5)*1im
Z = (9.9923902416215127e-1) + (3.9004776521238349e-2)*1im
case(44)
W = (1.2864813335720538e-3) + (3.8254542936105366e-5)*1im
Z = (9.9955818427461929e-1) + (2.9722655494190512e-2)*1im
case(45)
W = (9.8171039842577979e-4) + (2.2231801069936816e-5)*1im
Z = (9.9974367821792698e-1) + (2.2640182492421911e-2)*1im
case(46)
W = (7.4949109140293982e-4) + (1.2919044514441024e-5)*1im
Z = (9.9985147448075562e-1) + (1.7234528675274002e-2)*1im
case(47)
W = (5.7273754900334433e-4) + (7.5068928472465222e-6)*1im
Z = (9.9991411384269779e-1) + (1.3105912336512029e-2)*1im
case(48)
W = (4.3840291264360994e-4) + (4.3617417606661609e-6)*1im
Z = (9.9995051075810371e-1) + (9.9486699918864014e-3)*1im
case(49)
W = (3.3655289344432129e-4) + (2.5339479248362398e-6)*1im
Z = (9.9997165736303506e-1) + (7.5289089929910773e-3)*1im
case(50)
W = (2.5964733455728753e-4) + (1.4715244324426047e-6)*1im
Z = (9.9998394069450114e-1) + (5.6673056293498336e-3)*1im
case(51)
W = (2.0198918524432522e-4) + (8.5358791379195321e-7)*1im
Z = (9.9999107096619733e-1) + (4.2258712566424312e-3)*1im
case(52)
W = (1.5930565536197383e-4) + (4.9349124722739312e-7)*1im
Z = (9.9999520196512837e-1) + (3.0977486537930887e-3)*1im
case(53)
W = (1.2843298795715179e-4) + (2.8246352114045597e-7)*1im
Z = (9.9999758153396057e-1) + (2.1993013049440135e-3)*1im
case(54)
W = (1.0708258208781082e-4) + (1.5676024389248878e-7)*1im
Z = (9.9999892847206506e-1) + (1.4639175939086017e-3)*1im
case(55)
W = (9.3671614351072461e-5) + (7.8410128732040906e-8)*1im
Z = (9.9999964965324450e-1) + (8.3707430265302118e-4)*1im
case(56)
W = (8.7205817070324285e-5) + (2.3745715284134577e-8)*1im
Z = (9.9999996292769566e-1) + (2.7229507390174712e-4)*1im

=#

Z = Z[1:Integer(end/2)];
W = -W[1:Integer(end/2)];

if (norm(Z) < eps(Float64))
  error("zoloPts not implemented for this n.")
end

return Z,W
end


# f(x,y)=x'+y'

FXtransposePYtranspose(x::Float64,y::Float64)=Fxpy(x,y)
FXtransposePYtranspose(x::Float64,y)=Fxpy(x,y')

FXtransposePYtranspose(x,y::Float64)=Fxpy(x,y')

FXtransposePYtranspose(x::Array,y::Array)=Fxpy(x',y')

FXtransposePYtranspose_inplace(value,auxvalue,x::Array,y::Array)=Fxpy_inplace(value,auxvalue,x',y')

function DXtransposePYtranspose(derivativeIDX,f_c,faux_c,grad_c,grad_n,x::Array,y::Array)
    if size(x)==(1,1) && derivativeIDX==1
        axpy!(1.0,[sum(grad_c)],grad_n)
    elseif size(y)==(1,1) && derivativeIDX==2
        axpy!(1.0,[sum(grad_c)],grad_n)
    else
        axpy!(1.0,grad_c',grad_n)
    end
end

if PROC=="GPU"
    function FXtransposePYtranspose_inplace(value,auxvalue,x::CudaArray,y::CudaArray)
        if size(x)==(1,1)
            FXPYtranspose_inplace(value,auxvalue,x::CudaArray,y::CudaArray)
        elseif size(y)==(1,1)
            FXtransposePY_inplace(value,auxvalue,x::CudaArray,y::CudaArray)
        else
            CUBLAS.geam!('T','T',1.0,x,1.0,y,value)
        end
    end

    function DXtransposePYtranspose(derivativeIDX,f_c,faux_c,grad_c,grad_n,x::CudaArray,y::CudaArray)
        if derivativeIDX==1
            DXtransposePY(derivativeIDX,f_c,faux_c,grad_c,grad_n,x::CudaArray,y::CudaArray)
        elseif derivativeIDX==2
            DXPYtranspose(derivativeIDX,f_c,faux_c,grad_c,grad_n,x::CudaArray,y::CudaArray)
        end
    end

end

Derivative[FXtransposePYtranspose]=DXtransposePYtranspose
Inplace[FXtransposePYtranspose]=FXtransposePYtranspose_inplace

export FXtransposePYtranspose


# Code to extract parameters from unlabeled parameter vector

# model assumes model parameters = [ Period, K, k, h, M0 ] for each planet followed by each RV offset and jitter
const num_param_per_planet = 5
const num_jitters = 0   # WARNING: Setting this to zero removes jitter from parameter list, setting it to one leads to errors
pl_offset(plid::Integer) = (plid-1)*num_param_per_planet
num_planets(theta::Vector) = floor(Int64,(length(theta)-num_jitters)//num_param_per_planet)  # WARNING: Assumes num_rvoffsets<num_param_per_planet
num_obs_offsets(theta::Vector) = length(theta)-num_planets(theta)*num_param_per_planet-num_jitters
obs_offset(theta::Vector,obsid::Integer = 1) = num_planets(theta)*num_param_per_planet+obsid
jitter_offset(theta::Vector,obsid::Integer = 1) = length(theta)-(num_jitters-1)

# constants defining unit system/scale for modified Jeffrys priors
const P0 = 1.0  # units of days
const K0 = 1.0  # units of m/s
const Jitter0 = 1.0  # units of m/s

set_period(theta::Vector, P; plid::Integer = 1) = theta[1+pl_offset(plid)] = log1p(P/P0)
set_amplitude(theta::Vector, K; plid::Integer = 1) = theta[2+pl_offset(plid)] = log1p(K/K0)
set_ecosw(theta::Vector, h; plid::Integer = 1) = theta[3+pl_offset(plid)] = h
set_esinw(theta::Vector, k; plid::Integer = 1) = theta[4+pl_offset(plid)] = k
set_w_plus_mean_anomaly_at_t0(theta::Vector, wpM0; plid::Integer = 1) = theta[5+pl_offset(plid)] = wpM0
set_rvoffset(theta::Vector, C; obsid::Integer = 1) = theta[obs_offset(theta,obsid)] = C
set_jitter(theta::Vector, jitter; obsid::Integer = 1) = theta[jitter_offset(theta,obsid)] = log1p(jitter/Jitter0)
function set_ew(theta::Vector, e, w; plid::Integer = 1)
  set_ecosw(theta,e*cos(w),plid=plid)
  set_esinw(theta,e*sin(w),plid=plid)
  return
end
function set_ewM0(theta::Vector, e, w, M0; plid::Integer = 1)
  set_ecosw(theta,e*cos(w),plid=plid)
  set_esinw(theta,e*sin(w),plid=plid)
  set_w_plus_mean_anomaly_at_t0(theta,w+M0,plid=plid)
  return
end
function set_PKewM0(theta::Vector, P, K, e, w, M0; plid::Integer = 1)
  set_period(theta,P,plid=plid)
  set_amplitude(theta,K,plid=plid)
  set_ecosw(theta,e*cos(w),plid=plid)
  set_esinw(theta,e*sin(w),plid=plid)
  set_w_plus_mean_anomaly_at_t0(theta,w+M0,plid=plid)
  return
end

extract_period(theta::Vector; plid::Integer = 1) = P0*(exp(theta[1+pl_offset(plid)])-1)
extract_amplitude(theta::Vector; plid::Integer = 1) = K0*(exp(theta[2+pl_offset(plid)])-1)
extract_ecosw(theta::Vector; plid::Integer = 1) = theta[3+pl_offset(plid)]
extract_esinw(theta::Vector; plid::Integer = 1) = theta[4+pl_offset(plid)]
extract_w(theta::Vector; plid::Integer = 1) = atan(extract_esinw(theta,plid), extract_ecosw(theta,plid))
extract_w_plus_mean_anomaly_at_t0(theta::Vector; plid::Integer = 1) = theta[5+pl_offset(plid)]
extract_mean_anomaly_at_t0(theta::Vector; plid::Integer = 1) = extract_w_plus_mean_anomaly_at_t0(theta,plid)-extract_w(theta,plid)
extract_rvoffset(theta::Vector; obsid::Integer = 1) = theta[obs_offset(theta,obsid)]
extract_jitter(theta::Vector; obsid::Integer = 1) = Jitter0*(exp(theta[jitter_offset(theta,obsid)])-1)
function extract_ewM0(theta::Vector; plid::Integer = 1)
  h = extract_ecosw(theta,plid=plid)
  k = extract_esinw(theta,plid=plid)
  wpM0 = extract_w_plus_mean_anomaly_at_t0(theta,plid=plid)
  e = sqrt(h*h+k*k)
  w = atan2(k,h)
  M0 = wpM0-w
  return (e,w,M0)
end

function extract_PKhkM(theta::Vector; plid::Integer = 1)
  local P,K,ecosw,esinw,M0
  P = extract_period(theta, plid=plid)
  K = extract_amplitude(theta, plid=plid)
  ecosw = extract_ecosw(theta, plid=plid)
  esinw = extract_esinw(theta, plid=plid)
  M0 = extract_w_plus_mean_anomaly_at_t0(theta, plid=plid) - atan2(esinw,ecosw)
  return (P,K,ecosw,esinw,M0)
end

function extract_PKewM(theta::Vector; plid::Integer = 1)
  local P,K,e,w,M0
  P = extract_period(theta, plid=plid)
  K = extract_amplitude(theta, plid=plid)
  (e,w,M0) = extract_ewM0(theta, plid=plid)
  return (P,K,e,w,M0)
end

# check if parameters are valid, i.e., e<1
function is_valid(p::Vector)
  local num_pl,j,k,ecc_sq
  num_pl = num_planets(p)
  @assert length(num_pl) >= 1
  for plid in 1:num_pl
    h = extract_ecosw(p,plid=plid)
    k = extract_esinw(p,plid=plid)
    ecc_sq = h*h+k*k
    if !(ecc_sq<1.0)
      return false
    end
  end
  return true
end

export num_planets, num_obs_offsets
export set_period, set_amplitude, set_ecosw, set_esinw, set_w_plus_mean_anomaly_at_t0, set_mean_anomaly_at_t0, set_rvoffset, set_jitter, set_ew, set_ewM0
export extract_period, extract_amplitude, extract_ecosw, extract_esinw, extract_w_plus_mean_anomaly_at_t0, extract_mean_anomaly_at_t0, extract_ewM0, extract_PKhkM, extract_PKewM, extract_rvoffset, extract_jitter
export extract_PKhkM, isvalid


module SA

##export simulated_annealing, random_search, grid_search

using Distributions
using SharedArrays

function grid_search(M, lower, upper, objective::Function)
    max_obj = objective(upper)
    best = lower
    K = length(M)

    ranges = map(i -> lower[i]:(upper[i]-lower[i])/M[i]:upper[i], 1:K)

    @inbounds for el in Base.product(ranges...)
        new_obj = objective(el)
        if new_obj > max_obj
            best, max_obj = el, new_obj
        end
    end

    best, max_obj
end

function simulated_annealing(M::Int, init::T,
                             kernel::Function,
                             objective::Function,
                             temperature::Function = t -> 1/log(t)) where T
    x = Vector{T}(undef,M+1)
    x[1] = init

    old_obj = objective(x[1])
    max_obj = old_obj
    accept = 0
    best_x = x[1]

    obj = Vector{T}(undef,M+1)
    obj[1] = old_obj

    @inbounds for t in 1:M
        proposal = kernel(x[t], temperature(t+1))
        new_obj = objective(proposal)

        if log(rand()) <= (new_obj - old_obj) / temperature(t+1)
            x[t+1] = proposal
            old_obj = new_obj

            if max_obj < new_obj
                max_obj = new_obj
                best_x = proposal
                accept += 1
            end
        else
            x[t+1] = x[t]
        end

        obj[t+1] = old_obj
    end

    max_obj, best_x, accept/(M+1)
    ##max_obj, best_x, obj[end], x[end], accept/(M+1)
end

function random_search(M::Int, init::T, kernel::Function, objective::Function) where T
    max_obj = objective(init)
    x::T = init
    accept = 0

    for t in 1:M
        proposal = kernel(x)
        new_obj = objective(proposal)

        if new_obj > max_obj
            x = proposal
            max_obj = new_obj
            accept += 1
        end
    end

    max_obj, x, accept/(M+1)
end

function test_kernel(x, temp)
    rand(Normal(0, 1), 2)
end

function test_kernel2(x, temp)
    rand(Uniform(-4,4), 2)
end

function test_obj(x)
    pdf(MultivariateNormal([1.0, -0.5], eye(2)), x)
end

function rosenbrock(x)
    -((1 - x[1])^2 + 100*(x[2] - x[1]^2)^2)
end

function himmelblau(x)
  -((x[1]^2 + x[2] - 11)^2 + (x[1] + x[2]^2 - 7)^2)
end

end


include("C:/Users/stefano/Documents/Zhulab/julia_code/model_fit.jl")
include("C:/Users/stefano/Documents/Zhulab/julia_code/models.jl")
include("C:/Users/stefano/Documents/Zhulab/julia_code/helper_functions.jl")
using DifferentialEquations, Flux, Optim, DiffEqFlux, DiffEqSensitivity, Plots
using BlackBoxOptim
using BenchmarkTools
using RecursiveArrayTools
# --------------------------create dummy data-----------------------------------
f = (du,u,p,t) -> bimolecular!(du,u,p,t,dens,cons) # enclose constants

# u0 = [0.0;0.0]
u₀ = [0.0]          # Initial condition
tspan = (0.0,10.0)  # Simulation interval
p = [1e-4,0.5]      # equation parameter. p = [k₁, k₋₁]
dens = [100., 10., 1.]
cons = []

# Setup the ODE problem, then solve
prob = ODEProblem(f,u₀,tspan,p)
sol = solve(prob,Tsit5())
# sol = solve(prob,Tsit5())
plot(sol)
dataset1 = generate_data(sol)

model_opt2 = (1.0e-6,10.0)
# pinit = [2.0e-4,0.6]
pinit = [-4.0,0.0]
t = collect(range(0,stop=10,length=101))
rates = optimization(bimolecular!,dataset1,dens',[],model_opt2,t)

# -------------------------------------------------------------------------------

# u0 = [0.0;0.0]
u₀ = [0.0]          # Initial condition
tspan = (0.0,10.0)  # Simulation interval
p = [1e-4,0.5]      # equation parameter. p = [k₁, k₋₁]
dens_new = [100., 50., 1.]
cons = []

f = (du,u,p,t) -> bimolecular!(du,u,p,t,dens_new,cons) # enclose constants
# Setup the ODE problem, then solve
prob = ODEProblem(f,u₀,tspan,p)
sol = solve(prob,Tsit5())
# sol = solve(prob,Tsit5())
plot!(sol)
dataset3 = generate_data(sol)
dataset_comb = vcat(dataset1,dataset3)

dens_comb = reduce(vcat, [dens', dens_new'])
rates = optimization(bimolecular!,dataset_comb,dens_comb,cons,model_opt2,t)

#----------------------------------------------------------------------------
# try TCR-pMHC only data from Muaz

using CSV
tcr_data = CSV.read("C:/Users/stefano/Documents/Zhulab/julia_code/tcr_only_data.csv")

t, Pa, n_se_tcr = clean_data(tcr_data)
n = Pa2n(Pa)

scatter(t,Pa)
scatter!(t,n)
scatter!(n_se_tcr[:,1],n_se_tcr[:,2],yerror=n_se_tcr[:,3],markercolor=:black)

densities_tcr = [25.0, 38.0, 1.0]
tspan = (0,maximum(t)+1)
u₀ = [0.0]
f = (du,u,p,t) -> bimolecular!(du,u,p,t,densities_tcr,[]) # enclose constants

pinit = [-4.5,-0.7]

rates_tcr = optimization(bimolecular!,n',densities_tcr',[],model_opt2,t)

prob = ODEProblem(f,u₀,tspan,10 .^ rates_tcr)
# prob = remake(prob, p=10 .^ rates)
sol = solve(prob,Rosenbrock23())
plot!(sol,linecolor=:black)
summ = reduce(vcat,sum(sol,dims=1))
h = plot!(sol.t,summ,linecolor=:black)

# prob = ODEProblem(f,u₀,tspan,[1e-4,0.5])

#------------------------------------------------------------------------------
# try TCR-pMHC-CD4 fit with 2-path, 2-step

tcr_cd4_1 = CSV.read("C:/Users/stefano/Documents/Zhulab/julia_code/tcr_cd4_1.csv")
tcr_cd4_2 = CSV.read("C:/Users/stefano/Documents/Zhulab/julia_code/tcr_cd4_2.csv")
tcr_cd4_3 = CSV.read("C:/Users/stefano/Documents/Zhulab/julia_code/tcr_cd4_3.csv")
t_tc1, Pa_tc1, n_se_1 = clean_data(tcr_cd4_1)
t_tc2, Pa_tc2,  n_se_2  = clean_data(tcr_cd4_2)
t_tc3, Pa_tc3,  n_se_3 = clean_data(tcr_cd4_3)

n_tc1 = Pa2n(Pa_tc1)
n_tc2 = Pa2n(Pa_tc2)
n_tc3 = Pa2n(Pa_tc3)

# scatter(t,Pa)
scatter(t_tc1,n_tc1)
scatter!(t_tc2,n_tc2)
scatter!(t_tc3,n_tc3)

densities_tc1 = [15.0, 38.0, 10.0, 1.0] # TCR, pMHC, CD4, Ac
tspan = (0,maximum(t)+1)
u₀ = [0.0, 0.0]

cons_tcr = 10 .^ rates_tcr
# f = (du,u,p,t) -> trimolecular!(du,u,p,t,densities_tc1,cons_tcr)
# rates_tc1 = optimization(trimolecular!,n_tc1',densities_tc1',cons_tcr,model_opt2,t_tc1)

u₀ = [0.0, 0.0, 0.0]
# CD4 rates from Muaz, need to get full CD4 curve
kon2 = 7.01979340915551e-06 / 6
koff2 = 0.4000
cons_tc = vcat(cons_tcr, kon2, koff2)

# cons_tcr_from_mat = [1.4e-4, 0.5]
# cons_tc = vcat(cons_tcr_from_mat, kon2, koff2)

f = (du,u,p,t) -> two_path!(du,u,p,t,densities_tc1,cons_tc) # enclose constants
# p = [9.32e-7,1.81e-1,1.7e-2,5.23e-5]
p = [0.0, 0.0, 0.0, 0.0]
prob = ODEProblem(f,u₀,tspan,p)
# prob = remake(prob, p=10 .^ rates)
sol = solve(prob,Rosenbrock23())
summ = reduce(vcat,sum(sol,dims=1))
plot!(sol.t,summ)

pinit = [1e-2,0.5,1e-2,0.5]
rates_tc1_2paths = optimization(two_path!,n_tc1',densities_tc1',cons_tc,model_opt2,t_tc1)

densities_tc2 = [8.0, 38.0, 17.0, 1.0]
rates_tc2_2paths = optimization(two_path!,n_tc2',densities_tc2',cons_tc,model_opt2,t_tc2)

densities_tc3 = [3.0, 38.0, 22.0, 1.0]
rates_tc3_2paths = optimization(two_path!,n_tc3',densities_tc3',cons_tc,model_opt2,t_tc3)

scatter(n_se_1[:,1],n_se_1[:,2],yerror=n_se_1[:,3],markercolor=:red)
scatter!(n_se_2[:,1],n_se_2[:,2],yerror=n_se_2[:,3],markercolor=:blue)
scatter!(n_se_3[:,1],n_se_3[:,2],yerror=n_se_3[:,3],markercolor=:cyan)

f = (du,u,p,t) -> two_path!(du,u,p,t,densities_tc1,cons_tc)
prob = ODEProblem(f,u₀,tspan,10 .^ rates_tc1_2paths)
sol = solve(prob,Rosenbrock23())
summ1 = reduce(vcat,sum(sol,dims=1))
time = sol.t
plot!(time,summ1,linecolor=:red)

f = (du,u,p,t) -> two_path!(du,u,p,t,densities_tc2,cons_tc)
prob = ODEProblem(f,u₀,tspan,10 .^ rates_tc2_2paths)
sol = solve(prob,Rosenbrock23())
summ = reduce(vcat,sum(sol,dims=1))
time = sol.t
plot!(sol.t,summ,linecolor=:blue)

f = (du,u,p,t) -> two_path!(du,u,p,t,densities_tc3,cons_tc)
prob = ODEProblem(f,u₀,tspan,10 .^ rates_tc3_2paths)
sol = solve(prob,Rosenbrock23())
summ = reduce(vcat,sum(sol,dims=1))
plot!(sol.t,summ,linecolor=:cyan)

function plot_sum(rates)
    prob = ODEProblem(f,u₀,tspan,rates)
    # prob = remake(prob, p=10 .^ rates)
    sol = solve(prob,Rosenbrock23())
    summ = reduce(vcat,sum(sol,dims=1))
    plot!(sol.t,summ)
end

# dprob = DiscreteProblem(u₀, tspan, 10 .^ rates)
#
# # now we create a JumpProblem, and specify Gillespie's Direct Method as the solver:\n",
# jprob = JumpProblem(dprob, Direct(), f)
#
# # now let's solve and plot the jump process:
# sol = solve(jprob, SSAStepper())
# plot(sol, fmt=:svg, ylims = (0, 2))
#
# prob = DiscreteProblem(bimol, u₀, tspan, 10 .^ rates)
# jump_prob = JumpProblem(bimol, prob, Direct())
# sol = solve(jump_prob, SSAStepper())
#
# plot(sol)
#
# function bimol(du,u,p,t)
#     # unpack rates and constants
#     nᵣ = u[1]
#     k₁,k₋₁  = p
#     mᵣ,mₗ,A = [100,10,1]
#     # model
#     du[1] = dnᵣ = A*k₁*mᵣ*mₗ - k₋₁*nᵣ
#
# end


module ParentSelection
    using ..Representation
    # Random parent selection
    function random_parent_selection(population, λ, μ_parents)
        groups_indices = split_indices(length(population.members), λ, μ_parents)
        groups = Array{Representation.Organism, 1}[]
        n_groups = ceil(Int, (μ_parents * λ) / length(population.members))
        for i in 1:n_groups
            group = population.members[groups_indices[i, :]]
            push!(groups, group)
        end
        return groups
    end
    # Aux function
    function split_indices(population_length, λ, μ_parents)
        multiplier = ceil(Int, (λ * μ_parents) / population_length)
        total_size = floor(Int, population_length * multiplier)
        group_size = div(total_size, population_length)
        all_indices = ceil.(Int, rand(total_size) * population_length)
        groups_indices = reshape(all_indices, (group_size, div(total_size, group_size)))
        return groups_indices

    end

end # module ParentSelection


# Example for using the 1x1 convolution operator to permute an image along the channel dimension.
# Author: Philipp Witte, pwitte3@gatech.edu
# Date: January 2020

using InvertibleNetworks, LinearAlgebra, Test
using Flux

# Dimensions
nx = 64 # no. of pixels in x dimension
ny = 64 # no. of pixels in y dimension
k = 10  # no. of channels
batchsize = 4

# Input image: nx x ny x k x batchsize
X = glorot_uniform(nx, ny, k, batchsize)

# 1x1 convolution operators
C = Conv1x1(k)
C0 = Conv1x1(k)

# Generate "true/observed" data with the same dimension as X
Y = C.forward(X)

# Predicted data
Y0 = C0.forward(X)
@test isnothing(C0.v1.grad) # after forward pass, gradients are not set

# Data residual
ΔY = Y0 - Y

# Backward pass: Pass ΔY to compute ΔX, the gradient w.r.t the input X.
# Also pass Y0 to recompute the forward state X using the inverse mapping
# and use it to compute the derivative w.r.t. the coefficients of the
# Householder matrix.
ΔX, X_ = C0.inverse((ΔY, Y0))
@test ~isnothing(C0.v1.grad)    # after inverse pass, gradients are set
@test isapprox(norm(X - X_)/norm(X), 0f0, atol=1f-6)    # X and X_ should be the same


# Code used to generate SDP problems with a quadratic objective save in Testproblems/SDPQuad with an accurante
# solution from MOSEK
# The problem data is generated based on the Optimality conditions:
# Ax*+s*=b, s in S+, Px*+q+A'y*=0, y in S+

# Create different objective functions
# 1) P = rand pos def
# 2) P = I
# 3) P = 0

# A) Create problems like Ax+s=b, mat(S) in S+
# B) Create problems like <Ai,X> = bi, X in S+

# workspace()
# include("./Helper.jl")
# include("../../OSSDP/Code/src/Solver.jl")

# using JuMP, Mosek, JLD, HelperFunctions, OSSDP, Base.Test

rng = MersenneTwister(12345)
dirPath = "../DataFiles/Julia/SDPQuad/"
!ispath(dirPath) && mkdir(dirPath)
nn = 45

  # @testset "Random SDP Mosek vs. QOCS" begin

 for iii =1:1:nn
  # choose size of problem
  n = rand(rng,10:100)
  r = rand(rng,2:15)
  m = r^2
  A = spzeros(m,n)
  # construct A with several symmetric Ai that are vectorized and make the cols of A
  for kkk=1:n
    Ai = sprandn(rng,r,r,0.4)
    Ai = 0.5*(Ai+Ai')
    A[:,kkk] = vec(Ai)
  end
  xtrue = randn(rng,n,1)

  # create primal feasible point -> b
  Strue = generatePosDefMatrix(r,rng)
  strue = vec(Strue)
  b = A*xtrue + strue

  # create dual feasible point -> P,q
  # consider three different types of the quadratic cost
  if iii <= 15
     P = generatePosDefMatrix(n,rng)
     Phalf = P^(1/2)
  elseif iii <=30
     P = speye(n)
     Phalf = P
  else
   P = Phalf = spzeros(n,n)
  end

  Ytrue = generatePosDefMatrix(r,rng)
  ytrue = vec(Ytrue)
  q = (-P*xtrue -  A'*ytrue)[:]
  ra = 0.
  Kf = 0
  Kl = 0
  Kq = []
  Ks = [r^2]

  iii<=30 && (PhalfInvQ=Phalf\q)

  # K = Cone(0,0,[],[r^2])
  # setOFF = OSSDPSettings(rho=0.1,sigma=1e-6,alpha=1.6,max_iter=1500,verbose=false,checkTermination=1,scaling = 0,eps_abs = 1e-5,eps_rel=1e-5,adaptive_rho=false)
  # setON = OSSDPSettings(rho=0.1,sigma=1e-6,alpha=1.6,max_iter=1500,verbose=false,checkTermination=1,scaling = 10,eps_abs = 1e-5,eps_rel=1e-5,adaptive_rho=false)
  # setAdaptiveOFF = OSSDPSettings(rho=0.1,sigma=1e-6,alpha=1.6,max_iter=1500,verbose=false,checkTermination=1,scaling = 0,eps_abs = 1e-5,eps_rel=1e-5,adaptive_rho=true)
  # setAdaptiveON = OSSDPSettings(rho=0.1,sigma=1e-6,alpha=1.6,max_iter=1500,verbose=false,checkTermination=1,scaling = 10,eps_abs = 1e-5,eps_rel=1e-5,adaptive_rho=true)
  # res1,nothing = OSSDP.solve(P,q,A,b,K,setOFF);
  # print("\n.")
  # res2,nothing = OSSDP.solve(P,q,A,b,K,setON);
  # print(".")
  # res3,nothing = OSSDP.solve(P,q,A,b,K,setAdaptiveOFF);
  # print(".")
  # res4,nothing = OSSDP.solve(P,q,A,b,K,setAdaptiveON);
  # print(".")

  # println(">>>QOCS: Problem $(iii), res1: cost: $(res1.cost), status: $(res1.status)")
  # println(">>>QOCS: Problem $(iii), res2: cost: $(res2.cost), status: $(res2.status)")
  # println(">>>QOCS: Problem $(iii), res3: cost: $(res3.cost), status: $(res3.status)")
  # println(">>>QOCS: Problem $(iii), res4: cost: $(res4.cost), status: $(res4.status)")

  # solve accurately once with mosek s
  model = Model(solver=MosekSolver())
  @variable(model, x[1:n])
  @variable(model, S[1:r,1:r],SDP)
  # if P!= 0 add soc-constraint to model
  if P == spzeros(n,n)
    @objective(model, Min, q'*x)
  else
    # in case of quadratic objective the following problem is solved:
    # min x'Px + 2q'x
    # s.t. Ax+s=b, s in S+
    # instead solve equivalent problem
    # min t
    # s.t. ||Phalf*x+inv(Phalf)*q|| <= t
    # Ax+s=b, s in S+
    @variable(model, t)
    @objective(model, Min, t)
    @constraint(model,norm(Phalf*x+PhalfInvQ) <= t)
  end
  s = vec(S)
  @constraint(model,A*x+s.==b)
  status = JuMP.solve(model)
  objTrue = getobjectivevalue(model)

  (P!=spzeros(n,n)) && (objTrue = 0.5*( objTrue^2-q'*(P\q) ))
  solTrue = getvalue(x)

  # @test abs(objTrue-res1.cost) < 1e-2
  # @test norm(solTrue-res1.x,Inf) < 1e-2

  nr = "$(iii)"
  if iii < 10
    nr = "0$(iii)"
  end

  fn = "SDPQuad"*nr*".jld"
  problemType = "Random SDP with quadratic Objective"
  problemName = "RandomSDP"*nr

  JLD.save(dirPath*fn,"n",n,"m",m,"A",A,"b",b,"P",P,"q",q,"r",ra,"objTrue",objTrue,"solTrue",solTrue,"problemType",problemType,"problemName",problemName,"Kf",Kf,"Kl",Kl,"Kq",Kq,"Ks",Ks)
  println("$(iii)/$(nn) completed!")
end
  # end


"""
NsoliPDE(n; fixedeta=true, eta=.1, lsolver="gmres", restarts = 99)

Solve the Elliptic PDE using nsoli.jl on an n x n grid. You give me
n and (optionally) the iteration paramaters and I return the output of nsoli.
"""
function NsoliPDE(
    n;
    eta = 0.1,
    fixedeta = true,
    rtol = 1.e-7,
    atol = 1.e-10,
    Pvec = Pvec2d,
    pside = "right",
    lsolver = "gmres",
    restarts = 99
)
    # Get some room for the residual
    u0 = zeros(n * n)
    FV = copy(u0)
    # Get the precomputed data from pdeinit
    pdata = pdeinit(n)
    # Storage for the Krylov basis
    (lsolver == "gmres") ? (JV = zeros(n * n, restarts+1)) : JV = zeros(n*n)
    pout = nsoli(
        pdeF!,
        u0,
        FV,
        JV,
        Jvec2d;
        rtol = rtol,
        atol = atol,
        Pvec = Pvec,
        pdata = pdata,
        eta = eta,
        fixedeta = fixedeta,
        maxit = 20,
        lmaxit = 20,
        pside = pside,
        lsolver = lsolver,
    )
    return pout
end


@testset "---- Crank-Nicolson Convergence Tests, Complex Step Jacobian -----" begin
start_dir = pwd()

cd("./m1")
fname = "input_vals1.jl"
mesh, sbp, eqn, opts = solvePDE(fname)

cd("../m2")
fname = "input_vals1.jl"
mesh, sbp, eqn, opts = solvePDE(fname)

cd("../m3")
fname = "input_vals1.jl"
mesh, sbp, eqn, opts = solvePDE(fname)

cd("..")
include("calc_line.jl")

slope = calc_line()
# println("slope = ", slope)

data = readdlm("err_data.dat")
err_vals = data[:, 2]
#println("err_vals = ", err_vals)

slope_val = 2.00
slope_margin = 0.1

@test  slope  > slope_val - slope_margin
@test  slope  < slope_val + slope_margin

err_val = 0.09095728504176116
slope_fac = 1.25
# println("err_vals[1] = ", err_vals[1])
@test  err_vals[1]  > err_val/slope_fac
@test  err_vals[1]  < err_val*slope_fac

end


include("sdsos_example.jl")
include("options_pricing.jl")


# ALL TESTS THAT CONCERN MODFICIATION

# include
using LatticePhysics
using Base.Test

# begin the Modification testblock
path_testset = @testset "Interaction Matrices" begin

################################################################################
#
#   METHODS FOR CONSTRUCTION INTERACTION MATRICES FOR LATTICES
#
################################################################################

################################################################################
#
#   1) INTERACTION MATRICES IN REAL SPACE
#
################################################################################

# begin the testset
@testset "Interaction matrices in real space" begin

    # begin the testset
    @testset "2D (honeycomb)" begin

        # get a unitcell
        unitcell = getUnitcellHoneycomb()
        # get a lattice
        lattice = getLattice(unitcell, [-4,-2])

        # test the function for unitcell
        @test typeof(getInteractionMatrixRealSpace(unitcell)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixRealSpace(unitcell, enforce_hermitian=true))
        @test size(getInteractionMatrixRealSpace(unitcell)) == (2,2)

        # test the function for lattice
        @test typeof(getInteractionMatrixRealSpace(lattice)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixRealSpace(lattice, enforce_hermitian=true))
        @test size(getInteractionMatrixRealSpace(lattice)) == (16,16)

    # end the testset here
    end;

    # begin the testset
    @testset "2D (square)" begin

        # get a unitcell
        unitcell = getUnitcellSquare()
        # get a lattice
        lattice = getLattice(unitcell, [-4,-2])

        # test the function for unitcell
        @test typeof(getInteractionMatrixRealSpace(unitcell)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixRealSpace(unitcell, enforce_hermitian=true))
        @test size(getInteractionMatrixRealSpace(unitcell)) == (1,1)

        # test the function for lattice
        @test typeof(getInteractionMatrixRealSpace(lattice)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixRealSpace(lattice, enforce_hermitian=true))
        @test size(getInteractionMatrixRealSpace(lattice)) == (8,8)

    # end the testset here
    end;

    # begin the testset
    @testset "3D (diamond)" begin

        # get a unitcell
        unitcell = getUnitcellDiamond()
        # get a lattice
        lattice = getLattice(unitcell, [-4,-2,-2])

        # test the function for unitcell
        @test typeof(getInteractionMatrixRealSpace(unitcell)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixRealSpace(unitcell, enforce_hermitian=true))
        @test size(getInteractionMatrixRealSpace(unitcell)) == (2,2)

        # test the function for lattice
        @test typeof(getInteractionMatrixRealSpace(lattice)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixRealSpace(lattice, enforce_hermitian=true))
        @test size(getInteractionMatrixRealSpace(lattice)) == (32,32)

    # end the testset here
    end;

# end the testset here
end;

################################################################################
#
#   2) INTERACTION MATRICES IN MOMENTUM SPACE
#
################################################################################

# begin the testset
@testset "Interaction matrices in momentum space" begin

    # begin the testset
    @testset "2D (honeycomb)" begin

        # get a unitcell
        unitcell = getUnitcellHoneycomb()
        # get a lattice
        lattice = getLattice(unitcell, [-4,-2])

        # k vector (random)
        k = [0.1, 0.151235]

        # test the function for unitcell
        @test typeof(getInteractionMatrixKSpace(unitcell, k)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixKSpace(unitcell, k, enforce_hermitian=true))
        @test size(getInteractionMatrixKSpace(unitcell, k)) == (2,2)

        # test the function for lattice
        @test typeof(getInteractionMatrixKSpace(lattice, k)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixKSpace(lattice, k, enforce_hermitian=true))
        @test size(getInteractionMatrixKSpace(lattice, k)) == (16,16)

    # end the testset here
    end;

    # begin the testset
    @testset "2D (square)" begin

        # get a unitcell
        unitcell = getUnitcellSquare()
        # get a lattice
        lattice = getLattice(unitcell, [-4,-2])

        # k vector (random)
        k = [0.1, 0.151235]

        # test the function for unitcell
        @test typeof(getInteractionMatrixKSpace(unitcell, k)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixKSpace(unitcell, k, enforce_hermitian=true))
        @test size(getInteractionMatrixKSpace(unitcell, k)) == (1,1)

        # test the function for lattice
        @test typeof(getInteractionMatrixKSpace(lattice, k)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixKSpace(lattice, k, enforce_hermitian=true))
        @test size(getInteractionMatrixKSpace(lattice, k)) == (8,8)

    # end the testset here
    end;

    # begin the testset
    @testset "3D (diamond)" begin

        # get a unitcell
        unitcell = getUnitcellDiamond()
        # get a lattice
        lattice = getLattice(unitcell, [-4,-2,-2])

        # k vector (random)
        k = [0.1, 0.151235, -0.21]

        # test the function for unitcell
        @test typeof(getInteractionMatrixKSpace(unitcell, k)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixKSpace(unitcell, k, enforce_hermitian=true))
        @test size(getInteractionMatrixKSpace(unitcell, k)) == (2,2)

        # test the function for lattice
        @test typeof(getInteractionMatrixKSpace(lattice, k)) == Array{Complex, 2}
        @test ishermitian(getInteractionMatrixKSpace(lattice, k, enforce_hermitian=true))
        @test size(getInteractionMatrixKSpace(lattice, k)) == (32,32)

    # end the testset here
    end;

# end the testset here
end;

# end the path test block here
end;


using Meshing
using GeometryTypes
using LinearAlgebra: dot, norm
using FileIO

# generate an SDF of a sphere
sdf_sphere = SignedDistanceField(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.))) do v
    sqrt(sum(dot(v,v))) - 1 # sphere
end

m = GLNormalMesh(sdf_sphere, MarchingCubes())

save("sphere.ply",m)


"""
Collection of anisotropy models.

* [`IsotropicModel`](@ref): isotropic model
* [`ConstantBetaModel`](@ref): constant beta model
* [`RSBetaModel`](@ref): flexible beta model from Read & Steger
* [`beta`](@ref): orbital anisotropy profile
"""
module Anisotropy

import Slomo.Utils: B_inc, gamma
import Slomo.Models: Model
export IsotropicModel, ConstantBetaModel, RSBetaModel, beta

abstract type AnisotropyModel <: Model end

"""
    beta(model::AnisotropyModel, r)

Orbital anisotropy as a function of radius.  The orbital anisotropy is defined as

```math
\\beta = 1 - \\sigma_\\theta^2 / \\sigma_r^2
```
"""
function beta(model::AnisotropyModel, r)
    throw(NotImplemented("no defined beta profile"))
end

"""
    g_jean(model::AnisotropyModel, r)

Integrating factor when solving for \$\\sigma_r^2\$
"""
function g_jeans(model::AnisotropyModel, r)
    exp.(2.0 * integrate(x -> beta(model, x) / x, 1.0, r))
end

"""
    K_jeans(model::AnisotropyModel, r, R)

Jeans projection kernel for an anisotropy model.
"""
function K_jeans(model::AnisotropyModel, r, R)
    throw(NotImplemented("no defined Jeans Kernel"))
end

"""
    IsotropicModel()

Parameter-less model representing an isotropic system (i.e. beta = 0).
"""
struct IsotropicModel <: AnisotropyModel end

beta(model::IsotropicModel, r) = zeros(size(r))

K_jeans(model::IsotropicModel, r, R) = begin
    @. sqrt(1.0 - 1.0 / (r / R) ^ 2)
end

g_jeans(model::IsotropicModel, r) = ones(size(r))

"""
    ConstantBetaModel(beta)

Model that assumes beta is constant with galactocentric radius.
"""
struct ConstantBetaModel <: AnisotropyModel
    beta::Float64
end
ConstantBetaModel() = ConstantBetaModel(0.0)

beta(model::ConstantBetaModel, r) = model.beta .* ones(size(r))

g_jeans(model::ConstantBetaModel, r) = r .^ (2 * model.beta)

K_jeans(model::ConstantBetaModel, r, R) = begin
    Γ = gamma
    β = model.beta
    # nudge half-integral beta
    if 2.0 * β - floor(2.0 * β) == 0.0
        β = β + rand([1, -1]) * eps()^0.5
    end
    u = r ./ R
    um2 = u .^ -2.0
    term1 = (1.5 - β) * √(π) * Γ(β - 0.5) / Γ(β)
    term2 = β * B_inc(β + 0.5, 0.5, um2)
    term3 = -B_inc(β - 0.5, 0.5, um2)
    return 0.5 * u .^ (2.0 * β - 1.0) .* (term1 .+ term2 .+ term3)
end

"""
    RSBetaModel(beta0, betaInf, rbeta, nbeta)

Flexible velocity anisotropy model from Read & Steger 2017 (eq. 9)

```math
\\beta(r) = \\beta_0 + (\\beta_\\infty - \\beta_0) / (1 + (r_\\beta / r))^{n_\\beta}
```

* `beta0`: inner asymptotic anisotropy
* `betaInf`: outer asymptotic anisotropy
* `rbeta`: transition radius
* `nbeta`: transition sharpness, higher is a faster transition

`beta0, betaInf, nbeta = 0, 1, 2` corresponds to the Osipkov-Merritt profile

`beta0, betaInf, nbeta = 0, 0.5, 1` corresponds to the Mamon-Lokas profile
"""
struct RSBetaModel <: AnisotropyModel
    beta0::Float64
    betaInf::Float64
    rbeta::Float64
    nbeta::Float64
end

RSBetaModel() = RSBetaModel(0.0, 0.5, 10.0, 1.0)

beta(model::RSBetaModel, r) = begin
    @. model.beta0 + (model.betaInf - model.beta0) / (1.0 + (model.rbeta / r) ^ model.nbeta)
end

g_jeans(model::RSBetaModel, r) = begin
    n = model.nbeta
    r_β = model.rbeta
    β_inf = model.betaInf
    Δβ = model.betaInf - model.beta0
    return @. r ^ (2.0 * β_inf) * ((r_β / r)^n + 1.0) ^ (2.0 * Δβ / n)
end

end


using DifferentialEquations
using PolynomialRoots
using Printf
# using MATLAB
# using ProgressMeter

include("leap_frog_definitions.jl")
max_hit_q1 = 6
max_hit_p2 = 10000
barrier = 5
H=.25

condition_max_hits(u, t, integrator) = u[3] >max_hit_q1 || u[6] > max_hit_p2 || maximum([abs(u[1]),abs(u[2]),abs(u[4]),abs(u[5])]) > barrier
affect_stop!(integrator) = terminate!(integrator)
function condition_hits_PSS_q1(u, t, integrator) # Event when event_f(u,t) == 0
    u[1]
end

function condition_hits_PSS_p2f(u, t, integrator) # Event when event_f(u,t) == 0
    u[5]+100
end

function affect_update_iterator_q1!(integrator)
    q2=integrator.u[2]
    p2=integrator.u[5]
    p1=integrator.u[4]
    P1=P1_poly(q2,p2, H)
    if !isempty(p1) && abs(p1-P1)<1e-5
        integrator.u[3] = integrator.u[3] + 1
    end
end
function affect_update_iterator_p2!(integrator)
    integrator.u[6] = integrator.u[6] + 1
end

callback_max_hits = DiscreteCallback(condition_max_hits, affect_stop!)
callback_hits_PSS_q1_forward = ContinuousCallback(condition_hits_PSS_q1, nothing,  affect_update_iterator_q1!,rootfind=true)
callback_hits_PSS_q1_backward = ContinuousCallback(condition_hits_PSS_q1,  affect_update_iterator_q1!,nothing,rootfind=true)

callback_hits_PSS_p2_forward = ContinuousCallback(condition_hits_PSS_p2f, affect_update_iterator_p2!)
callback_hits_PSS_p2_backward = ContinuousCallback(condition_hits_PSS_p2f, affect_update_iterator_p2!)

# callback_hits_PSS_q1_forward= ContinuousCallback(condition_hits_PSS_q1, affect_update_iterator_q1!)
# callback_hits_PSS_q1_backward= ContinuousCallback(condition_hits_PSS_q1,affect_update_iterator_q1!)

# callback_hits_PSS_p2_forward = ContinuousCallback(condition_hits_PSS_p2,  affect_update_iterator_p2!)
# callback_hits_PSS_p2_backward = ContinuousCallback(condition_hits_PSS_p2, affect_update_iterator_p2!)

cb_forward = CallbackSet(callback_hits_PSS_q1_forward, callback_hits_PSS_p2_forward, callback_max_hits)
cb_backward = CallbackSet(callback_hits_PSS_q1_backward, callback_hits_PSS_p2_backward, callback_max_hits)

function Q1_trajectories(Q2,P2, t_end, H,flag)
    P1 = P1_poly(Q2, P2, H)
    if ~isempty(P1)
        # println(Q1)
        q0, p0 = [zeros(3) for i in 1:2]
        q0[1] = 0
        q0[2] = Q2
        q0[3] = 0
        p0[1] = P1
        p0[2] = P2
        p0[3] = 0
       #constructor for ODE
        prob_f = HamiltonianProblem{true}(Hamiltonian_Dimer, q0, p0, (0., t_end));
        #solve ode , save_everystep=false is important to prevent sol to include all points, not just event points
        # sol_f = solve(prob_f,  RK4(),maxiters=1e20,callback = cb_forward, save_start = false, save_end = true, save_everystep = flag)
        sol_f = solve(prob_f, Vern9(),maxiters=1e20, reltol=1e-13,abstol=1e-15,callback = cb_forward, save_start = false, save_end = true, save_everystep = flag)

        # sol_f=solve(prob, Vern9(),maxiters=1e20, reltol=1e-13,abstol=1e-15,callback=cb_forward,save_start=false,save_end=true,save_everystep=false)

        prob_b = HamiltonianProblem{true}(Hamiltonian_Dimer_Backwards, q0, p0, (0., t_end));
        #solve ode , save_everystep=false is important to prevent sol to include all points, not just event points
        # sol_b = solve(prob_b,  Vern9(), maxiters = 1e20, reltol = 1e-13, abstol = 1e-15,  callback = cb_backward, save_start = false, save_end = true, save_everystep = flag)
        # sol_b = solve(prob_b,  RK4(), maxiters = 1e20, callback = cb_backward, save_start = false, save_end = true, save_everystep = flag)
        sol_b=solve(prob_b, Vern9(),maxiters=1e20, reltol=1e-13,abstol=1e-15,callback=cb_backward,save_start=false,save_end=true,save_everystep=flag)
        point=[sol_f[1,2],sol_f[2,2],sol_f[4,2],sol_f[5,2]]
        return sol_f.t, sol_f[1,:],sol_f[3,end],sol_b.t, sol_b[1,:],sol_b[3,end]

    else
        return -2
    end

end


#=
push!(LOAD_PATH, "<path to directory WarpFlow>") # <--- use this if Julia can't find WarpFlow
include("scripts/example1.jl")
=#
using WarpFlow
using PyPlot

# set the data and the target
dim = 1
tmpx = shuffle([rand(45); randn(175)/10 .+ .8])
nPhi = length(tmpx)
X = Array{Float64,1}[[(tmpx[i]- minimum(tmpx))/maximum(tmpx)] for i=1:nPhi]

# set the target function by overwriting the default...for v0.5 just pass a function.
target(x) = WarpFlow.targetUnif1d(x; targSig = 0.1, limit = 0.35, center = 0.5) # this sets an alias to a function giving in the file targets.jl

# generate Flow object
kappa  = Array{Float64,1}[X[Int(i)]  for i in 1:round(nPhi/4)]
nKap   = length(kappa)
y0     = Flow(kappa, WarpFlow.array1(dim, nKap), X, WarpFlow.array2eye(dim, nPhi), dim)

# function to save images
function plotrun(;fignum = 1, save = false)
	x_grd = linspace(-0.2, 1.2, 300)
	phix_grd_0  = Array{Float64,1}[[x_grd[i]] for i=1:length(x_grd)]
	Dphix_grd_0 = Array{Float64,2}[ones(1,1)  for i=1:length(x_grd)]
	yplt0 = Flow(y0.kappa, y0.eta_coeff, phix_grd_0, Dphix_grd_0, dim)
	dydt(t,y)= WarpFlow.d_forward_dt(y, sigma)
	(t1,yplt1)=WarpFlow.ode23_abstract_end(dydt,[0,1], yplt0) # Flow y0 forward to time 1
	det_grd = Float64[abs(yplt1.Dphix[i][1]) for i=1:length(x_grd)]
	den,    = target(yplt1.phix)
	est_den = det_grd.*den

	fig = figure()
	plt[:hist]([pnt[1] for pnt in X], 50, normed=1, histtype="stepfilled")
	plot(x_grd, est_den)
	if save
		plt[:savefig]("simulations/example1_v$fignum.pdf",dpi=180)
		plt[:close](fig)
	end
	return nothing
end

#  gradient ascent on eta_coeff
lambda, sigma = 1.0, 0.1
for counter = 1:35
	@time z0 = WarpFlow.get_grad(y0, target, lambda, sigma)
	y0 = y0 + 0.002 * z0
end

plotrun()

#
# #  gradient ascent on eta_coeff
# lambda, sigma = 0.1, 0.05
# for counter = 1:35
# 	@time z0 = get_grad(y0, lambda, sigma)
# 	y0 = y0 + 0.002 * z0
# end
#
# plotrun()


@testset "clustercoeffs" begin
    A = load_matrix_network("clique-10")
    v = ones(Int64,10)
    @testset "MatrixNetwork" begin
        cc = clustercoeffs(MatrixNetwork(A))
        @test v == cc
    end
    @testset "SparseMatrixCSC" begin
        cc = clustercoeffs(A)
        @test v == cc
    end
    @testset "CSR" begin
        cc = clustercoeffs(sparse_to_csr(A)...)
        @test v == cc
    end
    @testset "triplet" begin
        cc = clustercoeffs(findnz(A)[1], findnz(A)[2])
        @test v == cc
    end
    @testset "error throwing" begin
        # not undirected
        bad_mat = spdiagm(1=>[1;1]) #spdiagm(([1; 1],), 1, 3, 3)

        @test_throws ErrorException clustercoeffs(MatrixNetwork(bad_mat))
        @test_throws ErrorException clustercoeffs(bad_mat)
        # negative weights
        @test_throws ErrorException clustercoeffs(MatrixNetwork(-sparse(1.0I,4,4)))
        @test_throws ErrorException clustercoeffs(-sparse(1.0I,4,4))
    end
    cc_mn = clustercoeffs(MatrixNetwork(A), false, false)
    cc_csc = clustercoeffs(A, false, false)
    cc_csr = clustercoeffs(sparse_to_csr(A)..., false, false)
    cc_tri = clustercoeffs(findnz(A)[1], findnz(A)[2], false, false)
    @test cc_mn == cc_csc
    @test cc_csr == cc_csc
    @test cc_tri == cc_csc
end


struct TensorProduct <: QuadratureName end

const tensor_product = TensorProduct()

function Quadrature(p::Polytope,::TensorProduct,degrees)
  @assert is_n_cube(p) """\n
  Tensor product quadrature rule only for n-cubes.
  """
  @assert length(degrees) == num_dims(p)
  _tensor_product_legendre(degrees)
end

function Quadrature(p::Polytope,name::TensorProduct,degree::Integer)
  degrees = ntuple(i->degree,Val(num_dims(p)))
  Quadrature(p,name,degrees)
end

# Low level constructor

function _tensor_product_legendre(degrees)
    D = length(degrees)
    T = Float64
    npoints = [ ceil(Int,(degrees[i]+1.0)/2.0) for i in 1:D ]
    quads = [ gauss( eltype(Point{D,T}), npoints[i] ) for i in 1:D ]
    for i in 1:D
      quads[i][1] .+= 1;
      quads[i][1] .*= 1.0/2.0
      quads[i][2] .*= 1.0/2.0
    end
    (coords, weights) = _tensor_product(Point{D,T},quads,npoints)
    GenericQuadrature(coords,weights,"Tensor product of 1d Gauss-Legendre quadratures of degrees $degrees")
end

# Helpers

function _tensor_product(::Type{Point{D,T}},quads,npoints) where {D,T}
  @assert length(quads) == D
  @assert length(npoints) == D
  cis = CartesianIndices(tuple(npoints...))
  n = prod(npoints)
  coords = Vector{Point{D,T}}(undef,n)
  weights = Vector{T}(undef,n)
  _tensor_product!(quads,coords,weights,cis)
  (coords, weights)
end

function _tensor_product(::Type{Point{0,T}},quads,npoints) where T
  coords = [zero(Point{0,T})]
  weights = [one(T)]
  coords, weights
end

function _tensor_product!(quads,coords,weights,cis)
  p = zero(Mutable(eltype(coords)))
  D = length(p)
  lis = LinearIndices(cis)
  for ci in cis
    p[:] = 0.0
    w = 1.0
    for d in 1:D
      xi = quads[d][1][ci[d]]
      wi = quads[d][2][ci[d]]
      p[d] = xi
      w *= wi
    end
    li = lis[ci]
    coords[li] = p
    weights[li] = w
  end
end