/*****
 * knot.cc
 * Andy Hammerlindl 2005/02/10
 *
 * Describes a knot, a point and its neighbouring specifiers, used as an
 * intermediate structure in solving paths.
 *****/

#include "knot.h"
#include "util.h"

#include "angle.h"
#include "settings.h"

namespace camp {

/***** Debugging *****/
//bool tracing_solving=false;

template <typename T>
ostream& info(ostream& o, const char *name, cvector<T>& v)
{
  if (settings::verbose > 3) {
    o << name << ":\n\n";

    for(Int i=0; i < (Int) v.size(); ++i)
      o << v[i] << endl;

    o << endl;
  }
  return o;
}

ostream& info(ostream& o, string name, knotlist& l)
{
  if (settings::verbose > 3) {
    o << name << ":\n\n";

    for(Int i=0; i < (Int) l.size(); ++i)
      o << l[i] << endl;

    if (l.cyclic())
      o << "cyclic" << endl;

    o << endl;
  }
  return o;
}

#define INFO(x) info(cerr,#x,x)

/***** Auxillary computation functions *****/

// Computes the relative distance of a control point given the angles.
// The name is somewhat misleading as the velocity (with respect to the
// variable that parameterizes the path) is relative to the distance
// between the knots and even then, is actually three times this.
// The routine is based on the velocity function in Section 131 of the MetaPost
// code, but differs in that it automatically accounts for the tension and
// bounding with tension atleast.
double velocity(double theta, double phi, tension t)
{
  static const double VELOCITY_BOUND = 4.0;
  static const double a = sqrt(2.0);
  static const double b = 1.0/16.0;
  static const double c = 1.5*(sqrt(5.0)-1.0);
  static const double d = 1.5*(3.0-sqrt(5.0));

  double st = sin(theta), ct = cos(theta),
    sf = sin(phi),   cf = cos(phi);

  double denom = t.val * (3.0 + c*ct + d*cf);

  double r = denom != 0.0 ? (2.0 + a*(st - b*sf)*(sf - b*st)*(ct-cf)) / denom
    : VELOCITY_BOUND;

  //cerr << " velocity(" << theta << "," << phi <<")= " << r << endl;
  if (r >  VELOCITY_BOUND)
    r = VELOCITY_BOUND;

  // Apply boundedness condition for tension atleast cases.
  if (t.atleast)
    {
      double sine = sin(theta + phi);
      if ((st >= 0.0 && sf >= 0.0 && sine > 0.0) ||
          (st <= 0.0 && sf <= 0.0 && sine < 0.0))
        {
          double rmax = sf / sine;
          if (r > rmax)
            r = rmax;
        }
    }

  return r;
}

double niceAngle(pair z)
{
  return z.gety() == 0 ? z.getx() >= 0 ? 0 : PI
    : angle(z);
}

// Ensures an angle is in the range between -PI and PI.
double reduceAngle(double angle)
{
  return angle >  PI ? angle - 2.0*PI :
    angle < -PI ? angle + 2.0*PI :
    angle;
}


bool interesting(tension t)
{
  return t.val!=1.0 || t.atleast==true;
}

bool interesting(spec *s)
{
  return !s->open();
}

ostream& operator<<(ostream& out, const knot& k)
{
  if (interesting(k.tin))
    out << k.tin << " ";
  if (interesting(k.in))
    out << *k.in << " ";
  out << k.z;
  if (interesting(k.out))
    out << " " << *k.out;
  if (interesting(k.tout))
    out << " " << k.tout;
  return out;
}


eqn dirSpec::eqnOut(Int j, knotlist& l, cvector<double>&, cvector<double>&)
{
  // When choosing the control points, the path will come out the first knot
  // going straight to the next knot rotated by the angle theta.
  // Therefore, the angle theta we want is the difference between the
  // specified heading given and the heading to the next knot.
  double theta=reduceAngle(given-niceAngle(l[j+1].z-l[j].z));

  // Give a simple linear equation to ensure this theta is picked.
  return eqn(0.0,1.0,0.0,theta);
}

eqn dirSpec::eqnIn(Int j, knotlist& l, cvector<double>&, cvector<double>&)
{
  double theta=reduceAngle(given-niceAngle(l[j].z-l[j-1].z));
  return eqn(0.0,1.0,0.0,theta);
}

eqn curlSpec::eqnOut(Int j, knotlist& l, cvector<double>&,
                     cvector<double>& psi)
{
  double alpha=l[j].alpha();
  double beta=l[j+1].beta();

  double chi=alpha*alpha*gamma/(beta*beta);

  double C=alpha*chi+3-beta;
  double D=(3.0-alpha)*chi+beta;

  return eqn(0.0,C,D,-D*psi[j+1]);
}

eqn curlSpec::eqnIn(Int j, knotlist& l, cvector<double>&, cvector<double>&)
{
  double alpha=l[j-1].alpha();
  double beta=l[j].beta();

  double chi=beta*beta*gamma/(alpha*alpha);

  double A=(3-beta)*chi+alpha;
  double B=beta*chi+3-alpha;

  return eqn(A,B,0.0,0.0);
}

spec *controlSpec::outPartner(pair z)
{
  static curlSpec curl;
  return cz==z ? (spec *)&curl : (spec *)new dirSpec(z-cz);
}

spec *controlSpec::inPartner(pair z)
{
  static curlSpec curl;
  return cz==z ? (spec *)&curl : (spec *)new dirSpec(cz-z);
}

// Compute the displacement between points. The j-th result is the distance
// between knot j and knot j+1.
struct dzprop : public knotprop<pair> {
  dzprop(knotlist& l)
    : knotprop<pair>(l) {}

  pair solo(Int) { return pair(0,0); }
  pair start(Int j) { return l[j+1].z - l[j].z; }
  pair mid(Int j) { return l[j+1].z - l[j].z; }
  pair end(Int) { return pair(0,0); }
};

// Compute the distance between points, using the already computed dz.  This
// doesn't use the infomation in the knots, but the knotprop class is useful as
// it takes care of the iteration for us.
struct dprop : public knotprop<double> {
  cvector<pair>& dz;

  dprop(knotlist &l, cvector<pair>& dz)
    : knotprop<double>(l), dz(dz) {}

  double solo(Int j) { return length(dz[j]); }
  double start(Int j) { return length(dz[j]); }
  double mid(Int j) { return length(dz[j]); }
  double end(Int j) { return length(dz[j]); }
};

// Compute the turning angles (psi) between points, using the already computed
// dz.
struct psiprop : public knotprop<double> {
  cvector<pair>& dz;

  psiprop(knotlist &l, cvector<pair>& dz)
    : knotprop<double>(l), dz(dz) {}

  double solo(Int) { return 0; }

  // We set the starting and ending psi to zero.
  double start(Int) { return 0; }
  double end(Int) { return 0; }

  double mid(Int j) { return niceAngle(dz[j]/dz[j-1]); }
};

struct eqnprop : public knotprop<eqn> {
  cvector<double>& d;
  cvector<double>& psi;

  eqnprop(knotlist &l, cvector<double>& d, cvector<double>& psi)
    : knotprop<eqn>(l), d(d), psi(psi) {}

  eqn solo(Int) {
    assert(False);
    return eqn(0.0,1.0,0.0,0.0);
  }

  eqn start(Int j) {
    // Defer to the specifier, as it knows the specifics.
    return dynamic_cast<endSpec *>(l[j].out)->eqnOut(j,l,d,psi);
  }

  eqn end(Int j) {
    return dynamic_cast<endSpec *>(l[j].in)->eqnIn(j,l,d,psi);
  }

  eqn mid(Int j) {
    double lastAlpha = l[j-1].alpha();
    double thisAlpha = l[j].alpha();
    double thisBeta  = l[j].beta();
    double nextBeta  = l[j+1].beta();

    // Values based on the linear approximation of the curvature coming
    // into the knot with respect to theta[j-1] and theta[j].
    double inFactor = 1.0/(thisBeta*thisBeta*d[j-1]);
    double A = lastAlpha*inFactor;
    double B = (3.0 - lastAlpha)*inFactor;

    // Values based on the linear approximation of the curvature going out of
    // the knot with respect to theta[j] and theta[j+1].
    double outFactor = 1.0/(thisAlpha*thisAlpha*d[j]);
    double C = (3.0 - nextBeta)*outFactor;
    double D = nextBeta*outFactor;

    return eqn(A,B+C,D,-B*psi[j]-D*psi[j+1]);
  }
};

// If the system of equations is homogeneous (ie. we are solving for x in
// Ax=0), there is no need to solve for theta; we can just use zeros for the
// thetas.  In fact, our general solving method may not work in this case.
// A common example of this is
//
//   a{curl 1}..{curl 1}b
//
// which arises when solving a one-length path a..b or in a larger path a
// section a--b.
bool homogeneous(cvector<eqn>& e)
{
  for(cvector<eqn>::iterator p=e.begin(); p!=e.end(); ++p)
    if (p->aug != 0)
      return false;
  return true;
}

// Checks whether the equation being solved will be solved as a straight
// path from the first point to the second.
bool straightSection(cvector<eqn>& e)
{
  return e.size()==2 && e.front().aug==0 && e.back().aug==0;
}

struct weqn : public eqn {
  double w;
  weqn(double pre, double piv, double post, double aug, double w=0)
    : eqn(pre,piv,post,aug), w(w) {}

  friend ostream& operator<< (ostream& out, const weqn we)
  {
    return out << (eqn &)we << " + " << we.w << " * theta[0]";
  }
};

weqn scale(weqn q) {
  assert(q.pre == 0 && q.piv != 0);
  return weqn(0,1,q.post/q.piv,q.aug/q.piv,q.w/q.piv);
}

/* Recalculate the equations in the form:
 *   theta[j] + post * theta[j+1] = aug + w * theta[0]
 *
 * Used as the first step in solve cyclic equations.
 */
cvector<weqn> recalc(cvector<eqn>& e)
{
  Int n=(Int) e.size();
  cvector<weqn> we;
  weqn lasteqn(0,1,0,0,1);
  we.push_back(lasteqn); // As a placeholder.
  for (Int j=1; j < n; j++) {
    // Subtract a factor of the last equation so that the first entry is
    // zero, then procede to scale it.
    eqn& q=e[j];
    lasteqn=scale(weqn(0,q.piv-q.pre*lasteqn.post,q.post,
                       q.aug-q.pre*lasteqn.aug,-q.pre*lasteqn.w));
    we.push_back(lasteqn);
  }
  // To keep all of the infomation enocoded in the linear equations, we need
  // to augment the computation to replace our trivial start weqn with a
  // real one.  To do this, we take one more step in the iteration and
  // compute the weqn for j=n, since n=0 (mod n).
  {
    eqn& q=e[0];
    we.front()=scale(weqn(0,q.piv-q.pre*lasteqn.post,q.post,
                          q.aug-q.pre*lasteqn.aug,-q.pre*lasteqn.w));
  }
  return we;
}

double solveForTheta0(cvector<weqn>& we)
{
  // Solve for theta[0]=theta[n].
  // How we do this is essentially to write out the first equation as:
  //
  //   theta[n] = aug[0] + w[0]*theta[0] - post[0]*theta[1]
  //
  // and then use the next equation to substitute in for theta[1]:
  //
  //   theta[1] = aug[1] + w[1]*theta[0] - post[1]*theta[2]
  //
  // and so on until we have an equation just in terms of theta[0] and
  // theta[n] (which are the same theta).
  //
  // The loop invariant maintained is that after j iterations, we have
  //   theta[n]= a + b*theta[0] + c*theta[j]
  Int n=(Int) we.size();
  double a=0,b=0,c=1;
  for (Int j=0;j<n;++j) {
    weqn& q=we[j];
    a+=c*q.aug;
    b+=c*q.w;
    c=-c*q.post;
  }

  // After the iteration we have
  //
  //   theta[n] = a + b*theta[0] + c*theta[n]
  //
  // where theta[n]=theta[0], so
  return a/(1.0-(b+c));
}

cvector<double> backsubCyclic(cvector<weqn>& we, double theta0)
{
  Int n=(Int) we.size();
  cvector<double> thetas;
  double lastTheta=theta0;
  for (Int j=1;j<=n;++j)
    {
      weqn& q=we[n-j];
      assert(q.pre == 0 && q.piv == 1);
      double theta=-q.post*lastTheta+q.aug+q.w*theta0;
      thetas.push_back(theta);
      lastTheta=theta;
    }
  reverse(thetas.begin(),thetas.end());
  return thetas;
}

// For the non-cyclic equations, do row operation to put the matrix into
// reduced echelon form, ie. calculates equivalent equations but with pre=0 and
// piv=1 for each eqn.
struct ref : public knotprop<eqn> {
  cvector<eqn>& e;
  eqn lasteqn;

  ref(knotlist& l, cvector<eqn>& e)
    : knotprop<eqn>(l), e(e), lasteqn(0,1,0,0) {}

  // Scale the equation so that the pivot (diagonal) entry is one, and save
  // the new equation as lasteqn.
  eqn scale(eqn q) {
    assert(q.pre == 0 && q.piv != 0);
    return lasteqn = eqn(0,1,q.post/q.piv,q.aug/q.piv);
  }

  eqn start(Int j) {
    return scale(e[j]);
  }
  eqn mid(Int j) {
    // Subtract a factor of the last equation so that the first entry is
    // zero, then procede to scale it.
    eqn& q=e[j];
    return scale(eqn(0,q.piv-q.pre*lasteqn.post,q.post,
                     q.aug-q.pre*lasteqn.aug));
  }
  // The end case is the same as the middle case.
};

// Once the matrix is in reduced echelon form, we can solve for the values by
// back-substitution.  This algorithm works from the bottom-up, so backCompute
// must be used to get the answer.
struct backsub : public knotprop<double> {
  cvector<eqn>& e;
  double lastTheta;

  backsub(knotlist& l, cvector<eqn>& e)
    : knotprop<double>(l), e(e) {}

  double end(Int j) {
    eqn& q=e[j];
    assert(q.pre == 0 && q.piv == 1 && q.post == 0);
    double theta=q.aug;
    lastTheta=theta;
    return theta;
  }

  double mid(Int j) {
    eqn& q=e[j];
    assert(q.pre == 0 && q.piv == 1);
    double theta=-q.post*lastTheta+q.aug;
    lastTheta=theta;
    return theta;
  }

  // start is the same as mid.
};

// Once the equations have been determined, solve for the thetas.
cvector<double> solveThetas(knotlist& l, cvector<eqn>& e)
{
  if (homogeneous(e))
    // We are solving Ax=0, so a solution is zero for every theta.
    return cvector<double>(e.size(),0);
  else if (l.cyclic()) {
    // The knotprop template is unusually unhelpful in this case, so I
    // won't use it here. The algorithm breaks into three passes on the
    // object.  The old Asymptote code used a two-pass method, but I
    // implemented this to stay closer to the MetaPost source code.
    // This might be something to look at for optimization.
    cvector<weqn> we=recalc(e);
    INFO(we);
    double theta0=solveForTheta0(we);
    return backsubCyclic(we, theta0);
  }
  else { /* Non-cyclic case. */
    /* First do row operations to get it into reduced echelon form. */
    cvector<eqn> el=ref(l,e).compute();

    /* Then, do back substitution. */
    return backsub(l,el).backCompute();
  }
}

// Once thetas have been solved, determine the first control point of every
// join.
struct postcontrolprop : public knotprop<pair> {
  cvector<pair>& dz;
  cvector<double>& psi;
  cvector<double>& theta;

  postcontrolprop(knotlist& l, cvector<pair>& dz,
                  cvector<double>& psi, cvector<double>& theta)
    : knotprop<pair>(l), dz(dz), psi(psi), theta(theta) {}

  double phi(Int j) {
    /* The third angle: psi + theta + phi = 0 */
    return -psi[j] - theta[j];
  }

  double vel(Int j) {
    /* Use the standard velocity function. */
    return velocity(theta[j],phi(j+1),l[j].tout);
  }

  // start is the same as mid.

  pair mid(Int j) {
    // Put a control point at the relative distance determined by the velocity,
    // and at an angle determined by theta.
    return l[j].z + vel(j)*expi(theta[j])*dz[j];
  }

  // The end postcontrol is the same as the last knot.
  pair end(Int j) {
    return l[j].z;
  }
};

// Determine the first control point of every join.
struct precontrolprop : public knotprop<pair> {
  cvector<pair>& dz;
  cvector<double>& psi;
  cvector<double>& theta;

  precontrolprop(knotlist& l, cvector<pair>& dz,
                 cvector<double>& psi, cvector<double>& theta)
    : knotprop<pair>(l), dz(dz), psi(psi), theta(theta) {}

  double phi(Int j) {
    return -psi[j] - theta[j];
  }

  double vel(Int j) {
    return velocity(phi(j),theta[j-1],l[j].tin);
  }

  // The start precontrol is the same as the first knot.
  pair start(Int j) {
    return l[j].z;
  }
  pair mid(Int j) {
    return l[j].z - vel(j)*expi(-phi(j))*dz[j-1];
  }

  // end is the same as mid.
};

// Puts solved controls into a protopath starting at the given index.
// By convention, the first knot is not coded, as it is assumed to be coded by
// the previous section (or it is the first breakpoint and encoded as a special
// case).
struct encodeControls : public knoteffect {
  protopath& p;
  Int k;
  cvector<pair>& pre;
  cvector<pair>& post;

  encodeControls(protopath& p, Int k,
                 cvector<pair>& pre, knotlist& l, cvector<pair>& post)
    : knoteffect(l), p(p), k(k), pre(pre), post(post) {}

  void encodePre(Int j) {
    p.pre(k+j)=pre[j];
  }
  void encodePoint(Int j) {
    p.point(k+j)=l[j].z;
  }
  void encodePost(Int j) {
    p.post(k+j)=post[j];
  }

  void solo(Int) {
#if 0
    encodePoint(j);
#endif
  }
  void start(Int j) {
#if 0
    encodePoint(j);
#endif
    encodePost(j);
  }
  void mid(Int j) {
    encodePre(j);
    encodePoint(j);
    encodePost(j);
  }
  void end(Int j) {
    encodePre(j);
    encodePoint(j);
  }
};

void encodeStraight(protopath& p, Int k, knotlist& l)
{
  pair a=l.front().z;
  double at=l.front().tout.val;
  pair b=l.back().z;
  double bt=l.back().tin.val;
  pair step=(b-a)/3.0;

  if (at==1.0 && bt==1.0) {
    p.straight(k)=true;
    p.post(k)=a+step;
    p.pre(k+1)=b-step;
    p.point(k+1)=b;
  }
  else {
    p.post(k)=a+step/at;
    p.pre(k+1)=b-step/bt;
    p.point(k+1)=b;
  }
}

void solveSection(protopath& p, Int k, knotlist& l)
{
  if (l.length()>0) {
    info(cerr, "solving section", l);

    // Calculate useful properties.
    cvector<pair>   dz  =  dzprop(l)   .compute();
    cvector<double> d   =   dprop(l,dz).compute();
    cvector<double> psi = psiprop(l,dz).compute();

    INFO(dz); INFO(d); INFO(psi);

    // Build and solve the linear equations for theta.
    cvector<eqn>        e = eqnprop(l,d,psi).compute();
    INFO(e);

    if (straightSection(e))
      // Handle straight section as special case.
      encodeStraight(p,k,l);
    else {
      cvector<double> theta = solveThetas(l,e);
      INFO(theta);

      // Calculate the control points.
      cvector<pair> post = postcontrolprop(l,dz,psi,theta).compute();
      cvector<pair> pre  =  precontrolprop(l,dz,psi,theta).compute();

      // Encode the results into the protopath.
      encodeControls(p,k,pre,l,post).exec();
    }
  }
}

// Find the first breakpoint in the knotlist, ie. where we can start solving a
// non-cyclic section.  If the knotlist is fully cyclic, then this returns
// NOBREAK.
// This must be called with a knot that has all of its implicit specifiers in
// place.
const Int NOBREAK=-1;
Int firstBreakpoint(knotlist& l)
{
  for (Int j=0;j<l.size();++j)
    if (!l[j].out->open())
      return j;
  return NOBREAK;
}

// Once a breakpoint, a, is found, find where the next breakpoint after it is.
// This must be called with a knot that has all of its implicit specifiers in
// place, so that breakpoint can be identified by either an in or out specifier
// that is not open.
Int nextBreakpoint(knotlist& l, Int a)
{
  // This is guaranteed to terminate if a is the index of a breakpoint.  If the
  // path is non-cyclic it will stop at or before the last knot which must be a
  // breakpoint.  If the path is cyclic, it will stop at or before looping back
  // around to a which is a breakpoint.
  Int j=a+1;
  while (l[j].in->open())
    ++j;
  return j;
}

// Write out the controls for section of the form
//   a.. control b and c ..d
void writeControls(protopath& p, Int a, knotlist& l)
{
  // By convention, the first point will already be encoded.
  p.straight(a)=dynamic_cast<controlSpec *>(l[a].out)->straight;
  p.post(a)=dynamic_cast<controlSpec *>(l[a].out)->cz;
  p.pre(a+1)=dynamic_cast<controlSpec *>(l[a+1].in)->cz;
  p.point(a+1)=l[a+1].z;
}

// Solves a path that has all of its specifiers laid out explicitly.
path solveSpecified(knotlist& l)
{
  protopath p(l.size(),l.cyclic());

  Int first=firstBreakpoint(l);
  if (first==NOBREAK)
    /* We are solving a fully cyclic path, so do it in one swoop. */
    solveSection(p,0,l);
  else {
    // Encode the first point.
    p.point(first)=l[first].z;

    // If the path is cyclic, we should stop where we started (modulo the
    // length of the path); otherwise, just stop at the end.
    Int last=l.cyclic() ? first+l.length()
      : l.length();
    Int a=first;
    while (a!=last) {
      if (l[a].out->controlled()) {
        assert(l[a+1].in->controlled());

        // Controls are already picked, just write them out.
        writeControls(p,a,l);
        ++a;
      }
      else {
        // Find the section a to b and solve it, putting the result (starting
        // from index a into our protopath.
        Int b=nextBreakpoint(l,a);
        subknotlist section(l,a,b);
        solveSection(p,a,section);
        a=b;
      }
    }

    // For a non-cyclic path, the end control points need to be set.
    p.controlEnds();
  }

  return p.fix();
}

/* If a knot is open on one side and restricted on the other, this replaces the
 * open side with a restriction determined by the restriction on the other
 * side. After this, any knot will either have two open specs or two
 * restrictions.
 */
struct partnerUp : public knoteffect {
  partnerUp(knotlist& l)
    : knoteffect(l) {}

  void mid(Int j) {
    knot& k=l[j];
    if (k.in->open() && !k.out->open())
      k.in=k.out->inPartner(k.z);
    else if (!k.in->open() && k.out->open())
      k.out=k.in->outPartner(k.z);
  }
};

/* Ensures a non-cyclic path has direction specifiers at the ends, adding curls
 * if there are none.
 */
void curlEnds(knotlist& l)
{
  static curlSpec endSpec;

  if (!l.cyclic()) {
    if (l.front().in->open())
      l.front().in=&endSpec;
    if (l.back().out->open())
      l.back().out=&endSpec;
  }
}

/* If a point occurs twice in a row in a knotlist, write in controls
 * between the two knots at that point (unless it already has controls).
 */
struct controlDuplicates : public knoteffect {
  controlDuplicates(knotlist& l)
    : knoteffect(l) {}

  void solo(Int) { /* One point ==> no duplicates */ }
  // start is the same as mid.
  void mid(Int j) {
    knot &k1=l[j];
    knot &k2=l[j+1];
    if (!k1.out->controlled() && k1.z==k2.z) {
      k1.out=k2.in=new controlSpec(k1.z,true);
    }
  }
  void end(Int) { /* No next point to compare with. */ }
};

path solve(knotlist& l)
{
  if (l.empty())
    return path();
  else {
    info(cerr, "input knotlist", l);
    curlEnds(l);
    controlDuplicates(l).exec();
    partnerUp(l).exec();
    info(cerr, "specified knotlist", l);
    return solveSpecified(l);
  }
}

// Code for Testing
#if 0
path solveSimple(cvector<pair>& z)
{
  // The two specifiers used: an open spec and a curl spec for the ends.
  spec open;

//  curlSpec curl;
//  curlSpec curly(2.0);
//  dirSpec E(0);
//  dirSpec N(PI/2.0);

  controlSpec here(pair(150,150));

  // Encode the knots as open in the knotlist.
  cvector<knot> nodes;
  for (cvector<pair>::iterator p=z.begin(); p!=z.end(); ++p) {
    knot k;
    k.z=*p;
    k.in=k.out=&open;

    nodes.push_back(k);
  }

  // Substitute in a curl spec for the ends.
  //nodes.front().out=nodes.back().in=&curl;

  // Test direction specifiers.
  //nodes.front().tout=2;
  //nodes.front().out=nodes.back().in=&curly;

  //nodes[0].out=nodes[0].in=&E;
  nodes[1].out=nodes[2].in=&here;

  simpleknotlist l(nodes,false);
  return solve(l);
}
#endif

} // namespace camp
