https://hal.archives-ouvertes.fr/hal-02398953
Raw File
Tip revision: c33910a29d53f4e137c225b21a8d59e43327cbf9 authored by Software Heritage on 08 December 2019, 12:26:32 UTC
hal: Deposit 351 in collection hal
Tip revision: c33910a
Polytope.cpp
#include "Polytope.h"
#include <cmath>
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <ctime>

using namespace std;
using namespace giac;

Polytope::Polytope(string s) {
  
  c_start = std::clock();
  
  verbose = false;

  K = CMfield(s);

  // options, to speed up process change some of these to false (some depend on each other though)
  emb_dim1 = true;
  emb_dim2 = true;
  check_cycles = true;
  check_euler = true;
  check_sphere = true;
  draw_pics = true;

  is_symmetric = true;
  
  isarithmeticitychecked = false;
  
  word_p1.push_back(1); // used if symmetric
  word_m1.push_back(-1);

  word_123.push_back(1); // used if non-symmmetric
  word_123.push_back(2);
  word_123.push_back(3);
  word_321.push_back(-3);
  word_321.push_back(-2);
  word_321.push_back(-1);

  w1.push_back(1);
  w2.push_back(2);
  w3.push_back(3);

  vecteur ipm;
  ipm.push_back(K.onenumber);
  ipm.push_back(K.zeronumber);
  ipm.push_back(K.onenumber);
  ipmin = gen(ipm);

  if (K.tag.at(0)=='m') {
    int k=1;
    bool done = false;
    string p_str = "";
    while (!done && k<K.tag.size()) {
      char te = K.tag.at(k);
      done = te=='-';
      if (!done) {
	p_str = p_str + te;
      }
      k++;
    }
    if (!done) {
      cerr << "Trouble reading tag" << endl;
      exit(1);
    }
    done = false;
    string tnum_str = "";
    while (!done && k<K.tag.size()) {
      char te = K.tag.at(k);
      done = te=='%';
      if (!done) {
	tnum_str = tnum_str + te;
      }
      k++;
    }
    if (!done) {
      cerr << "Trouble reading tag" << endl;
      exit(1);
    }
    done = false;
    string tden_str = "";
    while (k<K.tag.size()) {
      tden_str = tden_str + K.tag.at(k);
      k++;
    }
    pval = atoi(p_str.c_str());
    tnum = atoi(tnum_str.c_str());
    tden = atoi(tden_str.c_str());
    cout << "p=" << pval << endl;
    cout << "t=" << tnum << "/" << tden << endl;
    cout << endl;
  }
  else {
    if (K.tag.at(0)=='t') {
      // thompson group
      int k=1;
      bool done = false;
      string p_str = "";
      while (!done && k<K.tag.size()) {
	char te = K.tag.at(k);
	done = (te=='H' || te=='S' || te=='E');
	if (!done) {
	  p_str = p_str + te;
	}
	k++;
      }
      if (!done) {
	cerr << "Trouble reading tag" << endl;
	exit(1);
      }
      pval = atoi(p_str.c_str());
      cout << "p=" << pval << endl;
      cout << "T=" << K.tautag << endl;
      cout << endl;
    }
    else {
      // should be sporadic...
      int k=0;
      bool done = false;
      string p_str = "";
      while (!done && k<K.tag.size()) {
	char te = K.tag.at(k);
	done = te=='s';
	if (!done) {
	  p_str = p_str + te;
	}
	k++;
      }
      if (!done) {
	cerr << "Trouble reading tag" << endl;
	exit(1);
      }
      K.tautag = "s";
      while (k<K.tag.size()) {
	K.tautag = K.tautag + K.tag.at(k);
	k++;
      }
      pval = atoi(p_str.c_str());
      cout << "p=" << pval << endl;
      cout << "tau=" << K.tautag << endl;
      cout << endl;
    }
  }  

};

void Polytope::createGroup() {
  if (K.tag.at(0)=='m') {
    initMostowGroup();
  }
  else {
    if (K.tag.at(0)=='t') {
      initThompsonGroup();
    }
    else {
      initSporadicGroup();
    }
  }
}

void Polytope::startAlgo() {
  cout << "Creating initial prism..." << endl;
  Prism A0 = createPrism(e1w,e2w,e3w);  
  computeVertices(A0);
  addFaceOrbit(A0);
  // may also want to include its opposite face?

  cout << "Expanding family of prisms..." << endl;
  expand();
  //  cout << "Done with function startAlgo()" << endl;
}

void Polytope::initMostowGroup() {

  //  gen beta = eval(gen("2*sin(pi/"+print_INT_(pval)+")",&ct),1,&ct);
  //  gen beta_int = convert_interval(beta,K.nd,&ct);
  gen zeta = rootof(_cyclotomic(pval,&ct),&ct);
  gen zeta_int = convert_interval(zeta,K.nd,&ct);
  cout << "zeta interval=" << zeta_int << endl;
  cout << "zeta=" << zeta << endl;
  gen pol_zeta;
  if (operator_equal(simplify(zeta-K.onenumber,&ct),K.zeronumber,&ct) || operator_equal(simplify(zeta+K.onenumber,&ct),K.zeronumber,&ct)) {
    pol_zeta=z__IDNT_e-zeta;
  }
  else {
    cout << "Checked, not -1 nor 1" << endl;
    pol_zeta = giac::expand(_poly2symb(makesequence(_pmin(zeta,&ct),z__IDNT_e),&ct),&ct);
    cout << "min pol of zeta: " << pol_zeta << endl;
  
    if (!K.findInField(pol_zeta,zeta_int,zeta)) {
      cerr << "Trouble identifying reflection multiplier in field" << endl;
      exit(1);
    }

  }
  cout << "zeta in field: " << zeta << endl;

  gen arg_t3 = _simplify(((9*pval+2)*K.onenumber - 2*pval*tnum*K.onenumber/tden)/(4*pval*K.onenumber),&ct);
  int arg_t3_num = _numer(arg_t3,&ct).val;
  int arg_t3_den = _denom(arg_t3,&ct).val;
  arg_t3_num = arg_t3_num % arg_t3_den;
  gen t3ro;
  if (arg_t3_num==0) {
    t3ro = K.onenumber;
  }
  else {
    if (arg_t3_den==2) {
      // num must be one
      t3ro = -K.onenumber;
    }
    else {
      cout << "arg_t3/2pi: " << arg_t3_num << "/" << arg_t3_den << endl;
      t3ro = eval(_pow(makesequence(rootof(_cyclotomic(arg_t3_den,&ct),&ct),arg_t3_num),&ct),&ct);
      //      t3ro = eval(normal(_pow(makesequence(rootof(_cyclotomic(arg_t3_den,&ct),&ct),arg_t3_num),&ct),&ct),&ct);
    }
  }

  cout << "t3ro=" << t3ro << endl;
  gen t3_int = convert_interval(t3ro,K.nd,&ct);
  cout << "t3 int: " << t3_int << endl;
  
  gen t3;
  if (!operator_equal(t3ro,K.onenumber,&ct)) {
    //    gen pol_t3ro = giac::expand(_poly2symb(makesequence(_pmin(t3ro,&ct),z__IDNT_e),&ct),&ct);
    
    gen pol_t3ro = giac::expand(_poly2symb(makesequence(_pmin(eval(t3ro,&ct),&ct),z__IDNT_e),&ct),&ct);
    cout << "pol_t3ro=" << pol_t3ro << endl;
    if (! K.findInField(pol_t3ro,t3_int,t3)) {
      cerr << "Trouble identifying tau^3 in field" << endl;
      exit(1);
    }
  }
  else {
    t3 = K.onenumber;
  }
  cout << "t3 in field: " << t3 << endl;
  gen t3c = K.myconj(t3);
  cout << "t3c in field: " << t3c << endl;
  
  
  /*  gen alpha;

  if (!operator_equal(beta,K.onenumber,&ct)) {
    cout << eval(_pmin(beta,&ct),1,&ct) << endl;
    gen pol_beta = giac::expand(_poly2symb(makesequence(_pmin(beta,&ct),z__IDNT_e),&ct),&ct);
    cout << "min pol of 1/alpha: " << pol_beta << endl;
    
    if (! K.findInField(pol_beta,beta_int,beta)) {
      cerr << "Trouble identifying 1/alpha in field" << endl;
      exit(1);
    }
    alpha = K.inverse(beta);
  }
  else {
    alpha = K.onenumber;
    }*/


  /*  gen phi;
  // 2019 Oct 25, changed exp to rootof...
  //  gen phi_orig = eval(gen("exp(pi*i*"+print_INT_(tnum)+"/"+print_INT_(3*tden)+")",&ct),1,&ct);
  int tt = 6*tden;
  gen pp = normal(eval(gen("rootof(cyclotomic("+print_INT_(tt)+"))",&ct),1,&ct),&ct);
  gen phi_orig = normal(_pow(makesequence(pp,tnum),&ct),&ct);
  gen phi_int = convert_interval(phi_orig,K.nd,&ct);
  cout << "phi interval=" << phi_int << endl;
  //  cout << "phi alt=" << phi_alt << endl;
  cout << "phi=" << phi_orig << endl;
  
  if (!operator_equal(phi_orig,K.onenumber,&ct)) {
    cout << _pmin(phi_orig,&ct) << endl;
    gen pol_phi = giac::expand(_poly2symb(makesequence(_pmin(phi_orig,&ct),z__IDNT_e),&ct),&ct);
    cout << "min pol of phi: " << pol_phi << endl;
    
    if (!K.findInField(pol_phi,phi_int,phi)) {
      cerr << "Trouble identifying phase shift in field" << endl;
      exit(1);
    }
  }
  else {
    phi = K.onenumber;
    }*/

  taugen = K.taugen;
  cout << "Defining taugen of group: " << taugen << endl;
  cout << "   (" << _evalf(taugen,&ct) << ")" << endl;
  
  mult = zeta;
  multconj = K.myconj(mult);
  
  gen a = 2 - mult - multconj;
  gen b13 = (K.onenumber - multconj)*t3c;
  gen b12 = multconj - K.onenumber;

  K.red(a);
  K.red(b12);
  K.red(b13);

  vecteur coh(9);
  coh[0] = a;           coh[1] = b12;         coh[2] = b13;
  coh[3] = K.myconj(b12); coh[4] = a;           coh[5] = b12;
  coh[6] = K.myconj(b13); coh[7] = K.myconj(b12); coh[8] = a;
  H = K.createMatrix(coh);
  
  R1 = reflMat(K.e1,mult);
  R1i = reflMat(K.e1,multconj);
  R2 = reflMat(K.e2,mult);
  R2i = reflMat(K.e2,multconj);
  R3 = reflMat(K.e3,mult);
  R3i = reflMat(K.e3,multconj);

  gen bou = multconj*t3c;
  K.red(bou);
  
  vecteur coj(9);
  coj[0] = 0;  coj[1] = 0;    coj[2] = bou;
  coj[3] = 1;  coj[4] = 0;    coj[5] = 0;
  coj[6] = 0;  coj[7] = 1;    coj[8] = 0;
  J = K.createMatrix(coj);

  vecteur coji(9);
  coji[0] = 0;              coji[1] = 1;    coji[2] = 0;
  coji[3] = 0;              coji[4] = 0;    coji[5] = 1;
  coji[6] = K.myconj(bou);  coji[7] = 0;    coji[8] = 0;
  Ji = K.createMatrix(coji);

  is_symmetric = true;

  if (is_symmetric) {
    P = R1*J;
    Pi = Ji*R1i;
  }
  else {
    P = R1*R2*R3;
    Pi = R3i*R2i*R1i;
  }
  K.matred(P);
  K.matred(Pi);

  cout << "x is a root of " << K.minpol << endl;
  
  cout << endl;
  cout << "R1 has order " << K.order(R1) << endl;
  cout << "H=" << H << endl;
  cout << "R1=" << R1 << endl;
  cout << "J=" << J << endl;
  cout << "R1, R2 braid with order " << K.braidOrder(R1,R2) << endl;
  cout << endl;

  H_rootof = recursive_normal(eval(_subst(makesequence(H,x__IDNT_e,K.genasrootof),&ct),&ct),&ct);
  //  cout << "H as rootof: " << H_rootof << endl;

  orderofp = K.order(P);
  if (orderofp<0) {
    cerr << "I can't handle this group (yet), P has large (infinite?) order" << endl;
    exit(1);
  }
  else {
    cout << "P has order " << orderofp << endl;
  }

  // somehow the following doesn't work for some Mostow groups
  /*  gen axes_attempt;
  bool bo = my_jordan(P,axes_attempt);
  if (bo) {
    p0_rootof = _col(makesequence(axes_attempt,0),&ct);
  }
  else {
    cout << "Trouble computing fixed point of P in the ball" << endl;
    exit(1);
    }*/
  
  bool is_split = findEigenVectors_P();

  if (is_split) {

    p0 = _col(makesequence(Q,0),&ct);
    p0c = K.vectconj(p0);
    cout << endl;
    cout << "p0=" << p0 << endl;
  
    cout << "<p0,p0>=" << SqNorm(p0) << " (" << K.evali(SqNorm(p0)) << endl;
    gen imp0 = P*p0;
    K.vectred(imp0);
    cout << "Check p0 fixed by P? ";
    bool tf = K.areDep(p0,imp0);
    if (tf) {
      cout << " OK " << endl;
    }
    else {
      cout << " does not seem fixed :(" << endl;
      exit(1);
    }

    Ppower = K.Id;
    bool ir = false;
    orderofppower = 0;
    while (!ir && orderofppower<orderofp-1) {
      orderofppower++;
      Ppower = Ppower*P;
      K.matred(Ppower);
      gen dt = K.discrAdjusted(Ppower);
      ir = operator_equal(dt,K.zeronumber,&ct);
    }
    if (ir) {
      cout << "Power " << orderofppower << " of P is a complex reflection (or parabolic)" << endl;
    }    

    vector<int> w232b;
    w232b.push_back(2);
    w232b.push_back(3);
    w232b.push_back(-2);

    vector<int> w3b23;
    w3b23.push_back(-3);
    w3b23.push_back(2);
    w3b23.push_back(3);

    vector<int> w2323b2b;
    w2323b2b.push_back(2);
    w2323b2b.push_back(3);
    w2323b2b.push_back(2);
    w2323b2b.push_back(-3);
    w2323b2b.push_back(-2);

    createBraidRelation(word_p1,w2);
    createBraidRelation(word_p1,w232b);
    createBraidRelation(word_p1,w3b23);
    createBraidRelation(word_p1,w2323b2b);
    
    
    cout << "Relations:" << endl;
    for (int k=0; k<relations.size(); k++) {
      cout << "  " << convertword(relations[k]) << endl;
    }
    
    e1w = WVector(K.e1,w1);
    e2w = WVector(K.e2,w2);
    e3w = WVector(K.e3,w3);

  }
    
}

bool Polytope::findEigenVectors_P() {

  needppower = false;

  cout << "K.genasrootof=" << K.genasrootof << endl;
  gen st("j",&ct);
  gen tepo = simplify(K.minpol - gen("x^2+1",&ct),&ct);
  if (!operator_equal(tepo,K.zeronumber,&ct)) {
    cout << "Sto j with " << K.genasrootof << endl;
    //  }
    sto(st,K.genasrootof,&ct);
  }
  else {
    st = K.genasrootof;
    cout << "Skipping sto, can't do it with i" << endl;
  }
  
  //  if (verbose) {

  gen cpm0 = _charpoly(P,&ct);

  vecteur cpm1;
  for (int j=0; j<4; j++) {
    gen tej = cpm0[j];
    K.red(tej);
    cpm1.push_back(_subst(makesequence(tej,x__IDNT_e,eval(K.genasrootof,&ct)),&ct));
  }

  gen var = u__IDNT_e;
  
  gen charpol = normal(_poly2symb(makesequence(cpm1,var),&ct),&ct);

  //  gen charpol = _det(P - var*K.Id,&ct);
  //  K.red(charpol);
  //  cout << "charpol=" << charpol << endl;

  gen fa = _factors(makesequence(charpol,K.genasrootof),&ct);

  //  cout << "fa=" << fa << endl;
  
  bool split = true;
  for (int l=0; 2*l<(*fa._VECTptr).size(); l++) {
    int deg = _degree(makesequence(fa[2*l],var),&ct).val;
    if (deg>1) {
      split = false;
      cout << "In order to describe the axes of P, need to extend the ambient number field" << endl;
      cout << "  (this may well slow down the computations)" << endl;
      //   if (verbose) {
      cout << "Will now split " << fa[2*l] << endl;
	//      }
      gen avoid = eval(gen("u^2-i",&ct),&ct);
      gen test = _simplify(fa[2*l]-avoid,&ct);
      vecteur coe;
      if (operator_equal(test,K.zeronumber,&ct) && operator_equal(eval(gen("i",&ct),&ct),K.genasrootof,&ct)) {
	// ad hoc solution to handle m4-1%4 (annoying because of sto(i) impossible)
	coe.push_back(K.onenumber);
	coe.push_back(K.zeronumber);
	coe.push_back(x__IDNT_e);
      }
      else {
	for (int uu=deg; uu>=0; uu--) {
	  gen teuu = _simplify(_coeff(makesequence(fa[2*l],var,uu),&ct),&ct);
	  if (verbose) {
	    cout << "teuu=" << teuu << endl;
	  }
	  if (contains(teuu,st)) {
	    //	  cout << "Contains " << st << endl;
	    vecteur coe_u;
	    int subdeg = _degree(makesequence(teuu,st),&ct).val;
	    //	  cout << "subdeg=" << subdeg << endl;
	    for (int vv=subdeg; vv>=0; vv--) {
	      gen teuu_vv = _coeff(makesequence(teuu,st,vv),&ct);
	      coe_u.push_back(teuu_vv);
	    }
	    coe.push_back(giac::expand(_poly2symb(makesequence(coe_u,x__IDNT_e),&ct),&ct));
	  }
	  else {
	    if (teuu.type==8) {
	      if (teuu.is_symb_of_sommet(at_prod)) {
		gen yoo = _poly2symb(makesequence(_simplify(teuu[1][1]*teuu[2],&ct),x__IDNT_e),&ct);
		coe.push_back(yoo);
	      }
	      else {
		if (teuu.is_symb_of_sommet(at_rootof)) {
		  gen yoo = _poly2symb(makesequence(_simplify(teuu[1],&ct),x__IDNT_e),&ct);
		  coe.push_back(yoo);
		}
		else {
		  cout << "teuu=" << teuu << endl;
		  cout << "Problem reading factors!" << endl;
		}
	      }	 
	    }
	    else {
	      coe.push_back(teuu);
	    }
	  }
	}
      }
      vecteur eqs;
      eqs.push_back(K.minpol);
      eqs.push_back(giac::expand(_poly2symb(makesequence(coe,a__IDNT_e),&ct),&ct));
      vecteur vars;
      vars.push_back(x__IDNT_e);
      vars.push_back(a__IDNT_e);
      if (verbose) {
	cout << "eqs: " << eqs << endl;
	cout << "vars: " << vars << endl;
      }
      gen gb = _gbasis(makesequence(eqs,vars,change_subtype(_RUR_REVLEX,_INT_GROEBNER)),&ct);
      gen varprov = lidnt(gb[2])[0];
      gen para = _subst(makesequence(gb[2],varprov,x__IDNT_e),&ct);
      int dpara = _degree(para,&ct).val;
      if (gb[0]==-4)  {
	if (operator_equal(_coeff(makesequence(para,x__IDNT_e,dpara),&ct),-K.onenumber,&ct)) {
	  //	  if (verbose) {
	    cout << "New min pol: " << _simplify(-para,&ct) << endl;
	    //	  }
	  cout << endl;
	  cout << "Will start over with new field" << endl;
	  gen taugensave = K.taugen;
	  string tautagsave = K.tautag;
	  
	  //	  cout << "taugensave=" << taugensave << endl;
	  //	  cout << "  (" << _evalf(taugensave,&ct) << ")" << endl;
	  
	  gen Tsave = K.T;
	  K = CMfield(-para,K.tag);
	  K.taugen = taugensave;
	  K.tautag = tautagsave;
	  
	  //	  cout << "taugensave=" << taugensave << endl;
	  //	  cout << "  (" << _evalf(taugensave,&ct) << ")" << endl;

	  K.T = Tsave;
	  createGroup();
	}
	else {
	  cout << "New min pol: " << _simplify(para,&ct) << endl;		
	  cout << "Will start over with new field" << endl;
	  gen taugensave = K.taugen;

	  //	  cout << "taugensave=" << taugensave << endl;
	  //	  cout << "  (" << _evalf(taugensave,&ct) << ")" << endl;

	  gen Tsave = K.T;
	  K = CMfield(para,K.tag);
	  K.taugen = taugensave;

	  //	  cout << "taugensave=" << taugensave << endl;
	  //	  cout << "  (" << _evalf(taugensave,&ct) << ")" << endl;

	  K.T = Tsave;
	  createGroup();
	}
      }
      else {
	cerr << "Trouble computing primitive element" << endl;
	exit(1);
      }
    }
  }
  
  if (split) {

    gen Pk = P;
    Ppowers.push_back(K.Id);
    Ppowers.push_back(P);
    bool foundorder = false;
    int kk=1;
    while (!foundorder && kk<100) {
      kk++;
      Pk = Pk*P;
      K.matred(Pk);
      foundorder = K.isScalar(Pk);
      if (!foundorder) {
	Ppowers.push_back(Pk);
      }
    }
    if (foundorder) {
      orderofp = kk;
    }
    else {
      cout << "P has infinite (or at least very large) order, not sure what to do at this stage" << endl;
      exit(1);
    }

    cout << "Computations will take place in a number field of degree " << K.dc << endl;
    
    if (is_symmetric) {
      cout << "R1*J = P has order " << orderofp << endl;
    }
    else {
      cout << "R1*R2*R3 = P has order " << orderofp << endl;
    }
    
    vecteur evals_P;
    for (int l=0; 2*l<(*fa._VECTptr).size(); l++) {
      int deg = _degree(makesequence(fa[2*l],var),&ct).val; 
      if (deg>1) {
	cerr << "I thought we constructed an extension splitting the char poly of P!?" << endl;
	exit(1);
      }
      else {
	if (deg==1) {
	  gen te = _simplify(-_coeff(makesequence(fa[2*l],var,0),&ct)/_coeff(makesequence(fa[2*l],var,1),&ct),&ct);
	  if (contains(te,st)) {
	    int subdeg = _degree(makesequence(te,st),&ct).val;
	    vecteur te2;
	    for (int uu=subdeg; uu>=0; uu--) {
	      te2.push_back(_coeff(makesequence(te,st,uu),&ct));
	    }
	    evals_P.push_back(giac::expand(_poly2symb(makesequence(te2,x__IDNT_e),&ct),&ct));
	  }
	  else {
	    if (te.type==8) {
	      if (te.is_symb_of_sommet(at_prod)) {
		evals_P.push_back(giac::expand(_poly2symb(makesequence(_simplify(te[1][1]*te[2],&ct),x__IDNT_e),&ct),&ct));
	      }
	      else {
		if (te.is_symb_of_sommet(at_rootof)) {
		  evals_P.push_back(giac::expand(_poly2symb(makesequence(_simplify(te[1],&ct),x__IDNT_e),&ct),&ct));
		}
		else {
		  cout << "te=" << te << endl;
		  cout << "Problem reading factors!" << endl;
		}
	      }	 
	    }
	    else {
	      cout << "hopefully this is an integer (or at least rational): " << te << endl;
	      evals_P.push_back(te);
	    }
	  }
	}
      }
    }    
    
    vecteur evects_P;
    
    for (int k=0; k<evals_P.size(); k++) {
      gen U = _simplify(P-evals_P[k]*K.Id,&ct); // should have rank 2 in most cases   WELL NOT ALWAYS!
      K.matred(U);
      gen de = _det(U,&ct);
      K.red(de);
      
      gen X;
      vecteur U1;
      U1.push_back(U[0][1]*U[1][2]-U[1][1]*U[0][2]);
      U1.push_back(U[0][2]*U[1][0]-U[1][2]*U[0][0]);
      U1.push_back(U[0][0]*U[1][1]-U[1][0]*U[0][1]);
      X = gen(U1);
      K.vectred(X);
      if (operator_equal(X,K.zerovector,&ct)) {
	U1.clear();
	U1.push_back(U[1][1]*U[2][2]-U[1][1]*U[2][2]);
	U1.push_back(U[1][2]*U[2][0]-U[1][2]*U[2][0]);
	U1.push_back(U[1][0]*U[2][1]-U[1][0]*U[2][1]);
	X = gen(U1);
	K.vectred(X);
	if (operator_equal(X,K.zerovector,&ct)) {
	  U1.clear();
	  U1.push_back(U[2][1]*U[0][2]-U[0][1]*U[2][2]);
	  U1.push_back(U[2][2]*U[0][0]-U[0][2]*U[2][0]);
	  U1.push_back(U[2][0]*U[0][1]-U[0][0]*U[2][1]);
	  X = gen(U1);
	  K.vectred(X);
	  if (operator_equal(X,K.zerovector,&ct)) {
	    cerr << "Trouble computing eigenvectors of P" << endl;
	    exit(1);
	  }
	}
      }
      if (!operator_equal(X[0],K.zeronumber,&ct)) {
	gen x0i = K.inverse(X[0]);
	X = x0i*X;
	K.vectred(X);
      }
      else {
	if (!operator_equal(X[1],K.zeronumber,&ct)) {
	  gen x1i = K.inverse(X[1]);
	  X = x1i*X;
	  K.vectred(X);
	}
	else {
	  if (!operator_equal(X[2],K.zeronumber,&ct)) {
	    gen x2i = K.inverse(X[2]);
	    X = x2i*X;
	    K.vectred(X);
	  }
	}
      }
      evects_P.push_back(X);
    }

    gen TE = gen(evects_P);
    gen detTE =_det(TE,&ct);
    K.red(detTE);

    // may want to test that vectors are really orthogonal (this may not work in some cases...)
    for (int k1 = 0; k1<2; k1++) {
      for (int k2=k1+1; k2<3; k2++) {
	gen te = Inn(evects_P[k1],evects_P[k2]);
	if (!operator_equal(te,K.zeronumber,&ct)) {
	  cout << "te=" << te << endl;
	  cout << "value " << K.evali(te) << endl;
	  cerr << "Eigenvectors are not orthogonal, P is not regular elliptic" << endl;
	  exit(1);
	}
      }
    }

    int lneg;
    int count_neg = 0;
    for (int l=0; l<3; l++) {
      gen ns = SqNorm(evects_P[l]);
      int sgns;
      if (!K.checkSign(ns,sgns)){
	cerr << "Trouble checking sign of eigenvectors" << endl;
	exit(1);
      }
      else {
	if (sgns==0) {
	  cerr << "P is parabolic, I can't use any of its eigenvectors as center of the ball" << endl;
	  exit(1);
	}
	else {
	  if (sgns<0) {
	    count_neg++;
	    lneg = l;
	  }
	}
      }
    }
    
    if (count_neg!=1) {
      cout << "Trouble, found " << count_neg << " negative vectors among eigenvectors.." << endl;
      if (count_neg==2) {
	H = normal(-H,&ct);
      }
      else {
	cerr << "count_neg=" << count_neg << endl;
	exit(1);
      }
    }

    vecteur qcoo;
    qcoo.push_back(evects_P[lneg]);
    for (int uu=0; uu<3; uu++) {
      if (uu!=lneg) {
	qcoo.push_back(evects_P[uu]);
      }
    }    
    Q = _tran(gen(qcoo),&ct);

    vecteur evals_P_powers;
    evals_P_powers.push_back(K.onenumber);
    evals_P_powers.push_back(K.onenumber);
    evals_P_powers.push_back(K.onenumber);
    bool foundreflection = false;
    int po = 0;
    while (po<orderofp-1 && !foundreflection) {
      po++;
      for (int k=0; k<evals_P.size(); k++) {
	evals_P_powers[k] = evals_P_powers[k]*evals_P[k];
	K.red(evals_P_powers[k]);
      }
      if (operator_equal(evals_P_powers[0],evals_P_powers[1],&ct)) {
	if (operator_equal(evals_P_powers[0],evals_P_powers[2],&ct)) {
	  cerr << "P^" << po << " has a triple eigenvalue, this is suspicious!" << endl;
	  exit(1);
	}
	foundreflection = true;
	cout << "P^" << po << " has a repeated eigenvalue" << endl;
	gen te = SqNorm(evects_P[2]);
	int sign;
	if (K.checkSign(te,sign)) {
	  if (sign>0) {
	    cout << "Other eigenvector is positive, this is a reflection in a complex line" << endl;
	    extramirror = evects_P[2];
	    orderofppower = po;	    
	    needppower = true;
	  }
	  else {
	    if (sign==0) {
	      cerr << "Found an ideal fixed point, P is parabolic" << endl;
	      cerr << "This should not happen here" << endl;
	      exit(1);
	    }
	    else {
	      cout << "Other eigenvector is negative, reflection in a point" << endl;	      
	      // MAY STILL NEED TO CHECK POSSIBLE RIDGES STABILIZED BY THAT POWER??
	    }
	  }
	}
	else {
	  cerr << "Trouble checking sign of eigenvector of P" << endl;
	  exit(1);
	}
      } 

      if (!foundreflection && operator_equal(evals_P_powers[0],evals_P_powers[2],&ct)) {
	foundreflection = true;
	cout << "P^" << po << " has a repeated eigenvalue" << endl;
	gen te = SqNorm(evects_P[1]);
	int sign;
	if (K.checkSign(te,sign)) {
	  if (sign>0) {
	    cout << "Other eigenvector is positive, this is a reflection in a complex line" << endl;
	    extramirror = evects_P[1];
	    orderofppower = po;
	    needppower = true;
	  }
	  else {
	    if (sign==0) {
	      cerr << "Found an ideal fixed point, P is parabolic" << endl;
	      cerr << "This should not happen here" << endl;
	      exit(1);
	    }
	    else {
	      cout << "Other eigenvector is negative, reflection in a point" << endl;	      
	    }
	  }
	}
	else {
	  cerr << "Trouble checking sign of eigenvector of P" << endl;
	  exit(1);
	}
      } 

      if (!foundreflection && operator_equal(evals_P_powers[1],evals_P_powers[2],&ct)) {
	foundreflection = true;
	cout << "P^" << po << " has a repeated eigenvalue" << endl;
	gen te = SqNorm(evects_P[0]);
	int sign;
	if (K.checkSign(te,sign)) {
	  if (sign>0) {
	    cout << "Other eigenvector is positive, this is a reflection in a complex line" << endl;
	    extramirror = evects_P[0];
	    orderofppower = po;
	    needppower = true;
	  }
	  else {
	    if (sign==0) {
	      cerr << "Found an ideal fixed point, P is parabolic" << endl;
	      cerr << "This should not happen here" << endl;
	      exit(1);
	    }
	    else {
	      cout << "Other eigenvector is negative, reflection in a point" << endl;	      
	    }
	  }
	}
	else {
	  cerr << "Trouble checking sign of eigenvector of P" << endl;
	  exit(1);
	}
      } 
      
    }

  }

  return split;

}

void Polytope::initThompsonGroup() {

  is_symmetric = false;

  if (verbose) {
    cout << "Initializing Thompson group..." << endl;
  }
  // taugen makes no sense here, need to think about this
  
  gen zeta;
  if (pval>2) {
    zeta = rootof(_cyclotomic(pval,&ct),&ct);
    gen zeta_int = convert_interval(zeta,K.nd,&ct);
    if (verbose) {
      cout << "zeta interval=" << zeta_int << endl;
    }
    zeta = _simplify(zeta,&ct);
    if (verbose) {
      cout << "zeta=" << zeta << endl;
    }
    gen pol_zeta;
    if (operator_equal(simplify(zeta-K.onenumber,&ct),K.zeronumber,&ct) || operator_equal(simplify(zeta+K.onenumber,&ct),K.zeronumber,&ct)) {
      pol_zeta=z__IDNT_e-zeta;
    }
    else {
      pol_zeta = giac::expand(_poly2symb(makesequence(_pmin(zeta,&ct),z__IDNT_e),&ct),&ct);
    }
    if (verbose) {
      cout << "min pol of zeta: " << pol_zeta << endl;
    }
    if (!K.findInField(pol_zeta,zeta_int,mult)) {
      cerr << "Trouble identifying reflection multiplier in field" << endl;
      exit(1);
    }
    multconj = K.myconj(mult);
  }
  else {
    mult = -K.onenumber;
    multconj = -K.onenumber;
  }

  gen r = K.T[0];
  gen r_int = convert_interval(r,K.nd,&ct);
  if (verbose) {
    cout << "rho interval=" << r_int << endl;
  }
  r = _simplify(r,&ct);
  if (verbose) {
    cout << "rho=" << r << endl;
  }
  gen pol_r;
  if (operator_equal(r,K.onenumber,&ct) || operator_equal(r,-K.onenumber,&ct)) {
    pol_r=z__IDNT_e-r;
  }
  else {
    pol_r = giac::expand(_poly2symb(makesequence(_pmin(r,&ct),z__IDNT_e),&ct),&ct);
  }
  if (verbose) {
    cout << "min pol of rho: " << pol_r << endl;
  }
  if (!K.findInField(pol_r,r_int,r)) {
    cerr << "Trouble identifying reflection multiplier in field" << endl;
    exit(1);
  }
  
  gen s = K.T[1];
  gen s_int = convert_interval(s,K.nd,&ct);
  if (verbose) {
    cout << "sigma interval=" << s_int << endl;
  }
  s = _simplify(s,&ct);
  if (verbose) {
    cout << "sigma=" << s << endl;
  }
  gen pol_s;
  if (operator_equal(s,K.onenumber,&ct) || operator_equal(s,-K.onenumber,&ct)) {
    pol_s=z__IDNT_e-s;
  }
  else {
    pol_s = giac::expand(_poly2symb(makesequence(_pmin(s,&ct),z__IDNT_e),&ct),&ct);
  }
  if (verbose) {
    cout << "min pol of sigma: " << pol_s << endl;
  }
  if (!K.findInField(pol_s,s_int,s)) {
    cerr << "Trouble identifying reflection multiplier in field" << endl;
    exit(1);
  }
  
  gen t = K.T[2];
  gen t_int = convert_interval(t,K.nd,&ct);
  if (verbose) {
    cout << "tau interval=" << t_int << endl;
  }
  t = _simplify(t,&ct);
  if (verbose) {
    cout << "tau=" << t << endl;
  }
  gen pol_t;
  if (operator_equal(t,K.onenumber,&ct) || operator_equal(t,-K.onenumber,&ct)) {
    pol_t=z__IDNT_e-t;
  }
  else {
    pol_t = giac::expand(_poly2symb(makesequence(_pmin(t,&ct),z__IDNT_e),&ct),&ct);
  }
  if (verbose) {
    cout << "min pol of tau: " << pol_t << endl;
  }
  if (!K.findInField(pol_t,t_int,t)) {
    cerr << "Trouble identifying reflection multiplier in field" << endl;
    exit(1);
  }

  gen rc = K.myconj(r);
  gen sc = K.myconj(s);
  gen tc = K.myconj(t);
    
  gen a = 2-mult-multconj;
  gen b12 = (multconj-1)*r;
  gen b31 = (1-mult)*t;
  gen b23 = (multconj-1)*s;
  K.red(a);
  K.red(b12);
  K.red(b31);
  K.red(b23);

  vecteur coh(9);
  coh[0] = a;           coh[1] = b12;         coh[2] = K.myconj(b31);
  coh[3] = K.myconj(b12); coh[4] = a;           coh[5] = b23;
  coh[6] = b31; coh[7] = K.myconj(b23); coh[8] = a;
  H = K.createMatrix(coh);

  vecteur cor1(9);
  cor1[0] = mult;  cor1[1] = r;  cor1[2] = -tc;
  cor1[3] = 0;  cor1[4] = 1;    cor1[5] = 0;
  cor1[6] = 0;  cor1[7] = 0;    cor1[8] = 1;
  R1 = K.createMatrix(cor1);
  K.matred(R1);

  vecteur cor2(9);
  cor2[0] = 1;  cor2[1] = 0;  cor2[2] = 0;
  cor2[3] = -rc*mult;  cor2[4] = mult;    cor2[5] = s;
  cor2[6] = 0;  cor2[7] = 0;    cor2[8] = 1;
  R2 = K.createMatrix(cor2);
  K.matred(R2);

  vecteur cor3(9);
  cor3[0] = 1;  cor3[1] = 0;    cor3[2] = 0;
  cor3[3] = 0;  cor3[4] = 1;    cor3[5] = 0;
  cor3[6] = t*mult;  cor3[7] = -sc*mult;    cor3[8] = mult;
  R3 = K.createMatrix(cor3);
  K.matred(R3);

  vecteur cor1i(9);
  cor1i[0] = multconj;  cor1i[1] = -r*multconj;  cor1i[2] = tc*multconj;
  cor1i[3] = 0;  cor1i[4] = 1;    cor1i[5] = 0;
  cor1i[6] = 0;  cor1i[7] = 0;    cor1i[8] = 1;
  R1i = K.createMatrix(cor1i);
  K.matred(R1i);

  vecteur cor2i(9);
  cor2i[0] = 1;  cor2i[1] = 0;  cor2i[2] = 0;
  cor2i[3] = rc;  cor2i[4] = multconj;    cor2i[5] = -s*multconj;
  cor2i[6] = 0;  cor2i[7] = 0;    cor2i[8] = 1;
  R2i = K.createMatrix(cor2i);
  K.matred(R2i);

  vecteur cor3i(9);
  cor3i[0] = 1;  cor3i[1] = 0;    cor3i[2] = 0;
  cor3i[3] = 0;  cor3i[4] = 1;    cor3i[5] = 0;
  cor3i[6] = -t;  cor3i[7] = sc;    cor3i[8] = multconj;
  R3i = K.createMatrix(cor3i);
  K.matred(R3i);

  if (verbose) {
    cout << "R1:" << R1 << endl;
    cout << "R2:" << R2 << endl;
    cout << "R3:" << R3 << endl;
    cout << "H:" << H << endl;
  }
  
  H_rootof = recursive_normal(_subst(makesequence(H,x__IDNT_e,K.genasrootof),&ct),&ct);
  //  cout << "H as rootof: " << H_rootof << endl;

  gen tede = K.det(H);
  if (verbose) {
    cout << "det(H)=" << tede << ",   " << K.evali(tede) << endl;
  }

  P = R1*R2*R3;
  Pi = R3i*R2i*R1i;
  K.matred(P);
  K.matred(Pi);

  cout << "P=" << normal(_subst(makesequence(P,x__IDNT_e,K.genasrootof),&ct),&ct) << endl;
  
  orderofp = K.order(P);
  if (orderofp<0) {
    cerr << "I can't handle this group (yet), P has large (infinite?) order" << endl;
    exit(1);
  }
  else {
    cout << "P has order " << orderofp << endl;
  }

  /*  gen axes_attempt, evs_attempt;
  bool bo = my_jordan(P,axes_attempt,evs_attempt);
  if (bo) {
    p0_rootof = _col(makesequence(axes_attempt,0),&ct);
    is_p0_rootof_computed = true;
  }
  else {
    cout << "Trouble computing fixed point of P in the ball" << endl;
    exit(1);
    }*/

  bool is_split = findEigenVectors_P();

  /*  gen M = P*P*P*R2i;
  K.matred(M);
  cout << "M:=" << M << ";" << endl;
  cout << "x_val:=" << K.genasrootof << endl;
  gen axes_m, ev_m;
  my_jordan(M,axes_m,ev_m);
  exit(1);*/
  
  if (is_split) {

    p0 = _col(makesequence(Q,0),&ct);
    p0c = K.vectconj(p0);
    cout << endl;
    cout << "p0=" << p0 << endl;

    gen p0_12 = R1*R2*p0;
    K.vectred(p0_12);
    gen p0_13 = R1*R3*p0;
    K.vectred(p0_13);
    gen p0_23 = R3i*R2i*p0;
    K.vectred(p0_23);
    gen p0_1232 = R1*R2*R3*R2i*p0;
    K.vectred(p0_1232);
    if (K.areDep(p0,p0_12)) {
      cout << "p0 fixed by 12" << endl;
    }
    if (K.areDep(p0,p0_13)) {
      cout << "p0 fixed by 13" << endl;
    }
    if (K.areDep(p0,p0_23)) {
      cout << "p0 fixed by 23" << endl;
    }
    if (K.areDep(p0,p0_1232)) {
      cout << "p0 fixed by 1232b" << endl;
    }
    //    exit(1);

    Ppower = K.Id;
    bool ir = false;
    orderofppower = 0;
    while (!ir && orderofppower<orderofp-1) {
      orderofppower++;
      Ppower = Ppower*P;
      K.matred(Ppower);
      gen dt = K.discrAdjusted(Ppower);
      ir = operator_equal(dt,K.zeronumber,&ct);
    }
    if (ir) {
      cout << "Power " << orderofppower << " of P is (probably) a complex reflection!" << endl;
      // not quite right! this does not work for s1 for instance...
    }
    else {
      cout << "No power of P is a complex reflection!" << endl;  
    }


    vector<int> w121b;
    w121b.push_back(1);
    w121b.push_back(2);
    w121b.push_back(-1);

    vector<int> w2b12;
    w2b12.push_back(-2);
    w2b12.push_back(1);
    w2b12.push_back(2);

    vector<int> w232b;
    w232b.push_back(2);
    w232b.push_back(3);
    w232b.push_back(-2);

    vector<int> w3b23;
    w3b23.push_back(-3);
    w3b23.push_back(2);
    w3b23.push_back(3);

    vector<int> w313b;
    w313b.push_back(3);
    w313b.push_back(1);
    w313b.push_back(-3);

    vector<int> w1b31;
    w1b31.push_back(-1);
    w1b31.push_back(3);
    w1b31.push_back(1);

    createBraidRelation(word_p1,w2);
    createBraidRelation(w2,w3);
    createBraidRelation(w3,w1);

    createBraidRelation(word_p1,w232b);
    createBraidRelation(word_p1,w3b23);
    createBraidRelation(w2,w313b);
    createBraidRelation(w2,w1b31);
    createBraidRelation(w3,w121b);
    createBraidRelation(w3,w2b12);

    cout << "Relations:" << endl;
    for (int k=0; k<relations.size(); k++) {
      cout << "  " << convertword(relations[k]) << endl;
    }

    e1w = WVector(K.e1,w1);
    e2w = WVector(K.e2,w2);
    e3w = WVector(K.e3,w3);

  }

}

void Polytope::initSporadicGroup() {

  if (verbose) {
    cout << "Initializing sporadic group..." << endl;
  }

  taugen = K.taugen;
  gen sigma_int = convert_interval(taugen,K.nd,&ct);
  gen sigma = _simplify(taugen,&ct);
  if (!operator_equal(sigma,K.onenumber,&ct)) {
    gen pol_sigma = giac::expand(_poly2symb(makesequence(_pmin(sigma,&ct),z__IDNT_e),&ct),&ct);
    if (!K.findInField(pol_sigma,sigma_int,tau)) {
      cerr << "Trouble identifying sigma in field" << endl;
      exit(1);
    }
  }
  tauc = K.myconj(tau);
  if (verbose) {
    cout << "sigma=" << tau << endl;
    cout << " evali: " << K.evali(tau) << endl;
  }
  
  gen zeta = gen("exp(2*pi*i/"+print_INT_(pval)+")",&ct);
  gen zeta_int = convert_interval(zeta,K.nd,&ct);
  if (verbose) {
    cout << "zeta interval=" << zeta_int << endl;
  }
  zeta = _simplify(zeta,&ct);
  if (verbose) {
    cout << "zeta=" << zeta << endl;
  }
  gen pol_zeta;
  if (operator_equal(simplify(zeta-K.onenumber,&ct),K.zeronumber,&ct) || operator_equal(simplify(zeta+K.onenumber,&ct),K.zeronumber,&ct)) {
    pol_zeta=z__IDNT_e-zeta;
    mult = zeta;
  }
  else {
    pol_zeta = giac::expand(_poly2symb(makesequence(_pmin(zeta,&ct),z__IDNT_e),&ct),&ct);
    //cout << _pmin(zeta,&ct) << endl;
    if (verbose) {
      cout << "min pol of zeta: " << pol_zeta << endl;
    }
    if (!K.findInField(pol_zeta,zeta_int,mult)) {
      cerr << "Trouble identifying reflection multiplier in field" << endl;
      exit(1);
    }
  }

  if (verbose) {
    cout << "mult=" << mult << endl;
  }
  multconj = K.myconj(mult);

  if (K.tautag!="s5") {
    cout << "Not s5" << endl;

    gen a = 2-mult-multconj;
    gen b12 = (multconj-1)*tau;
    gen b13 = (1-multconj)*tauc;
    K.red(a);
    K.red(b12);
    K.red(b13);
  
    vecteur coh(9);
    coh[0] = a;           coh[1] = b12;         coh[2] = b13;
    coh[3] = K.myconj(b12); coh[4] = a;           coh[5] = b12;
    coh[6] = K.myconj(b13); coh[7] = K.myconj(b12); coh[8] = a;
    H = K.createMatrix(coh);
  
    vecteur cor1(9);
    cor1[0] = mult;  cor1[1] = tau;  cor1[2] = -tauc;
    cor1[3] = 0;  cor1[4] = 1;    cor1[5] = 0;
    cor1[6] = 0;  cor1[7] = 0;    cor1[8] = 1;
    R1 = K.createMatrix(cor1);
    K.matred(R1);
    
    vecteur cor2(9);
    cor2[0] = 1;  cor2[1] = 0;  cor2[2] = 0;
    cor2[3] = -tauc*mult;  cor2[4] = mult;    cor2[5] = tau;
    cor2[6] = 0;  cor2[7] = 0;    cor2[8] = 1;
    R2 = K.createMatrix(cor2);
    K.matred(R2);
    
    vecteur cor3(9);
    cor3[0] = 1;  cor3[1] = 0;    cor3[2] = 0;
    cor3[3] = 0;  cor3[4] = 1;    cor3[5] = 0;
    cor3[6] = tau*mult;  cor3[7] = -tauc*mult;    cor3[8] = mult;
    R3 = K.createMatrix(cor3);
    K.matred(R3);
    
    vecteur cor1i(9);
    cor1i[0] = multconj;  cor1i[1] = -tau*multconj;  cor1i[2] = tauc*multconj;
    cor1i[3] = 0;  cor1i[4] = 1;    cor1i[5] = 0;
    cor1i[6] = 0;  cor1i[7] = 0;    cor1i[8] = 1;
    R1i = K.createMatrix(cor1i);
    K.matred(R1i);
    
    vecteur cor2i(9);
    cor2i[0] = 1;  cor2i[1] = 0;  cor2i[2] = 0;
    cor2i[3] = tauc;  cor2i[4] = multconj;    cor2i[5] = -tau*multconj;
    cor2i[6] = 0;  cor2i[7] = 0;    cor2i[8] = 1;
    R2i = K.createMatrix(cor2i);
    K.matred(R2i);
    
    vecteur cor3i(9);
    cor3i[0] = 1;  cor3i[1] = 0;    cor3i[2] = 0;
    cor3i[3] = 0;  cor3i[4] = 1;    cor3i[5] = 0;
    cor3i[6] = -tau;  cor3i[7] = tauc;    cor3i[8] = multconj;
    R3i = K.createMatrix(cor3i);
    K.matred(R3i);
    
    vecteur coj(9);
    coj[0] = 0;  coj[1] = 0;    coj[2] = multconj;
    coj[3] = 1;  coj[4] = 0;    coj[5] = 0;
    coj[6] = 0;  coj[7] = 1;    coj[8] = 0;
    J = K.createMatrix(coj);
    
    vecteur coji(9);
    coji[0] = 0;  coji[1] = 1;    coji[2] = 0;
    coji[3] = 0;  coji[4] = 0;    coji[5] = 1;
    coji[6] = mult;  coji[7] = 0;    coji[8] = 0;
    Ji = K.createMatrix(coji);

  }
  else {
    // special case for s5 to make CM field (slightly) smaller

    cout << "This is an s5 group, I will reduce CM field (could do even better by not using p0)" << endl;
    
    gen ta0 = _eval(gen("(sqrt(5)+i*sqrt(3))/2",&ct),&ct);
    gen ta0_int = convert_interval(ta0,K.nd,&ct);
    //    cout << "ta0_int " << ta0_int << endl; 
    gen pol_ta = giac::expand(_poly2symb(makesequence(_pmin(ta0,&ct),z__IDNT_e),&ct),&ct);

    //    cout << "min pol of ta: " << pol_ta << endl;
    gen ta;
    if (!K.findInField(pol_ta,ta0_int,ta)) {
      cerr << "Trouble identifying alt tau (s5 case) in field" << endl;
      exit(1);
    }
    cout << "tau^3=" << ta << endl;
    
    gen tac;
    if (!K.findInField(pol_ta,conj(ta0_int,&ct),tac)) {
      cerr << "Trouble identifying conj of alt tau (s5 case) in field" << endl;
      exit(1);
    }
    //    cout << "tac=" << tac << endl;
    
    
    gen om0 = gen("(-1+i*sqrt(3))/2",&ct);
    gen om0_int = convert_interval(om0,K.nd,&ct);
    gen pol_om = eval(gen("z^2+z+1",&ct),&ct);
    //      giac::expand(_poly2symb(makesequence(_pmin(om0,&ct),z__IDNT_e),&ct),&ct);
    //    cout << "min pol of om: " << pol_om << endl;
    gen om;
    if (!K.findInField(pol_om,om0_int,om)) {
      cerr << "Trouble identifying omega in field" << endl;
      exit(1);
    }
    cout << "om=" << om << endl;

    gen omc = K.inverse(om);

    gen a = 2-mult-multconj;
    gen b12 = (multconj-1)*ta;
    gen b13 = (multconj-1)*tac*omc;
    K.red(a);
    K.red(b12);
    K.red(b13);
  
    vecteur coh(9);
    coh[0] = a;             coh[1] = b12;           coh[2] = b13;
    coh[3] = K.myconj(b12); coh[4] = a;             coh[5] = b12;
    coh[6] = K.myconj(b13); coh[7] = K.myconj(b12); coh[8] = a;
    H = K.createMatrix(coh);

    vecteur cor1(9);
    cor1[0] = mult;  cor1[1] = ta;  cor1[2] = tac*omc;
    cor1[3] = 0;  cor1[4] = 1;    cor1[5] = 0;
    cor1[6] = 0;  cor1[7] = 0;    cor1[8] = 1;
    R1 = K.createMatrix(cor1);
    K.matred(R1);
    
    vecteur cor2(9);
    cor2[0] = 1;  cor2[1] = 0;  cor2[2] = 0;
    cor2[3] = -mult*tac;  cor2[4] = mult;  cor2[5] = ta;
    cor2[6] = 0;  cor2[7] = 0;    cor2[8] = 1;
    R2 = K.createMatrix(cor2);
    K.matred(R2);
    
    vecteur cor3(9);
    cor3[0] = 1;  cor3[1] = 0;    cor3[2] = 0;
    cor3[3] = 0;  cor3[4] = 1;    cor3[5] = 0;
    cor3[6] = -ta*mult*om;  cor3[7] = -tac*mult;    cor3[8] = mult;
    R3 = K.createMatrix(cor3);
    K.matred(R3);
    
    vecteur cor1i(9);
    cor1i[0] = multconj;  cor1i[1] = -ta*multconj;  cor1i[2] = -tac*multconj*omc;
    cor1i[3] = 0;  cor1i[4] = 1;    cor1i[5] = 0;
    cor1i[6] = 0;  cor1i[7] = 0;    cor1i[8] = 1;
    R1i = K.createMatrix(cor1i);
    K.matred(R1i);
    
    vecteur cor2i(9);
    cor2i[0] = 1;  cor2i[1] = 0;  cor2i[2] = 0;
    cor2i[3] = tac;  cor2i[4] = multconj;    cor2i[5] = -ta*multconj;
    cor2i[6] = 0;  cor2i[7] = 0;    cor2i[8] = 1;
    R2i = K.createMatrix(cor2i);
    K.matred(R2i);
    
    vecteur cor3i(9);
    cor3i[0] = 1;  cor3i[1] = 0;    cor3i[2] = 0;
    cor3i[3] = 0;  cor3i[4] = 1;    cor3i[5] = 0;
    cor3i[6] = ta*om;  cor3i[7] = tac;    cor3i[8] = multconj;
    R3i = K.createMatrix(cor3i);
    K.matred(R3i);
    
    vecteur coj(9);
    coj[0] = 0;  coj[1] = 0;    coj[2] = -multconj*omc;
    coj[3] = 1;  coj[4] = 0;    coj[5] = 0;
    coj[6] = 0;  coj[7] = 1;    coj[8] = 0;
    J = K.createMatrix(coj);
    
    vecteur coji(9);
    coji[0] = 0;  coji[1] = 1;    coji[2] = 0;
    coji[3] = 0;  coji[4] = 0;    coji[5] = 1;
    coji[6] = -mult*om;  coji[7] = 0;    coji[8] = 0;
    Ji = K.createMatrix(coji);
    
  }
  
  //  cout << "Done creating matrices" << endl;


  if (is_symmetric) {
    //    cout << "Symmetric" << endl;
    P = R1*J;
    Pi = Ji*R1i;
  }
  else { 
    //    cout << "Not symmetric" << endl;
    P = R1*R2*R3;
    Pi = R3i*R2i*R1i;
  }
  K.matred(P);
  K.matred(Pi);
  
  gen tede = _det(H,&ct);
  K.red(tede);

  K.convertToReal(tede);


  //  orderofp = K.order(P);
  orderofp = K.order(P);
  if (orderofp<0) {
    cerr << "I can't handle this group (yet), P has large (infinite?) order" << endl;
    exit(1);
  }
  else {
    cout << "P has order " << orderofp << endl;
  }

  H_rootof = recursive_normal(_subst(makesequence(H,x__IDNT_e,K.genasrootof),&ct),&ct);
  //  cout << "H as rootof: " << H_rootof << endl;
  
  /*  gen axes_attempt, evs_attempt;
  bool bo = my_jordan(P,axes_attempt,evs_attempt);
  if (bo) {
    p0_rootof = _col(makesequence(axes_attempt,0),&ct);
    is_p0_rootof_computed = true;
  }
  else {
    cout << "Trouble computing fixed point of P in the ball" << endl;
    exit(1);
    }*/
  
  /*  gen M = P*P*P*R2i;
  K.matred(M);
  cout << "M:=" << M << ";" << endl;
  cout << "x_val:=" << K.genasrootof << endl;
  gen axes_m, ev_m;
  my_jordan(M,axes_m,ev_m);
  exit(1);*/
  
  bool is_split = findEigenVectors_P();

  if (is_split) {

    /*    cout << "K.minpol: " << K.minpol << endl;
       
    gen Mt = P*P*P*R2i;
    K.matred(Mt);
    int ot = K.linearorder(Mt);
    cout << "Order P*P*P*R2^-1: " << ot << endl;
    cout << "Mt=" << Mt << endl;
    
    gen A = Mt*Mt;
    K.matred(A);
    A = A*Mt;
    K.matred(A);

    gen miA;
    if (!findMirror(A,miA)) {
      cout << "Error in findMirror" << endl;
      exit(1);
    }

    cout << "miA=" << miA << endl;
    cout << " intervals: " << convert_interval(miA,K.nd,&ct);
    
    gen miAnum = _subst(makesequence(miA,x__IDNT_e,K.xvi),&ct);
    gen Hnum = _subst(makesequence(H,x__IDNT_e,K.xvi),&ct);
    
    vector<int> wt;
    wt.push_back(1);
    wt.push_back(2);
    wt.push_back(3);
    wt.push_back(-2);
    
    vecteur axes_attempt, evs_attempt;
    //    bool bo = my_jordan(Mt,axes_attempt,evs_attempt);
    bool bo = my_jordan_knowing_linear_order_alt(Mt,ot,axes_attempt,evs_attempt);
    if (bo) {
      cout << "axes: " << axes_attempt << endl;
      cout << "evs: " << evs_attempt << endl;
    }
    else {
      cout << "trouble in my_jordan" << endl;
    }

    for (int j=1; j<3; j++) {
      cout << "Inner prod mirror perp with axis " << j << ": " << conj(miAnum,&ct)*Hnum*axes_attempt[j] << endl;
    }
    //    exit(1);*/

    p0 = _col(makesequence(Q,0),&ct);
    p0c = K.vectconj(p0);
    cout << endl;

    gen imp0 = P*p0;
    K.vectred(imp0);
    cout << "Check p0 fixed by P? ";
    bool tf = K.areDep(p0,imp0);
    if (tf) {
      cout << " OK " << endl;
    }
    else {
      cout << " does not seem fixed :(" << endl;
      exit(1);
    }


  
    Ppower = K.Id;
    bool ir = false;
    orderofppower = 0;
    while (!ir && orderofppower<orderofp-1) {
      orderofppower++;
      Ppower = Ppower*P;
      K.matred(Ppower);
      gen dt = K.discrAdjusted(Ppower);
      ir = operator_equal(dt,K.zeronumber,&ct);
    }
    if (ir) {
      cout << "Power " << orderofppower << " of P is (probably) a complex reflection!" << endl;
      // not quite right! this does not work for s1 for instance...
    }
    else {
      cout << "No power of P is a complex reflection!" << endl;  
    }


    vector<int> w232b;
    w232b.push_back(2);
    w232b.push_back(3);
    w232b.push_back(-2);

    vector<int> w3b23;
    w3b23.push_back(-3);
    w3b23.push_back(2);
    w3b23.push_back(3);


    createBraidRelation(word_p1,w2);
    createBraidRelation(word_p1,w232b);
    createBraidRelation(word_p1,w3b23);


    cout << "Relations:" << endl;
    for (int k=0; k<relations.size(); k++) {
      cout << "  " << convertword(relations[k]) << endl;
    }

    e1w = WVector(K.e1,w1);
    e2w = WVector(K.e2,w2);
    e3w = WVector(K.e3,w3);

  }

}

gen Polytope::Proj(gen v, gen w) {
  gen fai = Inn(w,w);
  if (operator_equal(fai,K.zeronumber,&ct)) {
    cerr << "Do you really mean to divide by zero?" << endl;
    exit(1);
  }
  else {
    gen fa = K.inverse(fai);
    gen te = _simplify(v-Inn(v,w)*fa*w,&ct);
    K.vectred(te);  
    return te;
  }
}

gen Polytope::Inn(gen v, gen w) {
  gen res = K.vectconj(w)*H*v;
  K.red(res);
  return res;
}

gen Polytope::SqNorm(gen v) {
  gen res = K.vectconj(v)*H*v;
  K.red(res);
  return K.convertToReal(res);
}

gen Polytope::BoxProd(gen v, gen w) {
  gen X = K.vectconj(v)*H;
  gen Y = K.vectconj(w)*H;
  vecteur res(3);
  res[0] = X[1]*Y[2] - X[2]*Y[1];
  res[1] = X[2]*Y[0] - X[0]*Y[2];
  res[2] = X[0]*Y[1] - X[1]*Y[0];
  gen bp = gen(res);
  K.vectred(bp);
  return bp;
}

gen Polytope::refl(gen X, gen V, gen z) {
  gen te = Inn(V,V);
  if (te==K.zeronumber) {
    cerr << "Trying to reflect across a null vector!" << endl;
    exit(1);
  }
  else {
    gen yo = _simplify(X + (z-1)*Inn(X,V)*K.inverse(te)*V,&ct);
    K.vectred(yo);
    return yo;
  }
}

gen Polytope::reflMat(gen u, gen z) {
  vecteur te;
  gen te1 = refl(K.e1,u,z);
  gen te2 = refl(K.e2,u,z);
  gen te3 = refl(K.e3,u,z);
  for (int k=0; k<3; k++) {
    te.push_back(te1[k]);
    te.push_back(te2[k]);
    te.push_back(te3[k]);
  }
  return K.createMatrix(te);
}

Prism Polytope::createPrism(WVector v0, WVector v1, WVector v2){  

  Prism A;
  
  A.jstart = 0;

  A.mirror = v0;

  A.sides.push_back(v1); 
  A.sides.push_back(v2); 

  vector<int> w; // provisoire, on trouvera w plus tard
  A.apex = WVector(BoxProd(v1.coords,v2.coords),w);

  A.apexProjection =  Proj(A.apex.coords,A.mirror.coords);

  A.cspine =  BoxProd(A.apex.coords,A.apexProjection);

  // the apex projection should be inside complex hyperbolic space 
  //   (this is relevant only when the corresponding apex is outside)
  int sg;
  if (!K.checkSign(SqNorm(A.apexProjection),sg)) {
    cerr << "Problem checking sign" << endl;
    exit(1);
  }
  else {
    if (sg>-1) {
      cout << "Apex projection outside the ball!? (I will stop here)" << endl;
      exit(1);
    }
  } 

  // the complex spine should intersect complex hyperbolic space
  //   (otherwise there is no natural bisector, in principle could still use extors)
  if (!K.checkSign(SqNorm(A.cspine),sg)) {
    cerr << "Problem checking sign" << endl;
    exit(1);
  }
  else {
    if (sg<0) {
      cout << "Complex spine does not intersect the ball!? (I will stop here)" << endl;
      exit(1);
    }
  }

  // Could replace by the projection of any point not on the bisector...
  //   (then would need to know which one we want to take as X0,
  //          which is done by checking if it is on same side as p0).  
  A.X0 = Proj(p0,A.cspine); // should we test the signs?
  A.X1 = reflectAcrossSpine(A.X0,A);
 
  bool done = false;
  int n = 2;
  while (!done && n<100) {
    gen M1 = reflMat(A.sides[0].coords,mult);
    vector<int> wl = conjugateword(A.sides[0].word,A.sides[1].word); 
    gen vl = M1*A.sides[1].coords; 
    K.vectred(vl);
    WVector Vl(vl,wl);
    if (!testSame(Vl,A.sides[n-1])) {
      A.sides.insert(A.sides.begin(),Vl);
      n++;
      A.jstart++;
    }
    else {
      done = true;
    }
    if (!done) {
      gen M2 = reflMat(A.sides[n-1].coords,multconj);
      vector<int> wr = conjugateword(invertword(A.sides[n-1].word),A.sides[n-2].word);
      gen vr = M2*A.sides[n-2].coords;
      K.vectred(vr);
      WVector Vr(vr,wr);
      if (!testSame(Vr,A.sides[0])) {
	A.sides.push_back(Vr);
	n++;
      }
      else {
	done = true;
      }
    }
  }
  if (!done) {
    cerr << "Braiding cycle did not close up!" << endl;
    exit(1);
  }
  else {

    int k1 = 0;
    int l1 = A.sides[0].word.size();
    for (int k=1; k<A.sides.size(); k++) {
      int l = A.sides[k].word.size();
      if (l<l1) {
	l1 = l;
	k1 = k;
      }
    }
    int k2;
    if (k1==0) {
      if (A.sides[A.sides.size()-1].word.size()<A.sides[1].word.size()) {
	k2 = A.sides.size()-1;
      }
      else {
	k2 = 1;
      }
    }
    else {
      k2 = (k1+1)%A.sides.size();
    }

    gen M1 = reflMat(A.sides[k1].coords,mult)*reflMat(A.sides[k2].coords,mult);
    K.matred(M1);
    int om1 = K.order(M1);
    if (verbose) {
      cout << "Product of order " << om1 << endl;
    }
    int bo = K.braidOrder(reflMat(A.sides[k1].coords,mult),reflMat(A.sides[k2].coords,mult));
    if (verbose) {
      cout << "Braid length " << bo << endl;
    }
    if (bo==2) {
      cerr << "This pyramid is degenerate!" << endl;
    }

    vector<int> w0 = concat(A.sides[k1].word,A.sides[k2].word);
    vector<int> w = concat(A.sides[k1].word,A.sides[k2].word);

    bool isrefl = false;
    int k=1;
    gen M = M1;
    while (k<om1 && !isrefl) {
      gen dt = K.discrAdjusted(M);
      isrefl = operator_equal(dt,K.zeronumber,&ct); // may still be parabolic
      if (isrefl) {
	int sg;
	if (!K.checkSign(SqNorm(A.apex.coords),sg)) {
	  cerr << "Problem checking sign" << endl;
	  exit(1);
	}
	else {
	  if (sg>0) {
	    gen X1 = BoxProd(A.cspine,A.apex.coords);
	    gen X2 = M*X1;
	    K.vectred(X2);
	    isrefl = K.areDep(X1,X2);
	  }
	  else {
	    if (sg<0) {
	      gen X1 = A.apex.coords;
	      gen X2 = M*X1;
	      K.vectred(X2);
	      isrefl = K.areDep(X1,X2);
	    }
	    else {
	      if (verbose) {
		cout << "Mirror intersection is an ideal point, parabolic element" << endl;
	      }
	      // don't know if we want to do anything...
	      //	      cout << "sg=" << sg << endl;
	      //	      cout << "Problem finding reflection power" << endl;
	      //	      exit(1);
	    }
	  }
	}
      }
      if (isrefl) {
	if (verbose) {
	  cout << "Found reflection, power " << k << endl;
	}
	A.toppower = k;
	A.toporder = om1/k;
      }
      else {
	M = M*M1;
	w = concat(w,w0);
	K.matred(M);
      }
      k++;
    }
    if (!isrefl) {
      if (verbose) {
	cout << "Did not find any reflection power..." << endl;
      }
      //      exit(1);
    }

    useRelations(w);
    A.apex.word = w;
    if (verbose) {
      cout << "word for apex=" << convertword(w) << endl;
    }

    int ns = A.sides.size();
    if (verbose) {
      cout << "Constructed a pyramid with " << ns << " sides!" << endl;
    }
    A.isSurrounded = false;
    for (int k=0; k<ns; k++) {
      A.freeRidges.push_back(k);
    }
    return A;
  }

}

bool Polytope::isPrismNew(Prism A){
  int k=0;
  bool res = true;
  while (res && k<faces.size()) {
    res = !comparePrisms(A,faces[k]);
    k++;
  }
  return res;
}

bool Polytope::isVertexNew(WVector V) {
  return !isInList(V,vertices);
}

bool Polytope::comparePrisms(Prism A, Prism B) {
  int ns = A.sides.size();
  if (!testSame(A.mirror,B.mirror) || ns!=B.sides.size()) {
    return false;
  }
  else {
    return samePolygon(A.sides,B.sides);
  }
}

bool Polytope::samePolygon(vector<WVector> V, vector<WVector> W){
  int vs = V.size();
  if (W.size()!=vs) {
    return false;
  }
  else {
    bool foundsame = false;
    int k=0;
    while (!foundsame && k<vs) {
      foundsame = testSame(V[0],W[k]);
      k++;
    }
    if (!foundsame) {
      return false;
    }
    else {
      int l=1;
      bool same = true;
      while (same && l<V.size()) {
	k = k%vs;
	same = testSame(V[l],W[k]);
	k++;
	l++;
      }
      return same;
    }
  }
}

void Polytope::updateFreeRidges(){
  int nf = faces.size();
  int ns = faces[nf-1].sides.size();
  for (int k=0; k<ns; k++) {    
    // test if side #k is free...
    vector<WVector> ridge1;
    ridge1.push_back(faces[nf-1].sides[k]);
    ridge1.push_back(faces[nf-1].sides[(k+1)%ns]);
    ridge1.push_back(faces[nf-1].mirror);
    bool isfree = true;
    int l = 0;
    while (isfree && l<nf-1) {
      if (!faces[l].isSurrounded) {
	int m=0;
	bool foundNeighbor = false;
	while (!foundNeighbor && m<faces[l].freeRidges.size()) {
	  vector<WVector> ridge2;
	  int mm = faces[l].freeRidges[m];
	  ridge2.push_back(faces[l].sides[mm]);
	  ridge2.push_back(faces[l].sides[(mm+1)%faces[l].sides.size()]);
	  ridge2.push_back(faces[l].mirror);
	  if (samePolygon(ridge1,ridge2)) {
	    //	    cout << "Same ridge..." << endl;
	    //	    cout << "Filling face #" << (nf-1) << endl;
	    faces[nf-1].fillRidge(k);
	    //	    cout << "Filling face #" << (l) << endl;
	    faces[l].fillRidge(mm);
	    foundNeighbor = true;
	    isfree = false;
	  }
	  m++;
	}
      }
      l++;
      //      cout << "l=" << l << endl;
    }
  }
}

Prism Polytope::applyReflection(Prism A) {

  gen M = reflMat(A.mirror.coords,mult);
  vector<int> w = A.mirror.word;

  Prism A1;
 
  A1.toporder = A.toporder;
  A1.toppower = A.toppower;

  gen m = M*A.mirror.coords;
  K.vectred(m);
  vector<int> w1 = conjugateword(w,A.mirror.word);
  A1.mirror = WVector(m,w1);

  A1.apex = WVector(M*A.apex.coords,conjugateword(w,A.apex.word));
  K.vectred(A1.apex.coords);

  A1.apexProjection = M*A.apexProjection;
  K.vectred(A1.apexProjection);

  A1.cspine = M*A.cspine;
  K.vectred(A1.cspine);

  for (int k=0; k<A.sides.size(); k++) {
      vector<int> wk = conjugateword(w,A.sides[k].word);
      gen sk = M*A.sides[k].coords;
      K.vectred(sk);
      A1.sides.push_back(WVector(sk,wk));
  }

  for (int k=0; k<A.bottomface.size(); k++) {
      vector<int> wk = conjugateword(w,A.bottomface[k].word);
      gen sk = M*A.bottomface[k].coords;
      K.vectred(sk);
      A1.bottomface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.topface.size(); k++) {
      vector<int> wk = conjugateword(w,A.topface[k].word);
      gen sk = M*A.topface[k].coords;
      K.vectred(sk);
      A1.topface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.midvertices.size(); k++) {
      vector<int> wk = conjugateword(w,A.midvertices[k].word);
      gen sk = M*A.midvertices[k].coords;
      K.vectred(sk);
      A1.midvertices.push_back(WVector(sk,wk));
      A1.midvertindices.push_back(A.midvertindices[k]);
  }

  A1.isSurrounded = false;
  for (int k=0; k<A1.sides.size(); k++) {
    A1.freeRidges.push_back(k);
  }
  A1.jstart = A.jstart;
 
  return A1;
  
}

Prism Polytope::applyInverseReflection(Prism A) {

  gen M = reflMat(A.mirror.coords,multconj);
  vector<int> w = A.mirror.word;

  Prism A1;

  A1.toporder = A.toporder;
  A1.toppower = A.toppower;
 
  gen m = M*A.mirror.coords;
  K.vectred(m);
  vector<int> w1 = conjugateword(w,A.mirror.word);
  A1.mirror = WVector(m,w1);

  A1.apex = WVector(M*A.apex.coords,conjugateword(w,A.apex.word));
  K.vectred(A1.apex.coords);
  A1.apexProjection = M*A.apexProjection;
  K.vectred(A1.apexProjection);
  A1.cspine = M*A.cspine;
  K.vectred(A1.cspine);
  
  for (int k=0; k<A.sides.size(); k++) {
      vector<int> wk = conjugateword(w,A.sides[k].word);
      gen sk = M*A.sides[k].coords;
      K.vectred(sk);
      A1.sides.push_back(WVector(sk,wk));
  }

  for (int k=0; k<A.bottomface.size(); k++) {
      vector<int> wk = conjugateword(w,A.bottomface[k].word);
      gen sk = M*A.bottomface[k].coords;
      K.vectred(sk);
      A1.bottomface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.topface.size(); k++) {
      vector<int> wk = conjugateword(w,A.topface[k].word);
      gen sk = M*A.topface[k].coords;
      K.vectred(sk);
      A1.topface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.midvertices.size(); k++) {
      vector<int> wk = conjugateword(w,A.midvertices[k].word);
      gen sk = M*A.midvertices[k].coords;
      K.vectred(sk);
      A1.midvertices.push_back(WVector(sk,wk));
      A1.midvertindices.push_back(A.midvertindices[k]);
  }

  A1.isSurrounded = false;
  for (int k=0; k<A1.sides.size(); k++) {
    A1.freeRidges.push_back(k);
  }
  A1.jstart = A.jstart;

  return A1;
  
}

void Polytope::startFromFaceData() {

  cout << "Will now bypass the algorithm and import face data..." << endl;
  importFaceData();

}

void Polytope::expand() {
  cout << "There are now " << faces.size() << " faces." << endl;
  if (faces.size()>500) {
    cout << "Found over 500 faces, are you sure you want to continue?!" << endl;
  }
  else {
    bool closed = true;
    int nf = faces.size();
    int n = 0;
    while (closed && n < nf) {
      closed = faces[n].isSurrounded;
      n++;
    } 
    if (closed) {
      cout << "The polytope has no free ridge!" << endl;

      bool hasremoved = false;
      for (int k=0; k<facePOrbits.size(); k++) {
	cout << convertword(facePOrbits[k]) << endl;
      }
      for (int k=facePOrbits.size()-1; k>=0; k--) {
	if (faces[facePOrbits[k][0]].sides.size()<3) {
	  hasremoved = true;
	  //	  cout << "Should remove orbit #" << k << endl;
	  int nr = facePOrbits[k].size();
	  for (int l=nr-1; l>=0; l--) {
	    //           cout << "Removing face #" << facePOrbits[k][l] << endl;
            faces.erase(faces.begin()+facePOrbits[k][l]);
	  }
	  for (int m=facePOrbits.size()-1; m>k; m--) {
	    for (int n=0; n<facePOrbits[m].size(); n++) {
	      facePOrbits[m][n] =  facePOrbits[m][n] - nr;
	    }
	  }
          facePOrbits.erase(facePOrbits.begin()+k);
	  // need to adjust facePOrbits indices!!!!
        }
      }
      if (hasremoved) {
	cout << "REMOVED SOME DEGENERATE PRISMS (hopefully this has no unpleasant consequences)!!" << endl;
      }

      exportFaceData();

      checkPairings();

      computeAllVertices();      
      computeAllEdges();
      computeAllRidges();

      cout << endl;

      if (emb_dim1) {
	cout << "Will now check that the 1-skeleton is embedded (there are " << edgePOrbits.size() << " orbits of edges)" << endl;
	for (int k=0; k<edgePOrbits.size(); k++) {
	  checkEdge(edgePOrbits[k][0]);
	}
	cout << "If you read this, the 1-skeleton is embedded!" << endl;
	cout << endl;
      }

      if (emb_dim2) {
	cout << "Will now check that G-polygons defining ridges are embedded" << endl;
	cout << "   be patient, there are " << ridgePOrbits.size() << " P-orbits of ridges..." << endl;

	for (int k=0; k<ridgePOrbits.size(); k++) {
	  cout << endl;
	  cout << "Ridge P-orbit #" << k << "..." << endl;
	  checkRidge(ridgePOrbits[k][0]);
	}

	cout << "     " << endl;
	cout << " * * " << endl;
	cout << " \\_/ " << endl;
	cout << "     " << endl;
	cout << "If you read this, the program checked that the polytope for " << K.tag << " is embedded!" << endl;
	cout << " Used at most " << K.ndrecord << " digits to perform the computations." << endl;
	cout << endl;

      }

      if (check_cycles) {
	checkCycles();
	cout << endl;
      }

      if (check_euler) {
	computeEulerCharacteristic();
	cout << endl;
      }

      if (check_sphere) {
	checkLinks();
	checkSphere();
	cout << endl;
      }

      cout << endl;

      isArithmetic();
      /*      bool is_arith = isArithmetic();
      if (is_arith) {
	cout << "Sorry, this group is arithmetic." << endl;
      }
      else {
	cout << "The group is NOT arithmetic, NA(" << naindex << ")" << endl;
      }*/
      
      cout << endl;

      cout << "Done studying group " << K.tag << endl;
      cout << "If you read this message, all requested checks were successful:" << endl;
      if (emb_dim1) {
	cout << "    1-skeleton is embedded" << endl;
      }
      if (emb_dim2) {
	cout << "    2-skeleton is embedded" << endl;
      }
      if (check_sphere) {
	cout << "    The boundary of the polytope should be a sphere" << endl;
	cout << "      (to be completely sure, you should simplify the presentation using GAP)" << endl;
      }
      if (check_cycles) {
	cout << "    Hypotheses of the Poincaré polyhedron theorem hold" << endl;
      }
      if (check_euler) {
	cout << "    I computed the Euler characteristic, it is " << euler_char << endl;
      }

      if (iscompact) {
	cout << "The group is cocompact" << endl;
      }
      else {
	cout << "The group is not cocompact" << endl;
      }

      if (isarithmeticitychecked) {
	if (isarithmetic) {
	  cout << "The group is arithmetic" << endl;
	}
	else {
	  cout << "The group is non-arithmetic!" << endl;
	}
      }
      else {
	cout << "I did not check arithmeticity" << endl;
      }

      cout << endl;
      
      // do this only to produce a picture of the polyhedron
      if (draw_pics) {

	cout << "I will now draw pictures of the polyhedron, in projections onto the axes of P, one for each axis" << endl;
	cout << "The pictures will be stored in in the pics directory, they need to be compiled with asymptote." << endl;

	drawProjection(1);
	drawProjection(2);

	for (int k=0; k<facePOrbits.size(); k++) {
	  drawFace(facePOrbits[k][0]);
	  if (needppower) {
	    //	    cout << "extramirror: " << extramirror << endl;
	    gen te2 = Inn(faces[facePOrbits[k][0]].apexProjection,extramirror);	  
	    if (operator_equal(te2,K.zeronumber,&ct)) {
	      int nb = faces[facePOrbits[k][0]].bottomface.size();
	      if (nb%2==0) {
		nb = nb/2;
	      }
	      else {
		nb = (nb-3)/2;
	      }
	      drawFaceSector(facePOrbits[k][0],nb,nb+1);
	    }
	  }
	  else {
	    int nb = faces[facePOrbits[k][0]].bottomface.size();
	    if (nb%2==0) {
	      nb = nb/2;
	    }
	    else {
	      nb = (nb-3)/2;
	    }
	    drawFaceSector(facePOrbits[k][0],nb,nb+1);
	  }
	}
	cout << "Done drawing pics" << endl;
	c_end = std::clock();
	long double time = (c_end-c_start)/CLOCKS_PER_SEC;
	cout << "Total time elapsed : " << convert_time(time);
      }
    }
    else {

      // not closed 
      //   (this is the main part of the expand procedure)


      // Should not add degenerate prisms!!!

      gen M = reflMat(faces[n-1].mirror.coords,mult);
      gen Mi = reflMat(faces[n-1].mirror.coords,multconj);
      gen U0 = M*faces[n-1].sides[0].coords;
      K.vectred(U0);
      gen U1 = M*faces[n-1].sides[1].coords;
      K.vectred(U1);
      gen V0 = Mi*faces[n-1].sides[0].coords;
      K.vectred(V0);
      gen V1 = Mi*faces[n-1].sides[1].coords;
      K.vectred(V1);

      WVector U0w = WVector(U0,conjugateword(faces[n-1].mirror.word,faces[n-1].sides[0].word));
      WVector U1w = WVector(U1,conjugateword(faces[n-1].mirror.word,faces[n-1].sides[1].word));
      Prism B1 = createPrism(faces[n-1].mirror, U0w, U1w);

      WVector V0w = WVector(V0,conjugateword(invertword(faces[n-1].mirror.word),faces[n-1].sides[0].word));
      WVector V1w = WVector(V1,conjugateword(invertword(faces[n-1].mirror.word),faces[n-1].sides[1].word));
      Prism B2 = createPrism(faces[n-1].mirror, V0w, V1w);

      gen N1 = reflMat(U0,mult);
      gen N2 = reflMat(U1,mult);
      gen N3 = reflMat(V0,mult);
      gen N4 = reflMat(V1,mult);
      
      int oo1 = K.braidOrder(N1,N2);
      int oo2 = K.braidOrder(N3,N4);

      if (verbose) {
	if (oo1==2) {
	  cout << "SHOULD NOT CHOOSE B1, degenerate prism!" << endl;
	};
	if (oo2==2) {
	  cout << "SHOULD NOT CHOOSE B2, degenerate prism!" << endl;
	};

	cout << "Braid length for B1: " << oo1 << endl;
	cout << "Braid length for B2: " << oo2 << endl;

	cout << "B1: " << B1 << endl;
	cout << "B2: " << B2 << endl;

	cout << "John's test (for inverses)" << endl;
      }
	
      vector<int> wt1 = concat( faces[n-1].mirror.word, 
				concat(conjugateword(faces[n-1].mirror.word,faces[n-1].sides[0].word), 
				       conjugateword(faces[n-1].mirror.word,faces[n-1].sides[1].word)) );
      vector<int> wt2 = concat( concat(conjugateword(faces[n-1].mirror.word,faces[n-1].sides[0].word), 
				       conjugateword(faces[n-1].mirror.word,faces[n-1].sides[1].word)), 
				faces[n-1].mirror.word );

      vector<int> wt3 = concat( invertword(faces[n-1].mirror.word), 
				concat( conjugateword(invertword(faces[n-1].mirror.word),faces[n-1].sides[0].word), 
					conjugateword(invertword(faces[n-1].mirror.word),faces[n-1].sides[1].word)) );
      vector<int> wt4 = concat( concat(conjugateword(invertword(faces[n-1].mirror.word),faces[n-1].sides[0].word), 
				       conjugateword(invertword(faces[n-1].mirror.word),faces[n-1].sides[1].word)), 
				invertword(faces[n-1].mirror.word)); 
				
      useRelations(wt1);
      useRelations(wt2);
      useRelations(wt3);
      useRelations(wt4);

      if (verbose) {
	cout << "wt1=" << convertword(wt1) << endl;
	cout << "wt2=" << convertword(wt2) << endl;
	cout << "wt3=" << convertword(wt3) << endl;
	cout << "wt4=" << convertword(wt4) << endl;
      }
      
      bool test1 = (wt1==word_123 || wt2==word_123);                                                                                                 
      bool test2 = (wt3==word_123 || wt4==word_123);                                                                                                         
      
      bool done;
      if (test1) {                                                                                                                                           
	if (test2) {                                                                                                                                         
	  cerr << "John's test fails, both prisms work" << endl;                                                                                             
	  exit(1);                                                                                                                                           
	}                                                                                                                                                    
	else {                                                                                                                             
	  if (isPrismNew(B1)) {
	    computeVertices(B1);
	    useRelations(B1);                                                            
	    if (verbose) {
	      cout << "Adding new face.." << endl;
	    }
	    addFaceOrbit(B1);             
	    done = true;                                                                                     
	  }      
	}                                                                                                                                                    
      }                                                                                                                                                      
      else {                                                                                                                                                 
	if (test2) {                                                                                                                           
	  if (isPrismNew(B2)) {
	    computeVertices(B2);
	    useRelations(B2);                                                                  
	    if (verbose) {
	      cout << "Adding new face.." << endl;                                         }
	    
	    addFaceOrbit(B2);                                                                                    
	    done = true;                
	  }   
	}                                                                                                                                                    
      }                                                                                                                                                      

      int k = faces[n-1].freeRidges[0];
      if (verbose) {
	cout << "Creating two new prisms: " << endl;
      }
      
      gen M1 = reflMat(faces[n-1].mirror.coords,mult);
      gen M2 = reflMat(faces[n-1].sides[k].coords,mult);
      gen M3 = reflMat(faces[n-1].sides[(k+1)%faces[n-1].sides.size()].coords,mult);
      gen M4 = reflMat(faces[n-1].mirror.coords,mult);
      
      int o1 = K.braidOrder(M1,M2);
      int o2 = K.braidOrder(M3,M4);

      if (verbose) {
	cout << "Braid length for A1: " << o1 << endl;
	cout << "Braid length for A2: " << o2 << endl;

	if (o1==2) {
	  cout << "SHOULD NOT CHOOSE A1, degenerate prism!" << endl;
	};
	if (o2==2) {
	  cout << "SHOULD NOT CHOOSE A2, degenerate prism!" << endl;
	};
      }

      Prism A1 = createPrism(faces[n-1].sides[(k+1)%faces[n-1].sides.size()], 
			     faces[n-1].mirror, 
			     faces[n-1].sides[k]);
      Prism A2 = createPrism(faces[n-1].sides[k], 
			     faces[n-1].sides[(k+1)%faces[n-1].sides.size()], 
			     faces[n-1].mirror);

      if (verbose) {
	cout << "A1=" << A1 << endl;
	cout << "A2=" << A2 << endl;
	
	cout << "John's test" << endl;
      }
      
      wt1 = concat( A1.mirror.word,
				concat(faces[n-1].mirror.word, faces[n-1].sides[k].word) );
      wt2 = concat( concat(faces[n-1].mirror.word, faces[n-1].sides[k].word),
				A1.mirror.word );

      wt3 = concat( A2.mirror.word, concat(faces[n-1].sides[(k+1)%faces[n-1].sides.size()].word, 
				       faces[n-1].mirror.word) );
      wt4 = concat( concat(faces[n-1].sides[(k+1)%faces[n-1].sides.size()].word, faces[n-1].mirror.word),
				A2.mirror.word );

      useRelations(wt1);
      useRelations(wt2);
      useRelations(wt3);
      useRelations(wt4);

      if (verbose) {
	cout << "test words for A1: " << convertword(wt1) << "  " << convertword(wt2) << endl;
	cout << "test words for A2: " << convertword(wt3) << "  " << convertword(wt4) << endl;
      }
      
      test1 = (wt1==word_123 || wt2==word_123);                                                                     
      test2 = (wt3==word_123 || wt4==word_123);                                                                 
      if (test1) {                                                                             
	if (test2) {                                                                                 
	  cerr << "John's test fails, both prisms work" << endl;                                                 
	  exit(1);                                                                                        
	}                                                                                                       
	else {                                                                                                  
	  if (isPrismNew(A1)) {
	    computeVertices(A1);
	    useRelations(A1);                                                                        
	    if (verbose) {
	      cout << "Adding new face (A1).." << endl;                                     }
	    
	    addFaceOrbit(A1);                                                                          
	    done = true;                                                                            
	  }                                                                                                       
	}                                                                                                  
      }                                                                                                          
      else {                                                                                                                                                 
	if (test2) {                                                                                         
	  if (isPrismNew(A2)) { 
	    computeVertices(A2);
	    useRelations(A2);                                                                             
	    if (verbose) {
	      cout << "Adding new face (A2).." << endl;                                    }
	    
	    addFaceOrbit(A2);                                                                           
	    done = true;                                                                                      
	  }              
	}                                                                                                                                                    
      }                                                                                                                                                      
                                                                                                                                                                     
      expand();
    }    
  }
}

vecteur Polytope::eulerdim0() {

  gen ed0_top = K.zeronumber; // to keep track of topological euler char (without orbifold weights)

  gen ed0 = K.zeronumber;
  vector<vector<int> > vertexOrbits;
  for (int k=0; k<vertexPOrbits.size(); k++) {
    vector<int> ok;
    ok.push_back(k);
    vertexOrbits.push_back(ok);
  }

  for (int k=0; k<vertexPOrbitPairings.size(); k++) {
    cout << vertexPOrbitPairings[k][0] << " -> " << vertexPOrbitPairings[k][1] << endl;
    cout << "   via " << convertword(vertexPOrbitPairingMaps[k]) << endl;

    int u1 = -1;
    bool fd1 = false;
    while (!fd1 && u1!=vertexOrbits.size()-1) {
      u1++;
      int v1 = -1;
      while (!fd1 && v1!=vertexOrbits[u1].size()-1) {
	v1++;
	fd1 = vertexPOrbitPairings[k][0]==vertexOrbits[u1][v1];
      }
    }
    int u2 = -1;
    bool fd2 = false;
    while (!fd2 && u2!=vertexOrbits.size()-1) {
      u2++;
      int v2 = -1;
      while (!fd2 && v2!=vertexOrbits[u2].size()-1) {
	v2++;
	fd2 = vertexPOrbitPairings[k][1]==vertexOrbits[u2][v2];
      }
    }
    if (!fd1 || !fd1) {
      cerr << "Problem identifiying indices in vertexPOrbits..." << endl;
      exit(1);
    }
    else {
      if (u1!=u2) {
	for (int z=0; z<vertexOrbits[u2].size(); z++) {
	  vertexOrbits[u1].push_back(vertexOrbits[u2][z]);
	}
	vertexOrbits.erase(vertexOrbits.begin()+u2);
      }
    }
    // probably better to construct the graph right away, to compute its connected components, then the fund group of each component...
    //   (I will not do that for now, this is not the time-consuming part of the program)

  }
  iscompact = true;
  cout << "Vertex orbits (under side pairings, * denotes an ideal vertex): " << endl;
  for (int z=0; z<vertexOrbits.size(); z++) {
    cout << "[";
    cout << convertword(vertexOrbits[z]) << "]";
    if (vertices[vertexPOrbits[vertexOrbits[z][0]][0]].faceindices[0]==-1) {
      iscompact = false;
      cout << "*";
    }
    cout << ",  ";
  }
  cout << endl;
  cout << "There are " << vertexOrbits.size() << " orbits of vertices" << endl;

  cout << "Still need to compute the order of stabilizer (only when finite vertex!)" << endl;
  for (int z=0; z<vertexOrbits.size(); z++) {
    vector<int> vo = vertexOrbits[z];

    bool needpowerofp;
    int baseptindex=0;
    int bpi;
    if (needppower) {
      needpowerofp = false;
      bpi = -1;
      while (!needpowerofp && bpi!=vo.size()-1) {
	bpi++;
	gen X = vertices[vertexPOrbits[vo[bpi]][0]].coords;
	needpowerofp = operator_equal(Inn(extramirror,X),K.zeronumber,&ct);
      }
      if (needpowerofp) {
	baseptindex = bpi;
      }
    } 

    cout << "Computing stabilizer of vertex P-Orbit #" << vo[baseptindex] << endl;
    if (needpowerofp) {
	cout << "Some element in this orbit (index " << bpi << ") is on mirror of power of P!" << endl;
    }

    gen XX = vertices[vertexPOrbits[vo[baseptindex]][0]].coords;
    vector<int> u1, u2;	
    for (int k=0; k<faces.size(); k++) {
      Prism A = faces[k];
      gen teXX = Inn(XX,A.mirror.coords);
      K.red(teXX);
      if (is_zero(teXX,&ct)) {
	cout << "On mirror of " << A << endl;
	u1 = A.mirror.word;
      }
      teXX = Inn(XX,A.apex.coords);
      K.red(teXX);
      if (is_zero(teXX,&ct) && A.topface.size()>1) {
	cout << "On top face of " << A << endl;
	u2 = concat(A.sides[A.jstart].word, A.sides[(A.jstart+1)%A.sides.size()].word),
	  cout << "(" << convertword(u2) << ")^" << A.toppower << " (refl of order " << A.toporder << ")" << endl;
      }
    }
	// not sure what to do with u1, u2, are there better choices?
	// a given mirror will be on many mirrors...

    vector<vector<int> > stab;
    if (vo.size()>1) {
      vector<vector<int> > graph_edges;
      vector<vector<int> > maps;
      for (int uu=0; uu<vertexPOrbitPairings.size(); uu++) {
	if (find(vo.begin(),vo.end(),vertexPOrbitPairings[uu][0])!=vo.end()) {
	  graph_edges.push_back(vertexPOrbitPairings[uu]);
	  maps.push_back(vertexPOrbitPairingMaps[uu]);
	}
      }
      sgraph G(graph_edges);
      
      vector<vector<int> > stabgens;
      vector<vector<bool> > orientations;
      G.fundamental_group(vo[baseptindex],stabgens,orientations);
      for (int kk=0; kk<stabgens.size(); kk++) {
	vector<int> yo;
	for (int ll=0; ll<stabgens[kk].size(); ll++) {
	  if (orientations[kk][ll]) {
	    yo = concat(maps[stabgens[kk][ll]],yo);
	  }
	  else {
	    yo = concat(invertword(maps[stabgens[kk][ll]]),yo);
	  }
	}
	stab.push_back(yo);
      }
    }
    else {
      // there is a single vertex, won't use graph routine for this (just take all pairings as generators)
      for (int uu=0; uu<vertexPOrbitPairings.size(); uu++) {
	if (find(vo.begin(),vo.end(),vertexPOrbitPairings[uu][0])!=vo.end()) {
	  stab.push_back(vertexPOrbitPairingMaps[uu]);
	}
      }
    }
    cout << "Generators of stab:" << endl;
    vector<gen> stabgens;
    vector<int> trivial_elts;
    for (int y=0; y<stab.size(); y++) {
      if (stab[y].size()>0) {
	cout << "  " << convertword(stab[y]) << endl;
	gen M = evalWord(stab[y]);
	
	int orm = K.order(M);

	if (orm>0) {
	  cout << "   (order " << orm << ")" << endl;
	}
	else {
	  cout << "Infinite order" << endl;
	}

	if (orm>1) {
	  gen dt = K.discrAdjusted(M);
	  if (operator_equal(dt,K.zeronumber,&ct)){

	    gen mirr;
	    if (!findMirror(M,mirr)) {
	      cout << "Trouble finding mirror" << endl;
	      exit(1);
	    }
	    gen te = SqNorm(mirr);

	    if (verbose) {
	      cout << "te=" << te << endl;
	    }
	    
	    int sg;
	    if (K.checkSign(te,sg)) {
	      if (sg<0) {
		cout << "    (\"reflection\" in point)" << endl;
	      }
	      else {
		if (sg>0) {
		  cout << "    (reflection in line)" << endl;		
		}
		else {
		  cout << "Single e-val gives ideal point, should this happen??" << endl;
		}
	      }
	    }
	    else {
	      cout << "Trouble locating mirror (inside or outside the ball)" << endl;
	      exit(1);	    
	    }
	  }
	  else {
	    // distinct eigenvalues, may want to compute rotation angles
	    cout << "    (distinct eigenvalues)" << endl;
	    cout << endl;
	  }
	}
	
	if (!K.isScalar(M)) {
	  stabgens.push_back(M);
	  gen Z1 = vertices[vertexPOrbits[vo[baseptindex]][0]].coords;
	  gen Z2 = M*Z1;
	  K.vectred(Z2);
	  if (K.areDep(Z1,Z2)) {
	    cout << "Check!" << endl;
	  }
	  else {
	    cout << "Problem, matrix is actually not in stab!!" << endl;	    
	  }
	}
	else {
	  cout << convertword(stab[y]) << " is identity" << endl;
	  trivial_elts.push_back(y);
	}
      }
    }
    for (int z=trivial_elts.size()-1; z>=0; z--) {
      stab.erase(stab.begin()+trivial_elts[z]);
    }
    cout << endl;

    if (vertices[vertexPOrbits[vo[baseptindex]][0]].faceindices[0]!=-1) {
      // the commented out method runs slow, to be used only for small groups
      /*	cout << "Creating finite group.." << endl;
		FiniteGroup L(tag);
		L.addElements(stabgens);
		L.expandToGroup();*/
       
      gen og;
      gen ogmax = K.zeronumber;
      bool needpowerofp;
      if (needppower) {
	needpowerofp = operator_equal(Inn(extramirror,vertices[vertexPOrbits[vo[0]][0]].coords),K.zeronumber,&ct);
      }
      else {
	needpowerofp = false;
      }

      if (needpowerofp) {

      	cout << "THIS POINT IS ON THE MIRROR OF P^" << orderofppower << endl;

       	stabgens.push_back(Ppower);

      }

      /*
       * SHOULD WORK WITH 2x2 MATRICES HERE!
       *   may also want to use the fact that we have a candidate for the group, where we know the center?
       *
       */

      //      if (K.tag!="5s4c" && K.tag!="3s5") {
      if (true) {
	// for the excluded groups we have large stabilizers..
	
	vector<gen> sg2d;
	// would like to find a mirror of reflection first
	gen mii;
	bool foundrefl = false;
	int zz=-1;      
	while (!foundrefl && zz!=stab.size()-1) {
	  zz++;
	  vector<int> w = stab[zz];
	  if (w.size()%2==1) {
	    int n = (w.size()-1)/2;
	    bool is_pal = true;
	    int uu=0;
	    while (is_pal && uu<n) {
	      is_pal = (w[uu]==-w[w.size()-1-uu]);
	      uu++;
	    }
	    foundrefl = is_pal;
	  }
	}
	if (foundrefl) {
	  if (verbose) {
	    cout << "Found a reflection: " << convertword(stab[zz]) << endl;
	  }
	  int n = (stab[zz].size()-1)/2;
	  mii = _col(makesequence(K.Id,abs(stab[zz][n])-1),&ct);
	  if (n>0) {
	    vector<int> w;
	    for (int jj=0; jj<n; jj++) {
	      w.push_back(stab[zz][jj]);
	    }
	    mii = evalWord(w)*mii;
	    K.vectred(mii);
	  }
	  if (verbose) {
	    cout << "mirror=" << mii << endl;
	  }
	}
	else {
	  // use any vector and do Gramm-Schmidt?
	  mii = K.e1; // (this will not always work?!)
	}

	vecteur basis;
	basis.push_back(vertices[vertexPOrbits[vo[baseptindex]][0]].coords);
	basis.push_back( mii );
	basis.push_back(BoxProd(basis[0],basis[1]));
	
	gen A = _tran(gen(basis),&ct);
	gen Ai = K.matinverse(A);
	for (int tt=0; tt<stabgens.size(); tt++) {
	  gen M = Ai*stabgens[tt]*A;
	  K.matred(M);
	  gen coe = K.inverse(M[0][0]);
	  vecteur r1;
	  r1.push_back(M[1][1]*coe);
	  r1.push_back(M[1][2]*coe);
	  vecteur r2;
	  r2.push_back(M[2][1]*coe);
	  r2.push_back(M[2][2]*coe);
	  vecteur rows;
	  rows.push_back(r1);
	  rows.push_back(r2);
	  gen R = gen(rows);
	  K.matred(R);
	  sg2d.push_back(R);
	}

	FiniteLinearGroup L(K);
	L.addElements(sg2d);

	int te_ord_gp;
	bool ters = L.studyReflectionSubgroup(te_ord_gp);

	//	if (false) {
	if (ters) {
	  cout << "The stabilizer is a 2-generator reflection group, computation is easy" << endl;
	  cout << "  (it has order " << te_ord_gp << ")" << endl;
	  ogmax = te_ord_gp;
	  cout << endl;
	}
	else {
	  cout << "The stabilizer is a not 2-generator reflection group" << endl;	  
	  cout << "I'm not quite sure how to get the order of stabilizer, will use brute force (this may take some time!)" << endl;
	  //	  cout << "Creating finite group (will first conjugate to linear group of 2x2 matrices).." << endl;
	  cout << endl;

	  L.expandToGroup();
	  
	  ogmax = L.elements.size();    

	  vector<int> reflinds;
	  for (int uu=1; uu<L.elements.size(); uu++) {
	    gen M = L.elements[uu];
	    int ouu = K.linearorder2d(M);
	    if (ouu>1) {
	      gen Mi = normal(M-K.Id2,&ct);
	      gen dt = K.det(Mi);
	      if (operator_equal(dt,K.zeronumber,&ct)){
		reflinds.push_back(uu);
	      }
	    }
	  }
	  cout << "There are " << reflinds.size() << " reflections in this group" << endl;

	  FiniteLinearGroup LR(K);
	  for (int uu=0; uu<reflinds.size(); uu++) {
	    LR.addElement(L.elements[reflinds[uu]]);
	  }
	  LR.expandToGroup();
	  cout << "Subgroup generated by reflections has order " << LR.elements.size() << endl;
	  
	  if (LR.elements.size()<L.elements.size()) {
	    cout << "The stabilizer is NOT generated by reflections, this will give a singular point of the quotient" << endl;
	    cout << endl;
	  }
	  else {
	    cout << "The stabilizer is generated by reflections!" << endl;
	    cout << endl;
	  }
	  cout << endl;

	}
	
	
	//	cout << "Stab is generated by " << L.elements.size() << " elements" << endl;

      }
      else {

	cout << "This group has a vertex stabilizer of very high order!" << endl;
	cout << "I will use the order of the known stabilizer, this is not valid in all generality (but it is in this case)" << endl;

	vector<WVector> mi = findMirrors(vertices[vertexPOrbits[vo[baseptindex]][0]]);
	vector<gen> stabgens_guess;
	for (int kk=0; kk<mi.size(); kk++) {
	  cout << "Mirror " << convertword(mi[kk].word) << " contains vertex" << endl;
	  stabgens_guess.push_back(evalWord(mi[kk].word));
	}
	for (int kk=0; kk<mi.size(); kk++) {
	  stabgens_guess.push_back(evalWord(invertword(mi[kk].word)));
	}
	int nsg = stabgens_guess.size()/2;

	int kk_max=0;
	int ll_max=0;
	for (int kk=0; kk<nsg; kk++) {
	  int ok = K.order(stabgens_guess[kk]);
	  for (int ll=kk+1; ll<nsg; ll++) {
	    int ol = K.order(stabgens_guess[ll]);
	    int te = K.braidOrder(stabgens_guess[kk],stabgens_guess[ll],20); // 10 is small, but it works for sporadic groups...
	    if (te>-1) {
	      cout << "Stab gens #" << kk << " (order " << ok << ") and " << ll << " (order " << ol << ") braid with order " << te << endl;
	      if (te>2) {
		og = gen("8",&ct);
		gen den = K.onenumber/ok + K.onenumber/ol + K.twonumber/te - K.onenumber; // formula from Mostow, say
		og = og/(te*den*den);
		cout << "  these generate a group of order " << og << endl;
	      }
	      else {
		if (te==2) {
		  og = ok*ol;
		}
		else {
		  cerr << "Braid length 0 or 1, this should not happen??" << endl;
		  exit(1);
		}
	      }
	      if (is_greater(og,ogmax,&ct)) {
		ogmax = og;
		kk_max = kk;
		ll_max = ll;
	      } 
	    }
	  }
	}
	cout << "Order of stab should be " << ogmax << endl;
	cout << "  (should check that the generators from incidence graph are all in the guessed stabilizer)" << endl;
	
      }
    
      cout << endl;
      ed0 = ed0+K.onenumber/ogmax;
      ed0_top = ed0_top+K.onenumber;
    }
  }
  vecteur te;
  te.push_back(ed0);
  te.push_back(ed0_top);
  return te;

}

bool Polytope::expressAsWord(gen M, vecteur list, gen fixed_pt, gen mirror, vector<int> &inds) {
  // is never used anywhere, I believe
  
  int ndsave = K.nd;
  K.increasePrecision();

  inds.clear();
  gen other_vect = BoxProd(fixed_pt,mirror);
  vecteur ov;
  ov.push_back(fixed_pt);
  ov.push_back(mirror);
  ov.push_back(other_vect);
  gen O = _tran(gen(ov),&ct);
  // then need its inverse...
  gen detO = K.det(O);
  K.red(detO);
  if (operator_equal(detO,K.zeronumber,&ct)) {
    cerr << "Problem in express as word, I expected a basis of C^3" << endl;
    exit(1);
  }
  else {
    vecteur coi;
    coi.push_back(O[1][1]*O[2][2]-O[1][2]*O[2][1]);
    coi.push_back(-O[1][0]*O[2][2]+O[1][2]*O[2][0]);
    coi.push_back(O[1][0]*O[2][1]-O[1][1]*O[2][0]);
    coi.push_back(-O[0][1]*O[2][2]+O[0][2]*O[2][1]);
    coi.push_back(O[0][0]*O[2][2]-O[0][2]*O[2][0]);
    coi.push_back(-O[0][0]*O[2][1]+O[0][1]*O[2][0]);
    coi.push_back(O[0][1]*O[1][2]-O[0][2]*O[1][1]);
    coi.push_back(-O[0][0]*O[1][2]+O[0][2]*O[1][0]);
    coi.push_back(O[0][0]*O[1][1]-O[0][1]*O[1][0]);
    gen Oi = _tran(K.createMatrix(coi),&ct)*K.inverse(detO);
    K.matred(Oi);
    gen te = O*Oi;
    K.matred(te);
    cout << "Check, should be Id: " << te << endl;
  
    gen On = K.matevali(O);
    gen Oin = K.matevali(Oi);

    cout << "Oin: " << Oin << endl;

    bool found = false;
    int count = 0;
    gen base_pt = p0;
    // not shoud how important it is to take p0,
    //   we want to take something strictly inside the polytope

    gen goal = conj(Oin*(K.matevali(M)*p0),&ct);
    goal = goal/goal[0];
    cout << "goal: " << goal << endl;
    gen den = sqrt(re(goal[1]*conj(goal[1],&ct)+goal[2]*conj(goal[2],&ct),&ct),&ct);

    gen C = K.Id;
    while (!found && count<100) {
      gen max = convert_interval(-1,K.nd,&ct);
      int lval;
      for (int l=0; l<list.size(); l++) {
	gen U = Oin*(K.matevali(list[l])*base_pt);
	U = U/U[0];
	gen den2 = sqrt(re(U[1]*conj(U[1],&ct)+U[2]*conj(U[2],&ct),&ct),&ct);
	gen val = re(U[1]*goal[1]+U[2]*goal[2],&ct)/(den*den2);
	cout << "val #" << l << ":" << val << endl;
	if (is_greater(val,max+K.prec,&ct)) {
	  max = val;
	  lval = l;
	}
      }
      inds.insert(inds.begin(),lval);
      cout << "inds=" << convertword(inds) << endl;
      base_pt = K.matevali(list[lval])*base_pt;
      C = list[lval]*C;
      K.matred(C);
      found = K.isMultiple(C,M);
      count++;
    }
    return found;
  }
  K.nd = ndsave;
  K.lowerPrecision();
}

vecteur Polytope::eulerdim1() {

  gen res_top = K.zeronumber;

  gen res = K.zeronumber;

  vector<vector<int> > edgePOrbitPairings;
  vector<vector<int> > edgePOrbitPairingMaps;
  
  vector<vector<int> > edgeOrbits;
  for (int k=0; k<edgePOrbits.size(); k++) {
    vector<int> ok;
    ok.push_back(k);
    edgeOrbits.push_back(ok);
    vector<int> inds = edges[edgePOrbits[k][0]].faceindices;
    for (int l=0; l<inds.size(); l++) {
      int image = applyPairingToEdge(inds[l],edgePOrbits[k][0]);
      cout << "Pairing for face #" << inds[l] << " maps edge #" <<  edgePOrbits[k][0] << " to edge #" << image << endl;
      int m=-1;
      int n;
      bool fd = false;
      while (!fd && m!=edgePOrbits.size()-1) {
	m++;
	n = -1;
	while (!fd && n!=edgePOrbits[m].size()-1) {
	  n++;
	  fd = (image==edgePOrbits[m][n]);
	}
      }
      if (!fd) {
	cerr << "Problem identifying image edge in a P-orbit" << endl;
	exit(1);
      }
      else {
	cout << "This gives a map from P-orbit #" << k << " to P-orbit #" << m << ", at index " << n << endl;
	vector<int> pa;
	pa.push_back(k);
	pa.push_back(m);
	edgePOrbitPairings.push_back(pa);
	vector<int> w;
	if (faces[inds[l]].invertpairing) {
	  w = invertword(faces[inds[l]].mirror.word);
	}
	else {
	  w = concat(w,faces[inds[l]].mirror.word);
	}
	if (n>orderofp/2) {
	  for (int zz=0; zz<orderofp-n; zz++) {
	    w.insert(w.begin(),4);
	  }
	}
	else {
	  for (int zz=0; zz<n; zz++) {
	    w.insert(w.begin(),-4);
	  }
	}	 
	cout << "Map: " << convertword(w) << endl;
	edgePOrbitPairingMaps.push_back(w);
      }
    }
  }

  for (int k=0; k<edgePOrbitPairings.size(); k++) {
    int u1 = -1;
    bool fd1 = false;
    while (!fd1 && u1!=edgeOrbits.size()-1) {
      u1++;
      int v1 = -1;
      while (!fd1 && v1!=edgeOrbits[u1].size()-1) {
	v1++;
	fd1 = edgePOrbitPairings[k][0]==edgeOrbits[u1][v1];
      }
    }
    int u2 = -1;
    bool fd2 = false;
    while (!fd2 && u2!=edgeOrbits.size()-1) {
      u2++;
      int v2 = -1;
      while (!fd2 && v2!=edgeOrbits[u2].size()-1) {
	v2++;
	fd2 = edgePOrbitPairings[k][1]==edgeOrbits[u2][v2];
      }
    }
    if (!fd1 || !fd2) {
      cerr << "Problem identifiying indices in vertexPOrbits..." << endl;
      exit(1);
    }
    else {
      if (u1!=u2) {
	for (int z=0; z<edgeOrbits[u2].size(); z++) {
	  edgeOrbits[u1].push_back(edgeOrbits[u2][z]);
	}
	edgeOrbits.erase(edgeOrbits.begin()+u2);
      }
    }
  }

  cout << "Edge orbits (under side pairings): " << endl;
  for (int z=0; z<edgeOrbits.size(); z++) {
    cout << "[";
    cout << convertword(edgeOrbits[z]) << "]";
    cout << ",  ";
  }
  cout << endl;
  cout << "There are " << edgeOrbits.size() << " orbits of edges" << endl;


  cout << "Still need to compute the order of stabilizer..." << endl;
  for (int z=0; z<edgeOrbits.size(); z++) {

    vector<int> vo = edgeOrbits[z];

    bool needpowerofp = false;
    gen Xo, Xe;
    int baseptindex = 0;
    if (needppower) {
      needpowerofp = false;
      int bpi = -1;
      while (!needpowerofp && bpi!=vo.size()-1) {
	bpi++;
	Xo = vertices[edges[edgePOrbits[vo[bpi]][0]].origin].coords;
	Xe = vertices[edges[edgePOrbits[vo[bpi]][0]].end].coords;
	needpowerofp = operator_equal(Inn(extramirror,Xo),K.zeronumber,&ct) && operator_equal(Inn(extramirror,Xe),K.zeronumber,&ct);
	
	gen yo = Inn(extramirror,BoxProd(Xo,Xe));
	if (operator_equal(yo,K.zeronumber,&ct)) {
	  cout << "Edge is orthogonal to mirror of P^" << orderofppower << endl;
	  // need to check if some POWER of Ppower flips the edge!
	  gen A = Ppower;
	  int mmax = (orderofp/orderofppower)/2;
	  int m = 1;
	  gen Xoi = Xo;
	  gen Xei = Xe;
	  bool fd = false;
	  while (m<=mmax && !fd) {
	    Xoi = Ppower*Xoi;
	    K.vectred(Xoi);
	    Xei = Ppower*Xei;
	    K.vectred(Xei);
	    A = A*Ppower;
	    K.matred(A);
	    fd = (K.areDep(Xoi,Xe) && K.areDep(Xei,Xo));
	    m++;
	  }
	  if (fd) {
	    cout << "FLIP!" << endl;
	  }
	}
      }
      if (needpowerofp) {
	baseptindex = bpi;
	cout << "Some element in this orbit (index " << bpi << ") is on mirror of P^" << orderofppower << endl;
	cout << "   (it joins v#" << edges[edgePOrbits[vo[bpi]][0]].origin << " and v#" << edges[edgePOrbits[vo[bpi]][0]].end << ")" << endl;
      }
      else {
	cout << "No element in this orbit is on mirror of power of P!" << endl;
	Xo = vertices[edges[edgePOrbits[vo[baseptindex]][0]].origin].coords;
	Xe = vertices[edges[edgePOrbits[vo[baseptindex]][0]].end].coords;
      }
    }
    else {
      // not s5
      Xo = vertices[edges[edgePOrbits[vo[baseptindex]][0]].origin].coords;
      Xe = vertices[edges[edgePOrbits[vo[baseptindex]][0]].end].coords;
    }

    cout << "Computing stabilizer of edge P-Orbit #" << vo[baseptindex] << endl;
    vector<vector<int> > stab;
    if (vo.size()>1) {
      vector<vector<int> > graph_edges;
      vector<vector<int> > maps;
      for (int uu=0; uu<edgePOrbitPairings.size(); uu++) {
	if (find(vo.begin(),vo.end(),edgePOrbitPairings[uu][0])!=vo.end()) {
	  graph_edges.push_back(edgePOrbitPairings[uu]);
	  maps.push_back(edgePOrbitPairingMaps[uu]);
	}
      }
      sgraph G(graph_edges);      
      vector<vector<int> > stabgens;
      vector<vector<bool> > orientations;
      G.fundamental_group(vo[baseptindex],stabgens,orientations);

      for (int kk=0; kk<stabgens.size(); kk++) {
	vector<int> yo;
	for (int ll=0; ll<stabgens[kk].size(); ll++) {
	  if (orientations[kk][ll]) {
	    yo = concat(maps[stabgens[kk][ll]],yo);
	  }
	  else {
	    yo = concat(invertword(maps[stabgens[kk][ll]]),yo);
	  }
	}
	if (yo.size()>0) {
	  cout << convertword(yo) << endl;
	}
	else {
	  cout << "Trivial elt!" << endl;
	}
	stab.push_back(yo);
      }
    }
    else {
      // there is a single vertex, won't use graph routine for this (just take all pairings as generators)
      for (int uu=0; uu<edgePOrbitPairings.size(); uu++) {
	if (find(vo.begin(),vo.end(),edgePOrbitPairings[uu][0])!=vo.end()) {
	  stab.push_back(edgePOrbitPairingMaps[uu]);
	}
      }
    }

    
    // Perhaps the edge is on the mirror of P^5 (s5 cases)
    if (needpowerofp) {
      cout << "THIS EDGE IS ON THE MIRROR OF P^" << orderofppower << endl;      
      vector<int> we;
      for (int oo=0; oo<orderofppower; oo++) {
	we.push_back(4);
      }
      stab.push_back(we);
    }
    cout << "Done computing stab" << endl;

    vector<gen> sgmatrices;
    cout << "Generators of stab: (there are " << stab.size() << ")" << endl;
    int max_ord=1;
    int maxordind;
    bool flip = false;
    for (int y=0; y<stab.size(); y++) {
      if (stab[y].size()>0) {
	cout << "  " << convertword(stab[y]) << endl;
	gen M = evalWord(stab[y]);
	int orm = K.order(M);
	cout << "   (order " << orm << ")";
	if (orm>1) {
	  gen dt = K.discrAdjusted(M);
	  if (operator_equal(dt,K.zeronumber,&ct)){
	    gen mirr;
	    if (!findMirror(M,mirr)) {
	      cout << "Trouble finding mirror" << endl;
	      exit(1);
	    }
	    gen te = SqNorm(mirr);
	    int sg;
	    if (K.checkSign(te,sg)) {
	      if (sg<0) {
		cout << "    (\"reflection\" in point)" << endl;
	      }
	      else {
		if (sg>0) {
		  cout << "    (reflection in line)" << endl;		
		}
		else {
		  cout << "Single e-val gives ideal point, should this happen??" << endl;
		  //		  exit(1);
		}
	      }
	    }
	    else {
	      cout << "Trouble locating mirror (inside or outside the ball)" << endl;
	      exit(1);	    
	    }
	  }
	  else {
	    // distinct eigenvalues, may want to compute rotation angles
	    cout << "    (distinct eigenvalues)" << endl;
	    cout << endl;
	  }
	}
	else {
	  cout << endl;
	}
	if (!K.isScalar(M)) {
	  sgmatrices.push_back(M);
	  if (orm>max_ord) {
	    max_ord = orm;
	    maxordind = sgmatrices.size()-1;
	  }
	  gen W1 = M*Xo;
	  gen W2 = M*Xe;
	  K.vectred(W1);
	  K.vectred(W2);
	  
	  if (K.areDep(W1,Xo) && K.areDep(W2,Xe)) {
	    cout << "Check (edge is fixed pointwise)" << endl;
	  }
	  else {
	    if (K.areDep(W1,Xe) && K.areDep(W2,Xo)) {
	      cout << "Check (edge is flipped)" << endl;
	      flip = true;
	    }
	    else {
	      cerr << "Trouble, edge is not stabilized!" << endl;
	      exit(1);
	    }
	  }
	}
      }
    }

    cout << endl;
    cout << "There is a cyclic subgroup of order " << max_ord << endl;
    vecteur popo;
    gen A = K.Id;
    for (int jjj=0; jjj<max_ord; jjj++) {
      popo.push_back(A);
      A = A*sgmatrices[maxordind];
      K.matred(A);
    }
    cout << "Constructed " << max_ord << " powers " << endl;
    
    bool is_cyclic = true;
    int jjj=0;
    while (is_cyclic && jjj<sgmatrices.size()) {
      //    for (int j=0; j++; j<sgmatrices.size()) {
      if (jjj!=maxordind) {
	int kkk=0;
	bool fd = false;
	while (kkk<max_ord && !fd) {
	  fd = K.isMultiple(sgmatrices[jjj],popo[kkk]);
	  kkk++;
	}
	is_cyclic = fd;
      }
      jjj++;
    }

    if (is_cyclic) {

      cout << "Cyclic group gives full stabilizer! " << endl;
      cout << endl;
      
      vector<int> reflinds;
      for (int uu=1; uu<popo.size(); uu++) {
	gen M = popo[uu];
	int ouu = K.order(M);
	if (ouu>1) {
	  gen dt = K.discrAdjusted(M);
	  if (operator_equal(dt,K.zeronumber,&ct)){
	    gen mirr;
	    if (!findMirror(M,mirr)) {
	      cout << "Trouble finding mirror" << endl;
	      exit(1);
	    }
	    gen te = SqNorm(mirr);
	    int sg;
	    if (K.checkSign(te,sg)) {
	      if (sg>0) {
		reflinds.push_back(uu);
	      }
	    }
	    else {
	      cout << "Trouble locating mirror (inside or outside the ball)" << endl;
	      exit(1);	    
	    }
	  }
	}
      }
      cout << "There are " << reflinds.size() << " reflections in this group" << endl;
      if (reflinds.size()==max_ord-1) {
	cout << "Stabilizer is a reflection group" << endl;
      }
      else {
	cout << "Stab is NOT a reflection group, singular point in quotient" << endl;
	cout << "Reflection subgroup has order " << (reflinds.size()+1) << ", index " << (max_ord/reflinds.size()+1) << endl;
      }
      
      res = res + K.onenumber/max_ord;
      if (!flip) {
	res_top = res_top + K.onenumber;
      }
      else {
	// do nothing, need to subdivide, and then contribution is 0?
      }

    }
    else {

      cout << "Group is not cyclic, will use brute force" << endl;
      cout << endl;
      
      FiniteGroup L(K);
      L.addElements(sgmatrices);
      L.expandToGroup();
      
      cout << "Stab has order " << L.elements.size() << endl;
      
      vector<int> reflinds;
      for (int uu=1; uu<L.elements.size(); uu++) {
	gen M = L.elements[uu];
	int ouu = K.order(M);
	if (ouu>1) {
	  gen dt = K.discrAdjusted(M);
	  if (operator_equal(dt,K.zeronumber,&ct)){
	    gen mirr;
	    if (!findMirror(M,mirr)) {
	      cout << "Trouble finding mirror" << endl;
	      exit(1);
	    }
	    gen te = SqNorm(mirr);
	    int sg;
	    if (K.checkSign(te,sg)) {
	      if (sg>0) {
		reflinds.push_back(uu);
	      }
	    }
	    else {
	      cout << "Trouble locating mirror (inside or outside the ball)" << endl;
	      exit(1);	    
	    }
	  }
	}
      }
      cout << "There are " << reflinds.size() << " reflections in this group" << endl;
    
      FiniteGroup LR(K);
      for (int uu=0; uu<reflinds.size(); uu++) {
	LR.addElement(L.elements[reflinds[uu]]);
      }
      LR.expandToGroup();
      cout << "Subgroup generated by reflections has order " << LR.elements.size() << endl;
      
      if (LR.elements.size()<L.elements.size()) {
	cout << "The stabilizer is NOT generated by reflections, this will give a singular point of the quotient" << endl;
      }
      else {
	cout << "The stabilizer is generated by reflections!" << endl;
      }
      
      cout << endl;
      
      res = res + K.onenumber/L.elements.size();
      if (!flip) {
	res_top = res_top + K.onenumber;
      }
      else {
	// do nothing, need to subdivide, and then contribution is 0?
      }
      
    }
    
  }
  
  vecteur te;
  te.push_back(res);
  te.push_back(res_top);
  
  return te;

}

gen Polytope::computeAngle(int j, gen X) {
  gen mi;
  if (isRidgeComplex(j,mi)) {
    gen te = SqNorm(X);
    if (verbose) {
      cout << "<X,X> = " << te << endl;
    }
    int sg;
    if (K.checkSign(te,sg)) {
      if (sg<0) {
	gen Y = BoxProd(X,mi);
	gen Z = BoxProd(X,Y);
	// X,Z spans the complex line orthog to common slice
	//   (beware it's orthogonal but not orthonormal)

	//	cout << "Will compute equations of faces in complex line" << endl;
	Prism P1 = faces[ridges[j].faceindices[0]];
	Prism P2 = faces[ridges[j].faceindices[1]];

	gen a1 = Inn(X,P1.X0)*Inn(P1.X0,X) - Inn(X,P1.X1)*Inn(P1.X1,X);
	gen a2 = Inn(X,P2.X0)*Inn(P2.X0,X) - Inn(X,P2.X1)*Inn(P2.X1,X);
	K.red(a1);
	K.red(a2);
       
	if (!operator_equal(a1,K.zeronumber,&ct) || !operator_equal(a2,K.zeronumber,&ct)) {
	  cerr << "Trouble, X is not on bisectors?" << endl;
	  exit(1);
	}	
	gen b1 = Inn(Z,P1.X0)*Inn(P1.X0,X) - Inn(Z,P1.X1)*Inn(P1.X1,X);
	gen b2 = Inn(Z,P2.X0)*Inn(P2.X0,X) - Inn(Z,P2.X1)*Inn(P2.X1,X);
	
	gen mu1 = K.evali(b1);
	gen mu2 = K.evali(b2);
	gen te = -re(mu1*conj(mu2,&ct)/(abs(mu1,&ct)*abs(mu2,&ct)),&ct);

	return acos(te,&ct)/(2*gen("pi",&ct));
      }
      else {
	cerr << "Can't compute the intersection angle at a point outside complex hyperbolic space" << endl;
	exit(1);
      }
    }
    else {
      cerr << "Trouble checking sign of X" << endl;
      exit(1);
    }
  }
  else {
    cerr << "Angle computation is implemented only for cotranchal intersections!" << endl;
    return K.zeronumber;
  }
}

gen Polytope::computeAngleNum(int j, gen X) {
  // same as previous method, but X has interval coordinates

  gen mi;
  if (isRidgeComplex(j,mi)) {
    gen Hn = K.matevali(H);

    gen te1 = conj(X,&ct)*Hn;
    gen te2 = conj(K.vectevali(mi),&ct)*Hn;
    vecteur Yv;
    Yv.push_back(te1[1]*te2[2]-te1[2]*te2[1]);
    Yv.push_back(te1[2]*te2[0]-te1[0]*te2[2]);
    Yv.push_back(te1[0]*te2[1]-te1[1]*te2[0]);
    gen Y = gen(Yv);

    te2 = conj(Y,&ct)*Hn;
    vecteur Zv;
    Zv.push_back(te1[1]*te2[2]-te1[2]*te2[1]);
    Zv.push_back(te1[2]*te2[0]-te1[0]*te2[2]);
    Zv.push_back(te1[0]*te2[1]-te1[1]*te2[0]);
    gen Z = gen(Zv);

    Prism P1 = faces[ridges[j].faceindices[0]];
    Prism P2 = faces[ridges[j].faceindices[1]];

    gen p1x0 = K.vectevali(P1.X0);
    gen p1x1 = K.vectevali(P1.X1);
    gen p2x0 = K.vectevali(P2.X0);
    gen p2x1 = K.vectevali(P2.X1);

    gen mu1 = (conj(p1x0,&ct)*Hn*Z)*(conj(X,&ct)*Hn*p1x0) - (conj(p1x1,&ct)*Hn*Z)*(conj(X,&ct)*Hn*p1x1);
    gen mu2 = (conj(p2x0,&ct)*Hn*Z)*(conj(X,&ct)*Hn*p2x0) - (conj(p2x1,&ct)*Hn*Z)*(conj(X,&ct)*Hn*p2x1);

    gen te = -re(mu1*conj(mu2,&ct)/(abs(mu1,&ct)*abs(mu2,&ct)),&ct);

    return acos(te,&ct)/(2*gen("pi",&ct));
  }
}

bool Polytope::isRidgeComplex(int j, gen &mi) {
  ridge r = ridges[j];
  int jface = r.faceindices[0];

  bool iscomplex = false;
  bool isbottom = true;
  int uu=0;
  while (isbottom && uu<r.verts.size()) {
    gen X = vertices[r.verts[uu]].coords;
    isbottom = operator_equal(Inn(X,faces[jface].mirror.coords),K.zeronumber,&ct);
    uu++;
  }
  if (isbottom) {
    if (verbose) {
      cout << "Ridge is bottom ridge of face " << jface << endl;
    }
    mi = faces[jface].mirror.coords;
    return true;
  }
  else {
    bool istop = true;
    uu=0;
    while (istop && uu<r.verts.size()) {
      gen X = vertices[r.verts[uu]].coords;
      istop = operator_equal(Inn(X,faces[jface].apex.coords),K.zeronumber,&ct);
      uu++;
    }
    if (istop) {
      if (verbose) {
	cout << "Ridge is top ridge of face " << jface << endl;
      }
      mi = faces[jface].apex.coords;
      return true;
    }
  }
  return false;
}

vecteur Polytope::eulerdim2() {

  gen res_top = K.zeronumber;

  gen res = K.zeronumber;

  vector<int> ridgeorbitstocheck;
  for (int k=0; k<ridgePOrbits.size(); k++) {
    ridgeorbitstocheck.push_back(k);
  }

  while (ridgeorbitstocheck.size()>0) {
    cout << "Ridge orbits to check: " << convertword(ridgeorbitstocheck) << endl;
    cout << "Studying orbit " << ridgePOrbits[ridgeorbitstocheck[0]] << endl;

    int init_orbit_index = ridgeorbitstocheck[0]; 
    cout << "Computing cycle for ridge on faces " << convertword(ridges[ridgePOrbits[init_orbit_index][0]].faceindices) << endl;

    gen mi;
    bool iscomplex = isRidgeComplex(ridgePOrbits[init_orbit_index][0],mi);

    int jstart = ridges[ridgePOrbits[init_orbit_index][0]].faceindices[0];
    int kstart = ridges[ridgePOrbits[init_orbit_index][0]].faceindices[1];

    int current_ridge = ridgePOrbits[init_orbit_index][0];
    int j = jstart;

    bool back = false;

    int ridge_stab;
    bool found_stab = false;

    if (needppower) {

      while (!back && !found_stab) {

	int jimage = applyPToRidge(orderofppower,current_ridge);
	if (jimage==current_ridge) {
	  found_stab = true;
	  cout << "Ridge #" << current_ridge << " is invariant by P^" << orderofppower << endl;
	  if (!iscomplex) {
	    cout << " (this is not a complex ridge!)" << endl;
	  }
	}
	else {
	  cout << "Ridge #" << j << " is NOT invariant by P^" << orderofppower << " (image is #" << jimage << ")" << endl;
	}
	
	if (found_stab) {
	  cout << "Element in ridge orbit is stabilized by P^" << orderofppower << "." << endl;
	  ridge_stab = current_ridge;
	}
	current_ridge = applyPairingToRidge(j,current_ridge);
	// need to change mi
	
	if (iscomplex) {
	  isRidgeComplex(current_ridge,mi);
	}
	bool fd = false;
	int u = -1;
	int v;
	while (!fd && u!=ridgePOrbits.size()-1) {
	  u++;
	  v = -1;
	  while (!fd && v!=ridgePOrbits[u].size()-1) {
	    v++;
	    fd = current_ridge == ridgePOrbits[u][v]; 
	  }
	}
	if (!fd) {
	  cerr << "Trouble finding image ridge in a ridge P-orbit" << endl;
	  exit(1);
	}
	else {
	  if (ridges[current_ridge].faceindices[0]==facePairings[j]) {
	    j = ridges[current_ridge].faceindices[1];
	  }
	  else {
	    if (ridges[current_ridge].faceindices[1]==facePairings[j]) {
	      j = ridges[current_ridge].faceindices[0];
	    }
	    else {
	      cerr << "Trouble finding next pairing" << endl;
	      exit(1);
	    }
	  }
	  back = (u==init_orbit_index);
	}
      }

      if (found_stab) {
	cout << "Found ridge in orbit stabilized by P power!" << endl;
	int jj=-1;
	int kk;
	bool fdd = false;
	while (!fdd && jj!=ridgePOrbits.size()-1) {
	  jj++;
	  kk=-1;
	  while (!fdd && kk!=ridgePOrbits[jj].size()-1) {
	    kk++;;
	    fdd = (ridge_stab==ridgePOrbits[jj][kk]);
	  }
	}
	if (!fdd) {
	  cout << "Trouble locating ridge fixed by P power" << endl;
	  exit(1);
	}
	else {
	  init_orbit_index=jj;
	}
      }
    }    

    gen C;
    vector<int> cw;

    if (found_stab) {
      cout << "Will start with orbit of " << ridges[ridgePOrbits[init_orbit_index][0]] << ", because it is fixed py power of P" << endl;
    }

    jstart = ridges[ridgePOrbits[init_orbit_index][0]].faceindices[0];
    kstart = ridges[ridgePOrbits[init_orbit_index][0]].faceindices[1];

    if (faces[jstart].invertpairing) {
      cout << convertword(invertword(faces[jstart].mirror.word)) << "->" << endl; 
      cw = invertword(faces[jstart].mirror.word);
    }
    else  {
      cout << convertword(faces[jstart].mirror.word) << "->" << endl; 
      cw = faces[jstart].mirror.word;      
    }
    C = faces[jstart].pairing;

    current_ridge = ridgePOrbits[init_orbit_index][0];
    j = jstart;    

    back = false;
    while (!back) {
      current_ridge = applyPairingToRidge(j,current_ridge);
      bool fd = false;
      int u = -1;
      int v;
      while (!fd && u!=ridgePOrbits.size()-1) {
	u++;
	v = -1;
	while (!fd && v!=ridgePOrbits[u].size()-1) {
	  v++;
	  fd = current_ridge == ridgePOrbits[u][v]; 
	}
      }
      if (!fd) {
	cerr << "Trouble finding image in some ridge P-orbit" << endl;
	exit(1);
      }
      else {
	ridgeorbitstocheck.erase(remove(ridgeorbitstocheck.begin(),ridgeorbitstocheck.end(),u),ridgeorbitstocheck.end());
	if (ridges[current_ridge].faceindices[0]==facePairings[j]) {
	  j = ridges[current_ridge].faceindices[1];
	}
	else {
	  if (ridges[current_ridge].faceindices[1]==facePairings[j]) {
	    j = ridges[current_ridge].faceindices[0];
	  }
	  else {
	    cerr << "Trouble finding next pairing" << endl;
	    exit(1);
	  }
	}
	back = (u==init_orbit_index);
	if (!back) {
	  if (faces[j].invertpairing) {
	    cout << convertword(invertword(faces[j].mirror.word)) << "->" << endl;
	    cw = concat(invertword(faces[j].mirror.word),cw);
	  }
	  else  {
	    cout << convertword(faces[j].mirror.word) << "->" << endl;
	    cw = concat(faces[j].mirror.word,cw);      
	  }
	  C = faces[j].pairing*C;
	  K.matred(C);
	}
	else {
	  vector<int> power_of_p;
	  for (int k=0; k<v; k++) {
	    power_of_p.push_back(-4);
	    C = Pi*C;
	    K.matred(C);
	  }
	  cout << "P^(-" << v << ")" << endl;
	  cw = concat(power_of_p,cw);	  
	}
      }
    }
    if (!back) {
      cerr << "Trouble with some ridge cycle" << endl;
      exit(1);
    }
    else {

      if (found_stab) {
	gen mirc;
	vector<gen> sgmatrices;
	bool is_gen_refl = false;
	sgmatrices.push_back(C);
	cout << "Elements in stab: " << endl;
	cout << convertword(cw);
	int orc = K.order(C);
	cout << "   (order " << orc << ")";
	if (orc>1) {
	  gen dt = K.discrAdjusted(C);
	  if (operator_equal(dt,K.zeronumber,&ct)){
	    if (!findMirror(C,mirc)) {
	      cout << "Trouble finding mirror" << endl;
	      exit(1);
	    }
	    gen te = SqNorm(mirc);
	    int sg;
	    if (K.checkSign(te,sg)) {
	      if (sg<0) {
		cout << "    (\"reflection\" in point)" << endl;
	      }
	      else {
		if (sg>0) {
		  is_gen_refl = true;
		  cout << "    (reflection in line)" << endl;		
		}
		else {
		  cout << "Single e-val gives ideal point, should this happen??" << endl;
		  //		  exit(1);
		}
	      }
	    }
	    else {
	      cout << "Trouble locating mirror (inside or outside the ball)" << endl;
	      exit(1);	    
	    }
	  }
	  else {
	    cout << endl;
	  }
	}
	cout << "P^" << orderofppower <<  " (this is a complex reflection)" << endl;
	sgmatrices.push_back(Ppower);

	if (iscomplex) {
	  isRidgeComplex(ridgePOrbits[init_orbit_index][0],mi);
	  for (int yy=0; yy<sgmatrices.size(); yy++) {
	    gen Y = sgmatrices[yy]*mi;
	    K.vectred(Y);
	    if (!K.areDep(mi,Y)) {
	      cout << "PROBLEM, MATRIX IN SGMATRICES DOES NOT PRESERVE RIDGE!" << endl;
	    }
	  }
	}
	else {

	  // need to implement a check in generic case...
	  //  (the part that is not implemented is just a sanity check)

	}

	// Should try studying reflection subgroups generated by two elements!
	bool found_shortcut = false;	      
	int shortcut_order;
	if (is_gen_refl) {
	  int bo = K.braidOrder(C,Ppower);
	  if (bo>2) {
	      cout << "Generators braid with length " << bo << endl;
	      cout << "  (it is not too bad to figure out what they generate, not implemented yet though)" << endl;
	  }
	  else {
	    if (bo==2) {
	      gen mirppow;
	      if (!findMirror(Ppower,mirppow)) {
		cout << "Trouble finding mirror" << endl;
		exit(1);
	      }
	      if (K.areDep(mirc,mirppow)) {
		cout << "Generators are reflections with the same mirror, cyclic group" << endl;
		cout << "  (it is not so bad to compute the order of the group they generated, but not implemented)" << endl;
		int opp = K.order(Ppower);
		vecteur clist;
		gen A = K.Id;
		for (int j=0; j<orc; j++) {
		  clist.push_back(A);
		  A = A*C;
		  K.matred(A);
		}
		gen B = K.Id;
		int count_intersection = 0;
		for (int j=0; j<opp; j++) {
		  bool is_in_clist = false;
		  int k = 0;
		  while (!is_in_clist && k<clist.size()) {
		    is_in_clist = K.isMultiple(B,clist[k]);
		    k++;
		  }
		  if (is_in_clist) {
		    count_intersection++;
		  }
		  B = B*Ppower;
		  K.matred(B);
		}
		shortcut_order = (opp*orc)/count_intersection;
		cout << "Order is simply the product of the orders (" << (orc*opp) << ")," << endl;
		cout << "         divided by order of intersection (" << count_intersection << ")" << endl;
	        cout << "                                              =" << shortcut_order << ")" << endl;
		found_shortcut = true;						       
	      }
	      else {
		cout << "Generators are reflections with orthogonal mirrors" << endl;
		int opp = K.order(Ppower);
		shortcut_order = orc*opp;
		cout << "Order is simply the product of the orders (" << shortcut_order << ")" << endl;
		found_shortcut = true;						       
	      }
	    }
	  }
	}

	if (found_shortcut) {

	  res = res + K.onenumber/shortcut_order;
	  res_top = res_top + K.onenumber;

	}
	else {
	      
	  FiniteGroup L(K);
	  L.addElements(sgmatrices);
	  L.expandToGroup();
	  cout << "Stab has order " << L.elements.size() << endl;
	  
	  vector<int> reflinds;
	  for (int uu=1; uu<L.elements.size(); uu++) {
	    gen M = L.elements[uu];
	    int ouu = K.order(M);
	    
	    if (ouu>1) {
	      gen dt = K.discrAdjusted(M);
	      
	      if (operator_equal(dt,K.zeronumber,&ct)){

		gen mirr;
		if (!findMirror(M,mirr)) {
		  cout << "Trouble finding mirror" << endl;
		  exit(1);
		}
		gen te = SqNorm(mirr);
		int sg;
		if (K.checkSign(te,sg)) {
		  if (sg>0) {
		    reflinds.push_back(uu);
		  }
		}
		else {
		  cout << "Trouble locating mirror (inside or outside the ball)" << endl;
		  exit(1);	    
		}
	      }
	    }
	  }
	  cout << "There are " << reflinds.size() << " reflections in this group" << endl;

	  FiniteGroup LR(K);
	  for (int uu=0; uu<reflinds.size(); uu++) {
	    LR.addElement(L.elements[reflinds[uu]]);
	  }
	  LR.expandToGroup();
	  cout << "Subgroup generated by reflections has order " << LR.elements.size() << endl;
	  
	  if (LR.elements.size()<L.elements.size()) {
	    cout << "The stabilizer is NOT generated by reflections, this will give a singular point of the quotient" << endl;
	  }
	  else {
	    cout << "The stabilizer is generated by reflections!" << endl;
	  }
	  cout << endl;
	  

	  res = res + K.onenumber/L.elements.size();
	  res_top = res_top + K.onenumber;

	
	}
      }
      else {
	int oc = K.order(C);
	if (oc<0) {
	  cerr << "Ridge with large order of stab (>1000), I will consider it as infinite, and STOP" << endl;
	  exit(1);
	}
	else {
	  cout << "Found a ridge with stabilizer of order " << oc << endl;
	  if (oc>1) {
	    gen dt = K.discrAdjusted(C);
	    if (operator_equal(dt,K.zeronumber,&ct)){
	      gen mirr;
	      if (!findMirror(C,mirr)) {
		cout << "Trouble finding mirror" << endl;
		exit(1);
	      }
	      gen te = SqNorm(mirr);
	      int sg;
	      if (K.checkSign(te,sg)) {
		if (sg<0) {
		  cout << "Cyclic group generated by a complex reflection in a point" << endl;
		  cout << "  this gives a singular point in the quotient" << endl;
		}
		else {
		  if (sg>0) {
		    cout << "    (reflection in line)" << endl;		
		  }
		  else {
		    cout << "Single e-val gives ideal point, this should not happen" << endl;
		    exit(1);
		  }
		}
	      }
	      else {
		cout << "Trouble locating mirror (inside or outside the ball)" << endl;
		exit(1);	    
	      }
	    }
	    else {
	      cout << "Cyclic group generated by regular elliptic" << endl;
	      cout << "  this gives a singular point in the quotient" << endl;
	      cout << endl;
	    }
	  }
	  else {
	    cout << endl;
	  }
	  res = res + K.onenumber/oc;
	  res_top = res_top + K.onenumber;
	}
      }
    }    
  }
  vecteur te;
  te.push_back(res);
  te.push_back(res_top);

  return te;
}

vecteur Polytope::eulerdim3() {

  if (needppower) {

    gen res = K.zeronumber;
    gen res_top = K.zeronumber;
    for (int k=0; k<facePOrbits.size(); k++) {
      gen te1 = Inn(faces[facePOrbits[k][0]].apex.coords,extramirror);
      if (operator_equal(te1,K.zeronumber,&ct)) {
	gen te2 = Inn(faces[facePOrbits[k][0]].apexProjection,extramirror);
	if (operator_equal(te2,K.zeronumber,&ct)) {
	  int te = orderofp/orderofppower;
	  res = res+K.onenumber/te;
	  res_top = res_top+K.onenumber;
	}
	else {
	  res = res + K.onenumber;
	  res_top = res_top + K.onenumber;
	}
      }
      else {
	res = res + K.onenumber;
	res_top = res_top + K.onenumber;
      }
    }
    vecteur te;
    te.push_back(res/K.twonumber);
    te.push_back(res_top/K.twonumber);    
    return te;
  }
  else {
    vecteur te;
    te.push_back(facePOrbits.size()/2);
    te.push_back(facePOrbits.size()/2);
    return te;
  }
}

void Polytope::computeEulerCharacteristic() {

  // VERTEX ORBITS
  cout << "Will now compute e0" << endl;
  vecteur yo0 = eulerdim0();
  gen ed0 = yo0[0];
  gen ed0_top = yo0[1];
  
  // EDGE ORBITS
  cout << "Will now compute e1" << endl;
  vecteur yo1 = eulerdim1();
  gen ed1 = yo1[0];
  gen ed1_top = yo1[1];

  // RIDGE ORBITS
  cout << "Will now compute e2" << endl;
  vecteur yo2 = eulerdim2();
  gen ed2 = yo2[0];
  gen ed2_top = yo2[1];

  // FACE ORBITS
  cout << "Will now compute e3" << endl;
  vecteur yo3 = eulerdim3();
  gen ed3 = yo3[0];
  gen ed3_top = yo3[1];
  // Can there ever be faces that are paired with something in the same P-orbit?
  //   (if so this may not be the right formula)
  
  cout << "Will now compute e4" << endl;
  // 4-dim contribution is just (order of P)^-1:
  gen ed4 = K.onenumber/orderofp;
  gen ed4_top = K.onenumber;

  cout << "Euler 0-dim contribution: " << ed0_top << endl;
  cout << "Euler 1-dim contribution: " << ed1_top << endl;
  cout << "Euler 2-dim contribution: " << ed2_top << endl;
  cout << "Euler 3-dim contribution: " << ed3_top << endl;
  cout << "Euler 4-dim contribution: " << ed4_top << endl;

  cout << endl;
  euler_char_top = ed0_top-ed1_top+ed2_top-ed3_top+ed4_top;
  cout << "Topological Euler characteristic: " << euler_char_top << endl;
  cout << endl;

  cout << "Orbifold Euler 0-dim contribution: " << ed0 << endl;
  cout << "Orbifold Euler 1-dim contribution: " << ed1 << endl;
  cout << "Orbifold Euler 2-dim contribution: " << ed2 << endl;
  cout << "Orbifold Euler 3-dim contribution: " << ed3 << endl;
  cout << "Orbifold Euler 4-dim contribution: " << ed4 << endl;

  cout << endl;
  euler_char = ed0-ed1+ed2-ed3+ed4;
  cout << "Orbifold Euler characteristic: " << euler_char << endl;
  cout << endl;

}

void Polytope::checkPairings() {
  for (int k=0; k<faces.size(); k++) {
    
    cout << "#" << k << ": ";
    cout << faces[k] << endl;
    
    Prism B = applyReflection(faces[k]);
    bool found = false;
    int l=0;
    while (!found && l<faces.size()) {
      found = comparePrisms(B,faces[l]);
      l++;
    }
    if (found) {
      cout << "  paired with " << faces[l-1] << endl;
      faces[k].pairing = reflMat(faces[k].mirror.coords,mult);
      faces[k].invertpairing = false;
      facePairings.push_back(l-1);
    }
    else {
      Prism Bi = applyInverseReflection(faces[k]);
      found = false;
      l=0;
      while (!found && l<faces.size()) {
	found = comparePrisms(Bi,faces[l]);
	l++;
      }
      if (found) {
	cout << "  [inverse] paired with " << faces[l-1] << endl;
	faces[k].invertpairing = true;
	faces[k].pairing = reflMat(faces[k].mirror.coords,multconj);
	facePairings.push_back(l-1);
      }
      else {
	cerr << "Problem with pairings!" << endl;
	exit(1);
      }
    }
  }
  cout << "List of pairings: " << endl;
  for (int k=0; k<faces.size() ; k++) {
    cout << "Face #" << k << " --> " << facePairings[k] << endl;
  }
}

void Polytope::computeAllVertices() {
  vector<WVector> rawverts;
  for (int k=0; k<faces.size(); k++) {
    int nb = faces[k].bottomface.size();
    for (int l=0; l<nb; l++) {
      if (!isInList(faces[k].bottomface[l],rawverts)) {
	rawverts.push_back(faces[k].bottomface[l]);
      }
    }
    nb = faces[k].topface.size();
    for (int l=0; l<nb; l++) {
      if (!isInList(faces[k].topface[l],rawverts)) {
	rawverts.push_back(faces[k].topface[l]);
      }
    }
    nb = faces[k].midvertices.size();
    for (int l=0; l<nb; l++) {
      if (!isInList(faces[k].midvertices[l],rawverts)) {
	rawverts.push_back(faces[k].midvertices[l]);
      }
    }
  }
  cout << "Collected " << rawverts.size() << " vertices..." << endl;
  
  while (rawverts.size()>0) {
    WVector v0 = rawverts[0];
    WVector vk = rawverts[0];
    vector<int> orb;
    orb.push_back(vertices.size());
    vertices.push_back(v0);
    vertReps.push_back(v0);
    rawverts.erase(rawverts.begin());
    bool back = false;
    while (!back) {
      vk.coords = P*vk.coords;
      if (is_symmetric) {
	vk.word = conjugateword(word_p1,shiftIndicesUp(vk.word));
      }
      else {
	vk.word = conjugateword(word_123,vk.word);
      }
      K.vectred(vk.coords);
      back = testSame(vk,v0);
      if (!back) {
	orb.push_back(vertices.size());
	vertices.push_back(vk);
	int ind = findInList(vk,rawverts);
	if (ind>-1) {
	  rawverts.erase(rawverts.begin()+ind);
	}
	else {
	  cerr << "Trouble computing orbit!" << endl;
	  exit(1);
	}
      }
      else {
	vertexPOrbits.push_back(orb);
      }
    }
  }
  cout << "The vertices come in " << vertReps.size() << " P-orbits." << endl;
  
  for (int k=0; k<vertexPOrbits.size(); k++) {
    cout << "Orbit #" << k << ": " << vertexPOrbits[k] << endl;
    int no = vertexPOrbits[k].size();
    int k0 = vertexPOrbits[k][0];
    vector<int> fi = findFaces(vertices[k0]);
    cout << "Vert #" << k0 << " is on " << convertword(fi) << endl;
    if (fi[0]==-1) {
      fi.erase(fi.begin());
      for (int m=0; m<no; m++) {
	vertices[k0+m].faceindices.push_back(-1);
      }
    }
    for (int l=0; l<fi.size(); l++) {
      int fil = fi[l];
      // identify it in some POrbit of faces!
      bool fd = false;
      int u=-1;
      int v;
      while (!fd && u!=facePOrbits.size()-1) {
	u++;
	v=-1;
	while (!fd && v!=facePOrbits[u].size()-1) {
	  v++;
	  fd = (fil==facePOrbits[u][v]);
	}
      }
      if (!fd) {
	cerr << "Problem, some face was not found in P-orbits of faces!" << endl;
	exit(1);
      }
      else {
	for (int m=0; m<no; m++) {
	  vertices[vertexPOrbits[k][m]].faceindices.push_back(facePOrbits[u][(v+m)%facePOrbits[u].size()]);
	}
      }
    }
    
    vector<WVector> mi = findMirrors(vertices[k0]);
    cout << "  and on following mirrors:  " << endl;
    for (int u = 0; u<mi.size(); u++) {
      cout << mi[u].word << endl;
    }

  }

  for (int k=0; k<vertReps.size(); k++) {
    // this is misnamed, for now it only finds pairings of vertices
    computeStabilizer(k);
  }

  for (int k=0; k<vertices.size(); k++) {
    cout << "Vertex #" << k << " is on " << convertword(vertices[k].faceindices) << endl;
  }
}

void Polytope::computeAllEdges() {
  cout << "Will now construct edges..." << endl;
  for (int kk=0; kk<facePOrbits.size(); kk++) {
    int k = facePOrbits[kk][0];
    cout << "Face #" << k << endl;
    vector<int> tf;
    for (int l=0; l<faces[k].topface.size(); l++) {
      int te = findInList(faces[k].topface[l],vertices);
      if (te<0) {
	cerr << "Problem identifying face vertex" << endl;
	exit(1);
      }
      else {
	tf.push_back(te);
	faces[k].topface[l].reftovlist = te;
      }
    }
    if (tf.size()>1) {
      for (int l=0; l<tf.size()-1; l++) {
	edge e;
	e.origin = tf[l];
	e.end = tf[l+1];	    
	addEdge(e);
      }
      edge e;
      e.origin = tf[tf.size()-1];
      e.end = tf[0];
      addEdge(e);
    }
    
    vector<int> mf;
    for (int l=0; l<faces[k].midvertices.size(); l++) {
      int te = findInList(faces[k].midvertices[l],vertices);
      if (te<0) {
	cerr << "Problem identifying face vertex" << endl;
	exit(1);
      }
      else {
	mf.push_back(te);
	faces[k].midvertices[l].reftovlist = te;
      }
    }
    
    vector<int> bf;
    for (int l=0; l<faces[k].bottomface.size(); l++) {
      int te = findInList(faces[k].bottomface[l],vertices);
      if (te<0) {
	cerr << "Problem identifying face vertex" << endl;
	exit(1);
      }
      else {
	bf.push_back(te);
	faces[k].bottomface[l].reftovlist = te;
      }
    }
    
    for (int l=0; l<bf.size()-1; l++) {
      edge e;
      e.origin = bf[l];
      e.end = bf[l+1];
      addEdge(e);
    }
    edge eb;
    eb.origin = bf[bf.size()-1];
    eb.end = bf[0];
    addEdge(eb);
    
    if (tf.size()>1) {
      for (int l=0; l<bf.size(); l++) {
	if (find(faces[k].midvertindices.begin(),faces[k].midvertindices.end(),l)==faces[k].midvertindices.end()) {
	  edge e;
	  e.origin = bf[l];
	  e.end = tf[l];
	  addEdge(e);
	}
      }
    }
    else {
      for (int l=0; l<bf.size(); l++) {
	if (find(faces[k].midvertindices.begin(),faces[k].midvertindices.end(),l)==faces[k].midvertindices.end()) {
	  edge e;
	  e.origin = bf[l];
	  e.end = tf[0];
	  addEdge(e);
	}
      }
    }
    
    for (int l=0; l<mf.size(); l++) {
      if (tf.size()>1) {
	edge e1, e2;
	e1.origin = bf[faces[k].midvertindices[l]];
	e1.end = mf[l];
	addEdge(e1);
	e2.origin = mf[l];
	e2.end = tf[faces[k].midvertindices[l]];
	addEdge(e2);
      }
      else {
	edge e1, e2;
	e1.origin = bf[faces[k].midvertindices[l]];
	e1.end = mf[l];
	addEdge(e1);
	e2.origin = mf[l];
	e2.end = tf[0];
	addEdge(e2);
      }
    }
  }
  cout << "Created " << edges.size() << " edges..." << endl;
}

void Polytope::computeAllRidges() {
  cout << "Will now construct ridges..." << endl;
  for (int kk=0; kk<facePOrbits.size(); kk++) {
    
    int k = facePOrbits[kk][0];
    cout << "Face #" << k << endl;
    int ns = faces[k].sides.size();
    for (int l=0; l<ns; l++) {
      ridge r;
      if (faces[k].topface.size()==1) {
	r.verts.push_back(faces[k].topface[0].reftovlist);
      }
      else {
	r.verts.push_back(faces[k].topface[(l+1)%ns].reftovlist);
	r.verts.push_back(faces[k].topface[l].reftovlist);
      }
      bool fd = false;
      int u1=0; 
      while (!fd && u1<faces[k].midvertindices.size()){
	fd = l==faces[k].midvertindices[u1];
	u1++;
      }
      if (fd){
	u1--;
	r.verts.push_back(faces[k].midvertices[u1].reftovlist);
      }	  
      r.verts.push_back(faces[k].bottomface[l].reftovlist);
      r.verts.push_back(faces[k].bottomface[(l+1)%ns].reftovlist);
      fd = false;
      int u2=0; 
      while (!fd && u2<faces[k].midvertindices.size()){
	fd = (l+1)%ns==faces[k].midvertindices[u2];
	u2++;
      }
      if (fd){
	u2--;
	r.verts.push_back(faces[k].midvertices[u2].reftovlist);
      }
      addRidge(r);
    }
    
    ridge r;
    for (int l=0; l<ns; l++) {
      r.verts.push_back(faces[k].bottomface[l].reftovlist);
    }
    addRidge(r);
    
    if (faces[k].topface.size()>1) {
      ridge r;
      for (int l=0; l<ns; l++) {
	r.verts.push_back(faces[k].topface[l].reftovlist);
      }
      addRidge(r);
    }	
    
  }
  cout << "Created " << ridges.size() << " ridges..." << endl;
}

bool Polytope::isSame(edge e1, edge e2) {
  return (e1.origin==e2.origin && e1.end == e2.end); 
}

bool Polytope::isFlipped(edge e1, edge e2) {
  return (e1.origin==e2.end && e1.end == e2.origin); 
}

bool Polytope::isEdgeNew(edge e) {
  bool res = true;
  int l=0;
  while (res && l<edges.size()){
    res = !isSame(e,edges[l]) && !isFlipped(e,edges[l]);
    l++;
  }
  return res;
}

int Polytope::applyPtoVertex(int k) {
  bool found = false;
  int l=-1;
  int m;
  while (!found && l!=vertexPOrbits.size()-1) {
    l++;
    m=-1;
    while (!found && m!=vertexPOrbits[l].size()-1) {
      m++;
      found = vertexPOrbits[l][m]==k; 
    }
  }
  if (!found) {
    cerr << "Not supposed to happen (trouble locating vertex index in P orbits)" << endl;
    exit(1);
  }
  else {
    return vertexPOrbits[l][(m+1)%vertexPOrbits[l].size()];
  }
}

vector<int> Polytope::getPOrbitOfVertex(int k) {
  bool found = false;
  int l=-1;
  int m;
  while (!found && l!=vertexPOrbits.size()-1) {
    l++;
    m=-1;
    while (!found && m!=vertexPOrbits[l].size()-1) {
      m++;
      found = vertexPOrbits[l][m]==k; 
    }
  }
  if (!found) {
    cerr << "Not supposed to happen (trouble locating vertex index in P orbits)" << endl;
    exit(1);
  }
  else {
    vector<int> res;
    int nmax = vertexPOrbits[l].size();
    for (int n=0; n<nmax; n++) {
      res.push_back(vertexPOrbits[l][(m+n)%nmax]);
    }
    return res;
  }
}

int Polytope::findNextInFacePOrbit(int k) {
  bool found = false;
  int l=-1;
  int m;
  while (!found && l!=facePOrbits.size()-1) {
    l++;
    m=-1;
    while (!found && m!=facePOrbits[l].size()-1) {
      m++;
      found = facePOrbits[l][m]==k; 
    }
  }
  if (!found) {
    cerr << "Not supposed to happen (trouble locating face index in P orbits)" << endl;
    exit(1);
  }
  else {
    return facePOrbits[l][(m+1)%facePOrbits[l].size()];
  }  
}

vector<int> Polytope::getPOrbitOfFace(int k) {
  bool found = false;
  int l=-1;
  int m;
  while (!found && l!=facePOrbits.size()-1) {
    l++;
    m=-1;
    while (!found && m!=facePOrbits[l].size()-1) {
      m++;
      found = facePOrbits[l][m]==k; 
    }
  }
  if (!found) {
    cerr << "Not supposed to happen (trouble locating face index in P orbits)" << endl;
    exit(1);
  }
  else {
    vector<int> res;
    int nmax = facePOrbits[l].size();
    for (int n=0; n<nmax; n++) {
      res.push_back(facePOrbits[l][(m+n)%nmax]);
    }
    return res;
  }
}

void Polytope::addRidge(ridge e) {

  vector<int> orbitinds;
  if (isRidgeNew(e)) {

    int nv = e.verts.size();

    vector<vector<int> > li;
    for (int u=0; u<nv; u++) {
      cout << "vertex #" << e.verts[u] << ": " << vertices[e.verts[u]].faceindices << endl;
      li.push_back(vertices[e.verts[u]].faceindices);
    }
    vector<int> inds = intersect(li);
    if (inds[0]==-1) {
      inds.erase(inds.begin());
    }
    if (inds.size()!=2) {
      cerr << "Problem with ridge, it is on " << inds.size() << " faces, namely " << convertword(inds) << endl;
      exit(1);
    }
    else {
      for (int u=0; u<2; u++) {
	e.faceindices.push_back(inds[u]);
      }
    }

    cout << "Adding ridge w/ vertices " << convertword(e.verts) << "     (which is on faces " << convertword(e.faceindices) << ")" << endl;
    orbitinds.push_back(ridges.size());
    ridges.push_back(e);

    vector<vector<int> > yo;
    int maxorb = 0;
    int maxorbind = 0;
    for (int k=0; k<nv; k++) {
      vector<int> yok = getPOrbitOfVertex(e.verts[k]);
      if (yok.size()>maxorb) {
	maxorb = yok.size();
	maxorbind = k;
      }
      yo.push_back(yok);
    }

    bool test = true;
    for (int l=1; l<yo[maxorbind].size() && test; l++) {
      ridge r;
      for (int m=0; m<nv; m++) {
	r.verts.push_back(yo[m][l%yo[m].size()]);
      }
      for (int u=0; u<2; u++) {
	r.faceindices.push_back(findNextInFacePOrbit(ridges[ridges.size()-1].faceindices[u]));
      }
      test = isRidgeNew(r);
      if (test) {
	cout << "Adding ridge w/ vertices " << convertword(r.verts) << "     (which is on faces " << convertword(r.faceindices) << ")" << endl;
	orbitinds.push_back(ridges.size());
	ridges.push_back(r);
      }
    }
    ridgePOrbits.push_back(orbitinds);
  }
}

void Polytope::addEdge(edge e) {

  vector<int> orbitinds;
  int j = e.origin;
  int k= e.end;
  vector<int> commoninds = intersect(vertices[j].faceindices,vertices[k].faceindices);
  if (commoninds[0]==-1) {
    commoninds.erase(commoninds.begin());
  }
  for (int m=0; m<commoninds.size(); m++) {
    if (isVertexSafe(vertices[j],faces[commoninds[m]]) && isVertexSafe(vertices[k],faces[commoninds[m]])){
      e.faceindices.push_back(commoninds[m]);
    } 
  }
  if (isEdgeNew(e)) {
    cout << "Adding edge... v#" << e.origin << "," << e.end << "     (which is on faces " << convertword(e.faceindices) << ")" << endl;
    orbitinds.push_back(edges.size());
    edges.push_back(e);

    vector<int> jo = getPOrbitOfVertex(j);
    vector<int> ko = getPOrbitOfVertex(k);
    
    bool test = true;
    if (jo.size()<ko.size()) {
      for (int l=1; l<ko.size() && test; l++) {
	edge e;
	e.origin = jo[l%jo.size()];
	e.end = ko[l];
	test = isEdgeNew(e);
	if (test) {
	  int z = edges.size()-1;
	  for (int m=0; m<edges[z].faceindices.size(); m++) {
	    int g = edges[z].faceindices[m];
	    if (g>-1)  {
	      e.faceindices.push_back(findNextInFacePOrbit(g));
	    }
	  }
	  cout << "Adding edge... v#" << e.origin << "," << e.end << "     (which is on faces " << convertword(e.faceindices) << ")" << endl;
	  orbitinds.push_back(edges.size());
	  edges.push_back(e);
	}
      }    
    }
    else {
      if (jo.size()>ko.size()) {
	for (int l=1; l<jo.size() && test; l++) {
	  edge e;
	  e.origin = jo[l];
	  e.end = ko[l%ko.size()];
	  test = isEdgeNew(e);
	  if (test) {
	    int z = edges.size()-1;
	    for (int m=0; m<edges[z].faceindices.size(); m++) {
	      int g = edges[z].faceindices[m];
	      if (g>-1)  {
		e.faceindices.push_back(findNextInFacePOrbit(g));
	      }
	    }
	    cout << "Adding edge... v#" << e.origin << "," << e.end << "     (which is on faces " << convertword(e.faceindices) << ")" << endl;
	    orbitinds.push_back(edges.size());
	    edges.push_back(e);
	  }
	}    
      }
      else {
	for (int l=1; l<jo.size() && test; l++) {
	  edge e;
	  e.origin = jo[l];
	  e.end = ko[l];
	  test = isEdgeNew(e);
	  if (test) {
	    int z = edges.size()-1;
	    for (int m=0; m<edges[z].faceindices.size(); m++) {
	      int g = edges[z].faceindices[m];
	      if (g>-1)  {
		e.faceindices.push_back(findNextInFacePOrbit(g));
	      }
	    }
	    cout << "Adding edge... v#" << e.origin << "," << e.end << "     (which is on faces " << convertword(e.faceindices) << ")" << endl;
	    orbitinds.push_back(edges.size());
	    edges.push_back(e);
	  }
	}
      }
    }
    edgePOrbits.push_back(orbitinds);
  }
  
}

bool Polytope::isSame(ridge e1, ridge e2) {
  if (e1.verts.size()!=e2.verts.size()) {
    return false;
  }
  else {
    bool res = true;
    int l=0;
    while (l<e1.verts.size() && res)  {
      res = e1.verts[l]==e2.verts[l];
      l++;
    }
    return res;
  }
}


bool Polytope::isRotatedFlip(ridge e1, ridge e2) {
  int nv = e1.verts.size();
  if (nv!=e2.verts.size()) {
    return false;
  }
  else {
    int k1 = e1.verts[0];
    int l=0;
    bool fd = false;
    while (!fd && l<nv) {
      fd = k1==e2.verts[l];
      l++;
    }
    if (!fd) {
      return false;
    }
    else {
      int k2 = (l-1)%nv;
      bool res = true;
      int m=1;
      while (res && m<nv) {
	res = e1.verts[m]==e2.verts[(k2+m)%nv];
	m++;
      }
      if (res) {
	return true;
      }
      else {
	int k2 = (l-1)%nv;
	bool res = true;
	int m=1;
	while (res && m<nv) {
	  int k2p;
	  if (k2-m>-1) {
	    k2p = k2-m;
	  }
	  else {
	    k2p = k2-m+nv;
	  }
	  res = e1.verts[m]==e2.verts[k2p];
	  m++;
	}
	return res;
      }
    }
  }  
}

// following version assumed the ridges were always oriented the same way,
//   this fails when we remove degenerate faces...
bool Polytope::isRotated(ridge e1, ridge e2) {
  int nv = e1.verts.size();
  if (nv!=e2.verts.size()) {
    return false;
  }
  else {
    int k1 = e1.verts[0];
    int l=0;
    bool fd = false;
    while (!fd && l<nv) {
      fd = k1==e2.verts[l];
      l++;
    }
    if (!fd) {
      return false;
    }
    else {
      int k2 = (l-1)%nv;
      bool res = true;
      int m=1;
      while (res && m<nv) {
	res = e1.verts[m]==e2.verts[(k2+m)%nv];
	m++;
      }
      return res;
    }
  }  
}

bool Polytope::isRidgeNew(ridge r) {
  bool res = true;
  int l=0;
  while (res && l<ridges.size()){
    res = !isRotated(r,ridges[l]); // apparently should also check if it is flipped!! (occurs in Gamma(6,2/3), eg)

    if (res) {
      if (isRotatedFlip(r,ridges[l])){
	cout << "Rotation with a flip!" << endl;
	res = false;
      }
    }

    l++;
  }
  return res;
}

bool Polytope::testSame(WVector V, WVector W){
  return K.areDep(V.coords,W.coords);
}

bool Polytope::isInList(WVector V, vector<WVector> li){
  int n = li.size();
  bool res = false;
  int k = 0;
  while (!res && k<n) {
    res = testSame(V,li[k]);
    k++;
  }
  return res;
}

int Polytope::findInList(gen V, vector<WVector> li){
  vector<int> w;
  WVector W = WVector(V,w);
  return findInList(W,li);
}

int Polytope::findInList(WVector V, vector<WVector> li){
  int n = li.size();
  bool res = false;
  int k = 0;
  while (!res && k<n) {
    res = testSame(V,li[k]);
    k++;
  }
  if (res) {
    return (k-1);
  }
  else {
    return -1;
  }
}

bool Polytope::computeVertices(Prism &A) {

  cout << "Will now compute vertices for " << A << endl;

  gen te = SqNorm(A.apex.coords);
  int res;
  if (K.checkSign(te,res)) {
    if (verbose) {
      cout << "Sign of sq nm of apex: " << res << endl;
    }

    int k1 = 0;
    int l1 = A.sides[0].word.size();
    for (int k=1; k<A.sides.size(); k++) {
      int l = A.sides[k].word.size();
      if (l<l1) {
	l1 = l;
	k1 = k;
      }
    }
    int k2;
    if (k1==0) {
      if (A.sides[A.sides.size()-1].word.size()<A.sides[1].word.size()) {
	k2 = A.sides.size()-1;
      }
      else {
	k2 = 1;
      }
    }
    else {
      k2 = (k1+1)%A.sides.size();
    }
    
    gen M1 = reflMat(A.sides[k1].coords,mult)*reflMat(A.sides[k2].coords,mult);
    K.matred(M1);
    int om1 = K.order(M1);
    if (om1==-1) {
      cout << "Product of infinite order" << endl;
    }
    else {
      cout << "Product of order " << om1 << endl;      
    }
    int bo = K.braidOrder(reflMat(A.sides[k1].coords,mult),reflMat(A.sides[k2].coords,mult));
    cout << "Braid length " << bo << endl;

    vector<int> w0 = concat(A.sides[k1].word,A.sides[k2].word);
    vector<int> w = concat(A.sides[k1].word,A.sides[k2].word);

    bool isrefl = false;
    int k=1;
    gen M = M1;
    while (k<om1 && !isrefl) {
      gen dt = K.discrAdjusted(M);
      isrefl = operator_equal(dt,K.zeronumber,&ct);
      if (isrefl) {
	int sg;
	if (!K.checkSign(SqNorm(A.apex.coords),sg)) {
	  cerr << "Problem checking sign" << endl;
	  exit(1);
	}
	else {
	  if (sg>0) {
	    gen X1 = BoxProd(A.cspine,A.apex.coords);
	    gen X2 = M*X1;
	    K.vectred(X2);
	    isrefl = K.areDep(X1,X2);
	  }
	  else {
	    if (sg<0) {
	      gen X1 = A.apex.coords;
	      gen X2 = M*X1;
	      K.vectred(X2);
	      isrefl = K.areDep(X1,X2);
	    }
	    else {
	      // don't know what to do...
	    }
	  }
	}
      }
      if (isrefl) {
	if (verbose) {
	  cout << "Found reflection, power " << k << endl;
	}
	A.toppower = k;
	A.toporder = om1/k;
      }
      else {
	M = M*M1;
	w = concat(w,w0);
	K.matred(M);
      }
      k++;
    }
    if (!isrefl) {
      if (verbose) {
	cout << "Did not find any reflection power..." << endl;
      }
      //      exit(1);
    }

    A.apex.word = w;
    if (verbose) {
      cout << "word for apex=" << convertword(w) << endl;
    }

    if (res<1) {
      if (verbose) {
	if (res==0) {
	  cout << "Single (IDEAL) vertex at top" << endl;
	}
	else {
	  cout << "Single vertex at top" << endl;
	}
      }
      A.topface.push_back(A.apex);
    }
    else {
      if (verbose) {
	cout << "Top face is not a point.." << endl;
      }
      
      for (int k=0; k<A.sides.size(); k++) {
	gen yo = BoxProd(A.apex.coords,A.sides[k].coords);
	WVector Vk( yo, concat(A.apex.word,A.sides[k].word) );
	A.topface.push_back(Vk);
        if (verbose) {
	  cout << "Vertex for " << convertword(A.sides[k].word) << endl;
	}
	if (K.checkSign(SqNorm(Vk.coords),res)){
	  if (res>0) {
	    cerr << "This should not happen!" << endl;
	    exit(1);
	  }
	  if (res==0) {
	    cout << " (previous vertex is IDEAL)" << endl;
	  }
	}
	else {
	  cerr << "Problem checking sign!" << endl;
	  exit(1);	  
	}	
      }
    }
  }
  else {
    cout << "Problem checking sign... " << K.evali(te) << endl;
  }

  if (verbose) {
    cout << "Constructing bottom face..." << endl;
  }
  for (int k=0; k<A.sides.size(); k++) {
    gen yo = BoxProd(A.mirror.coords,A.sides[k].coords);
    if (K.checkSign(SqNorm(yo),res)){
      if (res>0) {
	if (verbose) {
	  cout << "#" << k << "(" << convertword(A.sides[k].word) << "): bottom and mid vertex." << endl;
	}

	gen M1 = reflMat(A.mirror.coords,mult)*reflMat(A.sides[k].coords,mult);
	K.matred(M1);
	int om1 = K.order(M1);
	if (verbose) {
	  cout << "Product of order " << om1 << endl;
	}
	int bo = K.braidOrder(reflMat(A.mirror.coords,mult),reflMat(A.sides[k].coords,mult));
	if (verbose) {
	  cout << "Braid length " << bo << endl;
	}

	vector<int> w0 = concat(A.mirror.word,A.sides[k].word);
	vector<int> wk = concat(A.mirror.word,A.sides[k].word);

	bool isrefl = false;
	int kk=1;
	gen M = M1;
	while (kk<om1 && !isrefl) {
	  gen dt = K.discrAdjusted(M);
	  isrefl = operator_equal(dt,K.zeronumber,&ct);

	  if (isrefl) {
	    gen X1 = BoxProd(A.sides[k].coords,yo);
	    gen X2 = M*X1;
	    isrefl = K.areDep(X1,X2);
	  }
	  
	  // also want to check if vertex is fixed.
	  if (verbose && isrefl) {
	    cout << "Found reflection, power " << kk << endl;
	  }
	  M = M*M1;
	  wk = concat(wk,w0);
	  K.matred(M);
	  kk++;
	}
	if (verbose && !isrefl) {
	  cout << "Did not find any reflection power..." << endl;
	  //	  exit(1);
	}

	WVector nm(yo,wk);

	gen yok1 = BoxProd(A.mirror.coords,yo);
	gen yok2 = BoxProd(A.sides[k].coords,yo);
	WVector Wk1(yok1,concat(A.mirror.word,wk));
	WVector Wk2(yok2,concat(A.sides[k].word,wk));
	if (K.checkSign(SqNorm(Wk1.coords),res)){
	  if (res>-1) {
	    cerr << "This should not happen!" << endl;
	    exit(1);
	  }
	}
	else {
	  cerr << "Problem checking sign!" << endl;
	  exit(1);	  
	}	
	if (K.checkSign(SqNorm(Wk2.coords),res)){
	  if (res>-1) {
	    cerr << "This should not happen!" << endl;
	    exit(1);
	  }
	}
	else {
	  cerr << "Problem checking sign!" << endl;
	  exit(1);	  
	}	
	A.bottomface.push_back(Wk1);
	A.midvertices.push_back(Wk2);
	A.midvertindices.push_back(k);
      }
      else {
	if (verbose) {
	  cout << "#" << k << "(" << convertword(A.sides[k].word) << "): Single bottom vertex." << endl;
	}
	WVector Vk( yo, concat(A.mirror.word,A.sides[k].word) );
	A.bottomface.push_back(Vk);
	if (verbose && res==0) {
	  cout << " (previous vertex is IDEAL)" << endl;
	}
      }
    }
    else {
      cerr << "Problem checking sign!" << endl;
      exit(1);	  
    }	
  }
  

}

bool Polytope::isOnMirror(gen X, Prism A) {
  gen te = Inn(X,A.mirror.coords);
  return operator_equal(te,K.zeronumber,&ct);  
}

bool Polytope::isOnTopFace(gen X, Prism A) {
  gen te = Inn(X,A.apex.coords);
  return operator_equal(te,K.zeronumber,&ct);  
}

bool Polytope::isOnPrism(gen X, Prism A){
  gen te = Inn(X,A.apex.coords)*Inn(A.apex.coords,A.apexProjection)*Inn(A.apexProjection,X);
  K.red(te);
  return K.isReal(te);
}

gen Polytope::reflectAcrossSpine(gen X, Prism A){
  vecteur co;
  co.push_back(Inn(A.apex.coords,A.apex.coords));
  co.push_back(Inn(A.apex.coords,A.apexProjection));
  co.push_back(Inn(A.apexProjection,A.apex.coords));
  co.push_back(Inn(A.apexProjection,A.apexProjection));
  gen M = K.createMatrix(2,2,co);

  vecteur cov;
  cov.push_back(Inn(X,A.apex.coords));
  cov.push_back(Inn(X,A.apexProjection));
  gen b = gen(cov);

  gen de = K.det(M);
  K.red(de);
  if (operator_equal(de,K.zeronumber,&ct)) {
    cerr << "Problem reflecting across spine" << endl;
    exit(1);
  }
  else {
    gen dei = K.inverse(de);
    vecteur coeffs;
    coeffs.push_back(dei*(M[1][1]*b[0]-M[0][1]*b[1]));
    coeffs.push_back(dei*(-M[1][0]*b[0]+M[0][0]*b[1]));
    gen Y = K.myconj(coeffs[0])*A.apex.coords + K.myconj(coeffs[1])*A.apexProjection;
    K.vectred(Y);
    return Y;
  }
}

Prism Polytope::createPimage(Prism A) {
  Prism A1;

  A1.toporder = A.toporder;
  A1.toppower = A.toppower;

  gen m = P*A.mirror.coords;
  K.vectred(m);
  vector<int> w1;
  if (is_symmetric) {
    w1 = conjugateword(word_p1,shiftIndicesUp(A.mirror.word));
  }
  else {
    w1 = conjugateword(word_123,A.mirror.word);
  }
  useRelations(w1);
  A1.mirror = WVector(m,w1);

  if (is_symmetric) {
    A1.apex = WVector(P*A.apex.coords, conjugateword(word_p1,shiftIndicesUp(A.apex.word)));
  }
  else {
    A1.apex = WVector(P*A.apex.coords, conjugateword(word_123,A.apex.word));
  }
  K.vectred(A1.apex.coords);
  useRelations(A1.apex.word);

  A1.apexProjection = P*A.apexProjection;
  K.vectred(A1.apexProjection);

  A1.cspine = P*A.cspine;
  K.vectred(A1.cspine);
  
  A1.X0 = P*A.X0;
  A1.X1 = P*A.X1;
  K.vectred(A1.X0);
  K.vectred(A1.X1);

  for (int k=0; k<A.sides.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = conjugateword(word_p1,shiftIndicesUp(A.sides[k].word));
    }
    else {
      wk = conjugateword(word_123,A.sides[k].word);
    }
    gen sk = P*A.sides[k].coords;
    K.vectred(sk);
    A1.sides.push_back(WVector(sk,wk));
  }

  for (int k=0; k<A.bottomface.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = conjugateword(word_p1,shiftIndicesUp(A.bottomface[k].word));
    }
    else {
      wk = conjugateword(word_123,A.bottomface[k].word);
    }
      gen sk = P*A.bottomface[k].coords;
      K.vectred(sk);
      A1.bottomface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.topface.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = conjugateword(word_p1,shiftIndicesUp(A.topface[k].word));
    }
    else {
      wk = conjugateword(word_123,A.topface[k].word);
    }
    gen sk = P*A.topface[k].coords;
    K.vectred(sk);
    A1.topface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.midvertices.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = conjugateword(word_p1,shiftIndicesUp(A.midvertices[k].word));
    }
    else {
      wk = conjugateword(word_123,A.midvertices[k].word);
    }
    gen sk = P*A.midvertices[k].coords;
    K.vectred(sk);
    A1.midvertices.push_back(WVector(sk,wk));
    A1.midvertindices.push_back(A.midvertindices[k]);
  }

  A1.isSurrounded = false;
  for (int k=0; k<A1.sides.size(); k++) {
    A1.freeRidges.push_back(k);
  }
  A1.jstart = A.jstart;

  return A1;
  
}

Prism Polytope::createPinvimage(Prism A) {

  Prism A1;

  A1.toporder = A.toporder;
  A1.toppower = A.toppower;
 
  gen m = Pi*A.mirror.coords;
  K.vectred(m);
  vector<int> w1;
  if (is_symmetric) {
    w1 =shiftIndicesDown(conjugateword(word_m1,A.mirror.word));
  }
  else {
    w1 = conjugateword(word_321,A.mirror.word);
  }
  A1.mirror = WVector(m,w1);
  useRelations(A1.mirror.word);

  if (is_symmetric) {
    A1.apex = WVector(Pi*A.apex.coords,shiftIndicesDown(conjugateword(word_m1,A.apex.word)));
  }
  else {
    A1.apex = WVector(Pi*A.apex.coords,conjugateword(word_321,A.apex.word));
  }
  K.vectred(A1.apex.coords);
  useRelations(A1.apex.word);

  A1.apexProjection = Pi*A.apexProjection;
  K.vectred(A1.apexProjection);

  A1.cspine = Pi*A.cspine;
  K.vectred(A1.cspine);
  
  A1.X0 = Pi*A.X0;
  A1.X1 = Pi*A.X1;
  K.vectred(A1.X0);
  K.vectred(A1.X1);

  for (int k=0; k<A.sides.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = shiftIndicesDown(conjugateword(word_m1,A.sides[k].word));
    }
    else {
      wk = conjugateword(word_321,A.sides[k].word);      
    }
    gen sk = Pi*A.sides[k].coords;
    K.vectred(sk);
    A1.sides.push_back(WVector(sk,wk));
  }

  for (int k=0; k<A.bottomface.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = shiftIndicesDown(conjugateword(word_m1,A.bottomface[k].word));
    }
    else {
      wk = conjugateword(word_321,A.bottomface[k].word);
    }
    gen sk = Pi*A.bottomface[k].coords;
    K.vectred(sk);
    A1.bottomface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.topface.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = shiftIndicesDown(conjugateword(word_m1,A.topface[k].word));
    }
    else {
      wk = conjugateword(word_m1,A.topface[k].word);
    }
    gen sk = Pi*A.topface[k].coords;
    K.vectred(sk);
    A1.topface.push_back(WVector(sk,wk));
  }
  for (int k=0; k<A.midvertices.size(); k++) {
    vector<int> wk;
    if (is_symmetric) {
      wk = shiftIndicesDown(conjugateword(word_m1,A.midvertices[k].word));
    }
    else {
      wk = conjugateword(word_m1,A.midvertices[k].word);
    }
    gen sk = Pi*A.midvertices[k].coords;
    K.vectred(sk);
    A1.midvertices.push_back(WVector(sk,wk));
    A1.midvertindices.push_back(A.midvertindices[k]);
  }

  A1.isSurrounded = false;
  for (int k=0; k<A1.sides.size(); k++) {
    A1.freeRidges.push_back(k);
  }
  A1.jstart = A.jstart;

  return A1;

}

// this is better when P has infinite order, but here it doesn't make much sense...
void Polytope::addFaceOrbit(Prism A) {

    if (isPrismNew(A)) {

      vector<Prism> facestoadd;

      faces.push_back(A);
      faceReps.push_back(A);
      cout << "Adding " << A << endl;
      updateFreeRidges();
      bool done = false;
      Prism Ak = A.mycopy();
      Prism Aki = A.mycopy();
      int k=0;
      while (!done && k<100) {
	Ak = createPimage(Ak);
	done = comparePrisms(Ak,Aki);
	if (!done) {
	  useRelations(Ak);
	  facestoadd.push_back(Ak);
	  Aki = createPinvimage(Aki);
	  done = comparePrisms(Aki,Ak);
	  if (!done) {
	    useRelations(Aki);
	    facestoadd.push_back(Aki);
	  }
	}
	k++;
      }
      if (!done) {
	cerr << "P does not have finite order? (its order is at least 100) " << endl;
	exit(1);
      }

      vector<int> inds;
      int u = facestoadd.size();
      if (u%2!=0) {
	u--;
	inds.push_back(u);
      }
      while (u>1) {
	u--;
	inds.push_back(u);
	u--;
	inds.insert(inds.begin(),u);
      }

      vector<int> inds2;
      int nf = faces.size();
      inds2.push_back(nf-1);
      for (int k=0; k<inds.size(); k++) {
	inds2.push_back(nf+k);
      }
      facePOrbits.push_back(inds2);

      for (int k=0; k<inds.size(); k++) {
	faces.push_back(facestoadd[inds[k]]);
	cout << "Adding " << facestoadd[inds[k]] << endl;
	updateFreeRidges();
      }

    }
    else {
      cout << "Should not add this prism, it's already in the list!" << endl;
    }
    
}

vector<WVector> Polytope::findMirrors(gen V) {
  vector<WVector> res;
  for (int k=0; k<faces.size(); k++) {
    if (isOnMirror(V,faces[k])) {
      if (findInList(faces[k].mirror,res)<0) {
	res.push_back(faces[k].mirror);
      }
    }
    if (isOnTopFace(V,faces[k])) {
      if (findInList(faces[k].apex,res)<0) {
	res.push_back(faces[k].apex);
      }
    }
  }
  return res;
}

vector<WVector> Polytope::findMirrors(WVector v) {
  return findMirrors(v.coords);
}

vector<int> Polytope::findFaces(gen V) {
  vector<int> res;
  gen te = SqNorm(V);
  if (operator_equal(te,K.zeronumber,&ct)) {
    res.push_back(-1);
  }
  for (int k=0; k<faces.size(); k++) {
    if (isOnPrism(V,faces[k])) {
      bool fd = false;
      int u=0;
      while (!fd && u<faces[k].topface.size()) {
	fd = K.areDep(V,faces[k].topface[u].coords);
	u++;
      }
      if (!fd) {
	u=0;
	while (!fd && u<faces[k].midvertices.size()) {
	  fd = K.areDep(V,faces[k].midvertices[u].coords);
	  u++;
	}
	if (!fd) {
	  u=0;
	  while (!fd && u<faces[k].bottomface.size()) {
	    fd = K.areDep(V,faces[k].bottomface[u].coords);
	    u++;
	  }
	  if (fd) {
	    res.push_back(k);
	  }
	}
	else {
	  res.push_back(k);
	}
      }
      else {
	res.push_back(k);
      }
    }
  }
  return res;
}

// beware this finds more faces than "findFaces", in general
vector<int> Polytope::findBisectors(gen V) {
  vector<int> res;
  gen te = SqNorm(V);
  if (operator_equal(te,K.zeronumber,&ct)) {
    res.push_back(-1);
  }
  for (int k=0; k<faces.size(); k++) {
    if (isOnPrism(V,faces[k])) {
      res.push_back(k);
    }
  }
  return res;
}


vector<int> Polytope::findFaces(WVector v) {
  return findFaces(v.coords);
}

vector<int> Polytope::findBisectors(WVector v) {
  return findBisectors(v.coords);
}

void Polytope::useRelations(vector<int> &w) {
  int n = w.size();
  bool reduced = false;
  while (n>0 && !reduced) {
    int ns = w.size();
    for (int k=0; k<relations.size(); k++)  {
      vector<int> rel = relations[k];
      if (2*w.size()>rel.size()) {
	simplifyword(w,rel);
      }
    }
    reduced = ns==w.size();
  }
}

void Polytope::useRelations(Prism &A) {
  useRelations(A.mirror.word);
  for (int k=0; k<A.sides.size(); k++) {
    useRelations(A.sides[k].word);
  }
  for (int k=0; k<A.bottomface.size(); k++) {
    useRelations(A.bottomface[k].word); 
 }
  for (int k=0; k<A.topface.size(); k++) {
    useRelations(A.topface[k].word);
  }
  for (int k=0; k<A.midvertices.size(); k++) {
    useRelations(A.midvertices[k].word);
  }
}

void Polytope::studyVertexStabilizers() {
  for (int k=0; k<vertReps.size(); k++) {
    orbitsToVisit.push_back(k);
  }
  while (orbitsToVisit.size()>0) {
    int k = orbitsToVisit[0];
    cout << "TEST k=" << k << endl;
    currentVertexOrbit.clear();
    findNextVertexInCycle(k);
  }
  for (int k=0; k<vertexOrbits.size(); k++) {
    cout << "Vertex orbit #" << k << ": " << vertexOrbits[k] << endl;
  }

}

void Polytope::findNextVertexInCycle(int k) {

  currentVertexOrbit.push_back(k);

  cout << "Current vertex orbit " << currentVertexOrbit << endl;
  cout << "Orbits to visit: " << orbitsToVisit << endl;

  bool fd = false;
  int kt = 0;
  while (!fd && kt<orbitsToVisit.size()) {
    fd = orbitsToVisit[kt]==k;
    kt++;
  }
  if (!fd) {
    cerr << "Problem..." << endl;
    exit(1);
  }
  else {
    orbitsToVisit.erase(orbitsToVisit.begin()+(kt-1));
  }

  cout << "Orbits to visit (after): " << orbitsToVisit << endl;

  vector<int> te = findFaces(vertReps[k]);
  cout << endl;
  int yo = findInList(vertReps[k],vertices);
  cout << "Vertex rep #" << k << " (#" << yo << " in list) is on bisectors " << te << endl;
  for (int l=0; l<te.size(); l++) {
    if (te[l]>-1) {
      cout << "Applying pairing for face #" << te[l] << endl;
      if (verbose) {
	cout << "te=" << te << endl;
      }
      
      gen vimage = faces[te[l]].pairing*vertReps[k].coords;
      K.vectred(vimage);
      int m = findInList(vimage,vertices);
      if (m>-1) {
	cout << "Image by pairing is vertex #" << m << ", which is on: " << findFaces(vimage) << endl;
	bool foundOrb = false;
	int u = 0;
	while (!foundOrb && u<vertexPOrbits.size()) {
	  foundOrb = find(vertexPOrbits[u].begin(), vertexPOrbits[u].end(), m)!=vertexPOrbits[u].end();
	  u++;
	}
	if (!foundOrb) {
	  cerr << "Image is in none of the orbits?!" << endl;
	  exit(1);
	}
	else {
	  cout << "Image is in orbit #" << (u-1) << endl;
	  bool fd = false;
	  int kt = 0;
	  while (!fd && kt<currentVertexOrbit.size()) {
	    fd = currentVertexOrbit[kt]==(u-1);
	    kt++;
	  }
	  if (!fd) {
	    cout << "  this was not visited yet..." << endl;
	    findNextVertexInCycle(u-1);
	  }
	  else {
	    cout << "  this was already visited!" << endl;	    
	  }
	}
      }
      else {
	cerr << "Problem with vertex cycles... " << endl;
	exit(1);
      }
    }
  }

  cout << "Registering orbit: " << currentVertexOrbit << endl;
  cout << "Clearing current vertex orbit..." << endl;
  vertexOrbits.push_back(currentVertexOrbit);
  currentVertexOrbit.clear();

  cout << "Done with findNextVertex, k=" << k << endl;
  
}

void Polytope::computeStabilizer(int k) {
  // stab of vertex representative #k
  
  vector<int> te = vertices[vertexPOrbits[k][0]].faceindices; 
  // I hope this was computed before!!
  
  cout << endl;
  cout << "vertex #" << vertexPOrbits[k][0] << " is on: " << te << endl;
  for (int l=0; l<te.size(); l++) {
    if (te[l]>-1) {
      cout << "Applying pairing for face #" << te[l] << endl;

      gen vimage = faces[te[l]].pairing*vertReps[k].coords;
      K.vectred(vimage);
      int m = findInList(vimage,vertices);
      if (m!=-1) {
	cout << "  image by pairing is vertex #" << m << ", which is on: " << vertices[m].faceindices << endl;
	// I hope this was computed before!!

	vector<int> vp;
	vp.push_back(vertexPOrbits[k][0]);
	vp.push_back(te[l]);
	vp.push_back(m);
	vertexPairings.push_back(vp);
	cout << "v#" << vp[0] << " --(" << vp[1] << ")--> v#" << vp[2] << endl; 
	vector<int> vpi;
	vpi.push_back(m);
	vpi.push_back(facePairings[te[l]]);
	vpi.push_back(vertexPOrbits[k][0]);
	vertexPairings.push_back(vpi);
	cout << "v#" << vpi[0] << " --(" << vpi[1] << ")--> v#" << vpi[2] << endl; 
	// probably would like to include its P-conjugates?

	bool fdm = false;
	int v = -1;
	int w;
	while (!fdm && v!=vertexPOrbits.size()-1) {
	  v++;
	  w=-1;
	  while (!fdm && w!=vertexPOrbits[v].size()-1) {
	    w++;
	    fdm = m==vertexPOrbits[v][w];
	  }
	}
	if (!fdm) {
	  cerr << "Problem finding vertex in some orbit!" << endl;
	  exit(1);
	}

	bool fdm1 = false;
	int v1 = -1;
	int w1;
	while (!fdm1 && v1!=facePOrbits.size()-1) {
	  v1++;
	  w1=-1;
	  while (!fdm1 && w1!=facePOrbits[v1].size()-1) {
	    w1++;
	    fdm1 = te[l]==facePOrbits[v1][w1];
	  }
	}
	if (!fdm1) {
	  cerr << "Problem finding face index in some orbit!" << endl;
	  exit(1);
	}

	int n = vertexPOrbits[v].size();
	int n1 = facePOrbits[v1].size();
	if (n>n1) {
	  cout << "Interesting, a vertex has smaller P-orbit than the face.." << endl;	  
	}
	for (int u=1; u<n; u++) {
	  vector<int> vpu;
	  vpu.push_back(vertexPOrbits[k][u]);
	  vpu.push_back(facePOrbits[v1][(w1+u)%n1]);
	  vpu.push_back(vertexPOrbits[v][(w+u)%n]);
	  vertexPairings.push_back(vpu);
	  cout << "v#" << vpu[0] << " --(" << vpu[1] << ")--> v#" << vpu[2] << endl; 
	  vector<int> vpui;
	  vpui.push_back(vertexPOrbits[v][(w+u)%n]);
	  vpui.push_back(facePairings[facePOrbits[v1][(w1+u)%n1]]);
	  vpui.push_back(vertexPOrbits[k][u]);
	  vertexPairings.push_back(vpui);
	  cout << "v#" << vpui[0] << " --(" << vpui[1] << ")--> v#" << vpui[2] << endl; 
	}

	bool foundOrb = false;
	int u = 0;
	int vv;
	while (!foundOrb && u<vertexPOrbits.size()) {
	  vv = -1;
	  while (!foundOrb && vv!=vertexPOrbits[u].size()-1) {
	    vv++;
	    foundOrb = vertexPOrbits[u][vv]==m;
	  }
	  u++;
	}
	if (!foundOrb) {
	  cerr << "Image is in none of the orbits?!" << endl;
	  exit(1);
	}
	else {
	  vector<int> pa;
	  pa.push_back(k);
	  pa.push_back(u-1);
	  vertexPOrbitPairings.push_back(pa);
	  vector<int> w;
	  if (faces[te[l]].invertpairing) {
	    w = invertword(faces[te[l]].mirror.word);
	  }
	  else {
	    w = concat(w,faces[te[l]].mirror.word);
	  }

	  if (vv>orderofp/2) {
	    for (int zz=0; zz<orderofp-vv; zz++) { // could do better, if v is more than order of P
	      w.insert(w.begin(),4);
	    }
	  }
	  else {
	    for (int zz=0; zz<vv; zz++) { // could do better, if v is more than order of P
	      w.insert(w.begin(),-4);
	    }
	  }
	  vertexPOrbitPairingMaps.push_back(w);
	  cout << "   (image is in orbit #" << (u-1) << ")" << endl;
	}
      }
      else {
	cerr << "Problem with vertex cycles... " << endl;
	exit(1);
      }
    }
  }
}

gen Polytope::evalWord(vector<int> w) {
  gen res = K.Id;
  int k=0; 
  while (k<w.size()) {
    int l = w[k];
    if (l==1) {
      res = res*R1;
    }
    else {
      if (l==2) {
	res = res*R2;
      }
      else {
	if (l==3) {
	  res = res*R3;
	}
	else {
	  if (l==-1) {
	    res = res*R1i;
	  }
	  else {
	    if (l==-2) {
	      res = res*R2i;
	    }
	    else {
	      if (l==-3) {
		res = res*R3i;
	      }
	      else {
		if (l==4) {
		  res = res*P;
		}
		else {
		  if (l==-4) {
		    res = res*Pi;
		  }
		  else {
		    cerr << "Wrong index in word?" << endl;
		    exit(1);
		  }
		}
	      }
	    }
	  }
	}
      }
    }
    K.matred(res);
    k++;
  }
  return res;
}

gen Polytope::evalWordAsRootOf(vector<int> w) {
  gen res = K.Id;

  gen R1ro,R1iro,R2ro,R2iro,R3ro,R3iro,Pro,Piro;

  vector<int>::iterator it = find(w.begin(),w.end(),1);
  if (it!=w.end()){
    gen R1ro = _subst(makesequence(R1,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(R1ro);
    cout << "reduced R1: " << R1ro << endl;
  }
  
  it = find(w.begin(),w.end(),-1);
  if (it!=w.end()){
    gen R1iro = _subst(makesequence(R1i,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(R1iro);
    cout << "reduced R1^-1: " << R1iro << endl;
  }
  
  it = find(w.begin(),w.end(),2);
  if (it!=w.end()){
    gen R2ro = _subst(makesequence(R2,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(R2ro);
    cout << "reduced R2: " << R2 << endl;
  }
  
  it = find(w.begin(),w.end(),-2);
  if (it!=w.end()){
    gen R2iro = _subst(makesequence(R2i,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(R2iro);
    cout << "reduced R2^-1: " << R2iro << endl;
  }
  
  it = find(w.begin(),w.end(),3);
  if (it!=w.end()){
    gen R3ro = _subst(makesequence(R3,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(R3ro);
    cout << "reduced R3: " << R3ro << endl;
  }
  
  it = find(w.begin(),w.end(),-3);
  if (it!=w.end()){
    gen R3iro = _subst(makesequence(R3i,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(R3iro);
    cout << "reduced R3^-1: " << R3iro << endl;
  }
  
  it = find(w.begin(),w.end(),4);
  if (it!=w.end()){
    gen Pro = _subst(makesequence(P,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(Pro);
    cout << "reduced P: " << Pro << endl;
  }
  
  it = find(w.begin(),w.end(),-4);
  if (it!=w.end()){
    gen Piro = _subst(makesequence(Pi,x__IDNT_e,K.genasrootof),&ct);
    reduce_degree_mat(Piro);
    cout << "reduced P^-1: " << Piro << endl;
  }
  
  int k=0; 
  while (k<w.size()) {
    int l = w[k];
    if (l==1) {
      res = res*R1ro;
      reduce_degree_mat(res);
    }
    else {
      if (l==2) {
	res = res*R2ro;
	reduce_degree_mat(res);
      }
      else {
	if (l==3) {
	  res = res*R3ro;
	  reduce_degree_mat(res);
	}
	else {
	  if (l==-1) {
	    res = res*R1iro;
	    reduce_degree_mat(res);
	  }
	  else {
	    if (l==-2) {
	      res = res*R2iro;
	      reduce_degree_mat(res);
	    }
	    else {
	      if (l==-3) {
		res = res*R3iro;
		reduce_degree_mat(res);
	      }
	      else {
		if (l==4) {
		  res = res*Pro;
		  reduce_degree_mat(res);
		}
		else {
		  if (l==-4) {
		    res = res*Piro;
		    reduce_degree_mat(res);
		  }
		  else {
		    cerr << "Wrong index in word?" << endl;
		    exit(1);
		  }
		}
	      }
	    }
	  }
	}
      }
    }
    k++;
  }
  return res;
}

void Polytope::createBraidRelation(vector<int> w1, vector<int> w2) {
  gen A = evalWord(w1);
  gen B = evalWord(w2);
  cout << "Creating braid relation for " << w1 << " and " << w2 << endl;

  int bo = K.braidOrder(A,B);
  int bo2 = bo/2;

  cout << convertword(w1) << " and " << convertword(w2) << "  braid with order " << bo << endl;

  vector<int> w1i = invertword(w1);
  vector<int> w2i = invertword(w2);
  
  vector<int> r1;
  for (int k=0; k<bo2; k++) {
    r1 = concat(r1,w1);
    r1 = concat(r1,w2);
  }
  if (bo%2!=0) {
    r1 = concat(r1,w1);
    r1 = concat(r1,w2i);
  }
  for (int k=0; k<bo2; k++) {
    r1 = concat(r1,w1i);
    r1 = concat(r1,w2i);
  }

  vector<int> r2;
  for (int k=0; k<bo2; k++) {
    r2 = concat(r2,w2);
    r2 = concat(r2,w1);
  }
  if (bo%2!=0) {
    r2 = concat(r2,w2);
    r2 = concat(r2,w1i);

  }
  for (int k=0; k<bo2; k++) {
    r2 = concat(r2,w2i);
    r2 = concat(r2,w1i);
  }

  relations.push_back(r1);
  relations.push_back(r2);
  if (is_symmetric) {
    relations.push_back(shiftIndicesUp(r1));
    relations.push_back(shiftIndicesUp(r2));
    relations.push_back(shiftIndicesDown(r1));
    relations.push_back(shiftIndicesDown(r2));  
  }

}

bool Polytope::isVertexSafe(WVector V, Prism A) {
  for (int j=0; j<A.topface.size(); j++) {
    if (testSame(V,A.topface[j])) {
      return true;
    }
  }
  for (int j=0; j<A.midvertices.size(); j++) {
    if (testSame(V,A.midvertices[j])) {
      return true;
    }
  }
  for (int j=0; j<A.bottomface.size(); j++) {
    if (testSame(V,A.bottomface[j])) {
      return true;
    }
  }
  return false;  
}

// this is repeated under another name (small caps for t)
int Polytope::applyPToVertex(int pow, int jvertex) {
  // locate jvertex in a P-orbit, then shift
  bool fd = false;
  int j=-1;
  int n;
  int k;
  while (!fd && j+1!=vertexPOrbits.size()) {
    j++;
    fd = false;
    n = vertexPOrbits[j].size();
    k=-1;
    while (!fd && k+1!=n) {
      k++;
      fd = (vertexPOrbits[j][k]==jvertex);
    }
  }
  if (!fd) {
    cerr << "Trouble locating vertex in a P-orbit" << endl;
    exit(1);
  }
  else {
    return vertexPOrbits[j][(k+pow)%n];
  }
}

int Polytope::applyPToEdge(int pow, int jedge) {
  int jo = applyPToVertex(pow, edges[jedge].origin);
  int je = applyPToVertex(pow, edges[jedge].end);
  if (jo<0 || je<0) {
    cerr << "Trouble computing P-image of a 1-face (vertices are already a problem)!" << endl;
    exit(1);
  }
  else {
    bool fd = false;
    int u = -1;
    while (!fd && u!=edges.size()) {
      u++;
      fd = ( (edges[u].origin==jo && edges[u].end==je) || (edges[u].origin==je && edges[u].end==jo) ) ;
    } 
    if (!fd) {
      cerr << "Trouble computing image of a 1-face!" << endl;
      exit(1);
    }
    else {
      return u;
    }
  }  
}

int Polytope::applyPToRidge(int pow, int j) {
  int nv = ridges[j].verts.size();
  vector<int> verteximages;
  for (int u=0; u<nv; u++) {
    int jo = applyPToVertex(pow, ridges[j].verts[u]);
    if (jo<0) {
      cerr << "Trouble computing image of a ridge!" << endl;
      exit(1);
    }
    else {
      verteximages.push_back(jo);
    }
  }
  vector<int> commoninds;
  for (int u=0; u<vertices[verteximages[0]].faceindices.size(); u++) {
    commoninds.push_back(vertices[verteximages[0]].faceindices[u]);
  }
  for (int u=1; u<verteximages.size(); u++) {
    commoninds = intersect(commoninds,vertices[verteximages[u]].faceindices);
  }
  if (commoninds[0]==-1) {
    commoninds.erase(commoninds.begin());
  }
  if (commoninds.size()!=2) {
    cerr << "Trouble identifying image of a ridge (nb of indices containing verts: "<<commoninds.size()<<")" << endl;
    exit(1);
  }
  else {
    bool fd = false;
    int v=-1;
    while (!fd && v!=ridges.size()-1) {
      v++;
      fd = ( (ridges[v].faceindices[0]==commoninds[0] && ridges[v].faceindices[1]==commoninds[1]) || (ridges[v].faceindices[1]==commoninds[0] && ridges[v].faceindices[0]==commoninds[1]));
    }
    if (!fd) {
      cerr << "Trouble identifying image of a ridge." << endl;
      exit(1);
    }
    else {
      return v;
    }
  }
}

int Polytope::applyPinvToVertex(int pow, int jvertex) {
  // locate jvertex in a P-orbit, then shift
  bool fd = false;
  int j=-1;
  int n, k;
  while (!fd && j+1!=vertexPOrbits.size()) {
    j++;
    k=-1;
    bool fd = false;
    n = vertexPOrbits[j].size();
    while (!fd && k+1!=n) {
      k++;
      fd = (vertexPOrbits[j][k]==jvertex);
    }
  }
  if (!fd) {
    cerr << "Trouble locating vertex in a P-orbit" << endl;
    exit(1);
  }
  else {
    return vertexPOrbits[j][(k-pow)%n];
  }
}

int Polytope::applyPinvToEdge(int pow, int jedge) {
  int jo = applyPinvToVertex(pow, edges[jedge].origin);
  int je = applyPinvToVertex(pow, edges[jedge].end);
  if (jo<0 || je<0) {
    cerr << "Trouble computing P-image of a 1-face (vertices are already a problem)!" << endl;
    exit(1);
  }
  else {
    bool fd = false;
    int u = -1;
    while (!fd && u!=edges.size()) {
      u++;
      fd = ( (edges[u].origin==jo && edges[u].end==je) || (edges[u].origin==je && edges[u].end==jo) ) ;
    } 
    if (!fd) {
      cerr << "Trouble computing image of a 1-face!" << endl;
      exit(1);
    }
    else {
      return u;
    }
  }  
}

int Polytope::applyPinvToRidge(int pow, int j) {
  int nv = ridges[j].verts.size();
  vector<int> verteximages;
  for (int u=0; u<nv; u++) {
    int jo = applyPinvToVertex(pow, ridges[j].verts[u]);
    if (jo<0) {
      cerr << "Trouble computing image of a ridge!" << endl;
      exit(1);
    }
    else {
      verteximages.push_back(jo);
    }
  }
  vector<int> commoninds;
  for (int u=0; u<vertices[verteximages[0]].faceindices.size(); u++) {
    commoninds.push_back(vertices[verteximages[0]].faceindices[u]);
  }
  for (int u=1; u<verteximages.size(); u++) {
    commoninds = intersect(commoninds,vertices[verteximages[u]].faceindices);
  }
  if (commoninds[0]==-1) {
    commoninds.erase(commoninds.begin());
  }
  if (commoninds.size()!=2) {
    cerr << "Trouble identifying image of a ridge (nb of indices containing verts: "<<commoninds.size()<<")" << endl;
    exit(1);
  }
  else {
    bool fd = false;
    int v=-1;
    while (!fd && v!=ridges.size()-1) {
      v++;
      fd = ( (ridges[v].faceindices[0]==commoninds[0] && ridges[v].faceindices[1]==commoninds[1]) || (ridges[v].faceindices[1]==commoninds[0] && ridges[v].faceindices[0]==commoninds[1]));
    }
    if (!fd) {
      cerr << "Trouble identifying image of a ridge." << endl;
      exit(1);
    }
    else {
      return v;
    }
  }
}

int Polytope::applyPairingToVertex(int jface, int jvertex) {
  bool fd = false;
  int u = -1;
  while (!fd && u!=vertexPairings.size()-1) {
    u++;
    fd = ( vertexPairings[u][0]==jvertex && vertexPairings[u][1]==jface );
  }
  if (fd) {
    return vertexPairings[u][2];
  }
  else {
    return -1;
  }
}

int Polytope::applyPairingToEdge(int jface, int j) {
  int jo = applyPairingToVertex(jface, edges[j].origin);
  int je = applyPairingToVertex(jface, edges[j].end);
  cout << "jo=" << jo << endl;
  cout << "je=" << je << endl;
  if (jo<0 || je<0) {
    cerr << "Trouble computing image of a 1-face (vertices are already a problem)!" << endl;
    exit(1);
  }
  else {
    bool fd = false;
    int u = -1;
    while (!fd && u!=edges.size()) {
      u++;
      fd = ( (edges[u].origin==jo && edges[u].end==je) || (edges[u].origin==je && edges[u].end==jo) ) ;
    } 
    if (!fd) {
      cerr << "Trouble computing image of a 1-face!" << endl;
      exit(1);
    }
    else {
      return u;
    }
  }
}

int Polytope::applyPairingToRidge(int jface, int j) {
  int nv = ridges[j].verts.size();
  vector<int> verteximages;
  for (int u=0; u<nv; u++) {
    int jo = applyPairingToVertex(jface, ridges[j].verts[u]);
    if (jo<0) {
      cerr << "Trouble computing image of a ridge!" << endl;
      exit(1);
    }
    else {
      verteximages.push_back(jo);
    }
  }
  vector<int> commoninds;
  for (int u=0; u<vertices[verteximages[0]].faceindices.size(); u++) {
    commoninds.push_back(vertices[verteximages[0]].faceindices[u]);
  }
  for (int u=1; u<verteximages.size(); u++) {
    commoninds = intersect(commoninds,vertices[verteximages[u]].faceindices);
  }
  if (commoninds[0]==-1) {
    commoninds.erase(commoninds.begin());
  }
  if (commoninds.size()!=2) {
    cerr << "Trouble identifying image of a ridge (nb of indices containing verts: "<<commoninds.size()<<")" << endl;
    exit(1);
  }
  else {
    bool fd = false;
    int v=-1;
    while (!fd && v!=ridges.size()-1) {
      v++;
      fd = ( (ridges[v].faceindices[0]==commoninds[0] && ridges[v].faceindices[1]==commoninds[1]) || (ridges[v].faceindices[1]==commoninds[0] && ridges[v].faceindices[0]==commoninds[1]));
    }
    if (!fd) {
      cerr << "Trouble identifying image of a ridge." << endl;
      exit(1);
    }
    else {
      return v;
    }
  }
}

void Polytope::checkCycles(){

  ofstream file;
  string filename = "pres/pres"+K.tag+".g";
  file.open(filename.c_str());
  file << "F:=FreeGroup(\"R1\",\"R2\",\"R3\",\"P\");" << endl;
  file << "R1:=F.1; R2:=F.2; R3:=F.3; P:=F.4;" << endl;
  stringstream ops;
  ops << orderofp;
  if (is_symmetric) {
    file << "G:=F/[P*R3*P^-1*R1^-1, P*R1*P^-1*R1*R2^-1*R1^-1, P^" << ops.str();
  }
  else {
    // don't know if the first relation always appears in cycles...
    file << "G:=F/[P^-1*R1*R2*R3,P^" << ops.str();
  }

  vector<string> rels;
  vector<int> ridgeorbitstocheck;
  for (int k=0; k<ridgePOrbits.size(); k++) {
    ridgeorbitstocheck.push_back(k);
  }


  while (ridgeorbitstocheck.size()>0) {
    if (verbose) {
      cout << "Ridge orbits to check: " << convertword(ridgeorbitstocheck) << endl;
      cout << "ridgePOrbits[ridgeorbitstocheck[0]]= " << ridgePOrbits[ridgeorbitstocheck[0]] << endl;
    }
    cout << "Computing cycle for ridge on faces " << ridges[ridgePOrbits[ridgeorbitstocheck[0]][0]] << endl;

    gen C;
    vector<int> cw;

    gen p0orbit = p0;
    // could probably replace p0 by some vector inside the polytope,
    //  and check whether it comes back inside the polytope too early
    
    int init_orbit_index = ridgeorbitstocheck[0];
    if (verbose) {
      cout << "Initial ridge orbit: " << init_orbit_index << endl;
    }
    
    int jstart = ridges[ridgePOrbits[init_orbit_index][0]].faceindices[0];
    int kstart = ridges[ridgePOrbits[init_orbit_index][0]].faceindices[1];
  
    gen mi0;
    bool iscomplex = isRidgeComplex(ridgePOrbits[init_orbit_index][0],mi0);
    gen Xt, angle_sum;
    angle_sum = K.zeronumber;
    if (iscomplex) {
      if (verbose) {
	cout << "mi0: " << mi0 << endl;
	cout << "genasrootof: " << K.genasrootof << endl;
      }
      
      cout << "Should compute angles!" << endl;
      Prism P1 = faces[jstart];
      Xt = BoxProd(mi0,P1.cspine); // should take apex projection? does this depend on sign?
      gen UU = P1.apexProjection;
      if (verbose) {
	cout << "Sq nm of apex projection: " << K.evali(SqNorm(UU)) << endl;
	cout << "Sq nm of cspine: " << K.evali(SqNorm(P1.cspine)) << endl;
      
	cout << "P1.cspine=" << P1.cspine << endl;
	cout << "Xt=" << Xt << endl;
      }
      gen angle_fraction = computeAngle(ridgePOrbits[init_orbit_index][0], Xt);
      if (verbose) {
	cout << "angle: 1/" << K.onenumber/angle_fraction << endl;
      }
      angle_sum = angle_sum + angle_fraction;
    }

    if (faces[jstart].invertpairing) {
      cw = invertword(faces[jstart].mirror.word);
    }
    else  {
      cw = faces[jstart].mirror.word;      
    }
    C = faces[jstart].pairing;
    Xt = C*Xt;
    K.vectred(Xt);
    p0orbit = C*p0orbit;
    K.vectred(p0orbit);
    int tp0 = testPoint(p0orbit);
    if (!tp0>0) {
      cout << "Orbit pt is inside polytope!" << endl;
    }

    if (K.areDep(p0orbit,p0)) {
      if (verbose) {
	cout << "C=" << C << endl;
	cout << "p0=" << p0 << endl;
      }
      gen te = K.inverse((*p0orbit._VECTptr)[1]);
      gen vt = te*p0orbit;
      K.vectred(vt);
      if (verbose) {
	cout << "p0orbit=" << vt << endl;
      }
      cout << "Got back to p0, word so far is " << convertword(cw) << endl;
      if (!isPPower(C)) {
	cout << "Cycle is not in <P>, seems there is no local tiling!" << endl;
	exit(1);
      }
    }
    
    int current_ridge = ridgePOrbits[init_orbit_index][0];

    // SHOULDN'T WE DO THIS?
    //    ridgeorbitstocheck.erase(remove(ridgeorbitstocheck.begin(),ridgeorbitstocheck.end(),init_orbit_index),ridgeorbitstocheck.end());

    cout << ridges[current_ridge].faceindices[0] << ", " << ridges[current_ridge].faceindices[1] << endl;
    int j = jstart;

    bool back = false; // probably not what we want?
    while (!back) {

      current_ridge = applyPairingToRidge(j,current_ridge);
      bool fd = false;
      int u = -1;
      int v;
      while (!fd && u!=ridgePOrbits.size()-1) {
	u++;
	v = -1;
	while (!fd && v!=ridgePOrbits[u].size()-1) {
	  v++;
	  fd = current_ridge == ridgePOrbits[u][v]; 
	}
      }
      if (!fd) {
	cerr << "Trouble finding image ridge in a ridge P-orbit" << endl;
	exit(1);
      }
      else {
	if (verbose) {
	  cout << "u=" << u << endl;
	  cout << "v=" << v << endl;
	}
	cout << ridges[current_ridge].faceindices[0] << ", " << ridges[current_ridge].faceindices[1] << endl;
	ridgeorbitstocheck.erase(remove(ridgeorbitstocheck.begin(),ridgeorbitstocheck.end(),u),ridgeorbitstocheck.end());
	if (ridges[current_ridge].faceindices[0]==facePairings[j]) {
	  j = ridges[current_ridge].faceindices[1];
	}
	else {
	  if (ridges[current_ridge].faceindices[1]==facePairings[j]) {
	    j = ridges[current_ridge].faceindices[0];
	  }
	  else {
	    cerr << "Trouble finding next pairing" << endl;
	    exit(1);
	  }
	}
	back = (u==init_orbit_index);
	if (!back) {
	  if (faces[j].invertpairing) {
	    cw = concat(invertword(faces[j].mirror.word),cw);
	  }
	  else  {
	    cw = concat(faces[j].mirror.word,cw);      
	  }
	  C = faces[j].pairing*C;
	  K.matred(C);
	  Xt = faces[j].pairing*Xt;
	  K.vectred(Xt);
	  p0orbit = faces[j].pairing*p0orbit;
	  K.vectred(p0orbit);
	  if (K.areDep(p0orbit,p0)) {
	    if (verbose) {
	      cout << "C=" << C << endl;
	      cout << "p0=" << p0 << endl;
	    }
	    gen te = K.inverse((*p0orbit._VECTptr)[1]);
	    gen vt = te*p0orbit;
	    K.vectred(vt);
	    if (verbose) {
	      cout << "p0orbit=" << vt << endl;
	      cout << "Got back to p0, word so far is " << convertword(cw) << endl;
	    }
	    if (!isPPower(C)) {
	      cout << "Cycle is not in <P>, seems there is no local tiling!" << endl;
	      exit(1);
	    }
	  }
	  if (iscomplex) {
	    cout << "Suspicious place" << endl;
	    gen angle_fraction = computeAngle(current_ridge, Xt);
	    cout << "angle: 1/" << K.onenumber/angle_fraction << endl;
	    angle_sum = angle_sum + angle_fraction;
	  }
	}
	else {
	  vector<int> power_of_p;
	  if (2*v<orderofp) {
	    for (int k=0; k<v; k++) {
	      power_of_p.push_back(-4);
	      C = Pi*C;
	      K.matred(C);
	      Xt = Pi*Xt;
	      K.vectred(Xt);

	      p0orbit = Pi*p0orbit;
	      K.vectred(p0orbit);
	      if (K.areDep(p0orbit,p0)) {
		if (verbose) {
		  cout << "C=" << C << endl;
		  cout << "p0=" << p0 << endl;
		}
		gen te = K.inverse((*p0orbit._VECTptr)[1]);
		gen vt = te*p0orbit;
		K.vectred(vt);
		if (verbose) {
		  cout << "p0orbit=" << vt << endl;
		}
		
		cout << "Got back to p0";
		if (!isPPower(C)) {
		  cout << "Cycle is not in <P>, seems there is no local tiling!" << endl;
		  exit(1);
		}
	      }

	    }
	  }
	  else {
	    int v2 = orderofp-v;
	    for (int k=0; k<v2; k++) {
	      power_of_p.push_back(4);
	      C = P*C;
	      K.matred(C);
	      Xt = P*Xt;
	      K.vectred(Xt);

	      p0orbit = P*p0orbit;
	      K.vectred(p0orbit);
	      if (K.areDep(p0orbit,p0)) {
		if (verbose) {
		  cout << "C=" << C << endl;
		  cout << "p0=" << p0 << endl;
		}
		gen te = K.inverse((*p0orbit._VECTptr)[1]);
		gen vt = te*p0orbit;
		K.vectred(vt);
		if (verbose) {
		  cout << "p0orbit=" << vt << endl;
		  cout << "Got back to p0" << endl;
		}
		if (!isPPower(C)) {
		  cout << ", but cycle is not in <P>, there is no local tiling!" << endl;
		  cout << "The shell may still work, but the symmetry group is larger than just the cyclic <P>" << endl;
		  exit(1);
		}
	      }
	    }
	  }
	  cw = concat(power_of_p,cw);	  
	  if (verbose) {
	    cout << ", word so far is " << convertword(cw) << endl;
	  }
	}
      }
    }
    if (!back) {
      cerr << "Trouble with some ridge cycle" << endl;
      exit(1);
    }
    else {

      cout << "Got back to the same ridge, word so far is " << convertword(cw) << endl;

      // start by checking if p0 goes back to itself under powers of C..
      bool p0back = false;
      gen p0copy = p0;
      
      gen Ctest = K.Id;

      int pow0 = 0;
      while (!p0back && pow0<100) {

	Ctest = Ctest*C;
	K.matred(Ctest);

	p0copy = C*p0copy;
	K.vectred(p0copy);
	p0back = K.areDep(p0,p0copy);

	pow0++;

      }
      if (!p0back) {
	cerr << "Did not find a small power of cycle tsf that's the identity (tried powers up to 100)" << endl;
	exit(1);
      }
      else {
	cout << "Power " << pow0 << " brings p0 back" << endl;
	if (!isPPower(Ctest)) {
	  cout << "Some cycle tsf fixes p0, but it is not a power of P, Poincare may fail!" << endl;
	  exit(1);
	}
	else {
	  cout << "Corresponding power is in <P>" << endl;
	}
      }      
      
      bool isid = false;
      gen tv_start1 = vertices[ridges[ridgePOrbits[init_orbit_index][0]].verts[0]].coords;
      gen tv_start2 = vertices[ridges[ridgePOrbits[init_orbit_index][0]].verts[1]].coords;

      gen tv1 = tv_start1;
      gen tv2 = tv_start2;
      int pow = 0;
      vector<int> cwp;
      gen Cp = K.Id;

      while (!isid && pow<100) {

	cwp = concat(cwp,cw);
	Cp = Cp*C;
	K.matred(Cp);

	tv1 = C*tv1;
	K.vectred(tv1);
	tv2 = C*tv2;
	K.vectred(tv2);
	isid = K.areDep(tv1,tv_start1) && K.areDep(tv2,tv_start2); // If two points are fixed, sure to be the identity on the ridge (?)

	pow++;

      }
      if (!isid) {
	cerr << "Did not find a small power of cycle tsf that's the identity on the ridge (tried powers up to 100)" << endl;
	exit(1);
      }
      else {

	if (verbose) {
	  cout << "C=" << C << endl;
	  cout << "Order of C: " << K.order(C) << endl;

	  cout << "Cp=" << Cp << endl;
	  cout << "Order of Cp: " << K.order(Cp) << endl;
	}
	
	int o = K.order(Cp);
	int op = o*pow;

	cout << "(" << convertword(cw) << ")^" << pow << "=id on the ridge" << endl; // not sure we care about that
	if (op>2) {
	  cout << "(" << convertword(cw) << ")^" << op << "=id (need to check angles)" << endl;
	}
	else {
	  cout << "(" << convertword(cw) << ")^" << op << "=id" << endl;
	}

	if (iscomplex) {

	  if (verbose) {
	    cout << "Complex line" << endl;
	  }

	  /*
	   * In some cases, there may be two angles to check:
	   *   one always needs to compare 
	   *        - the rotation angle of C^p in the direction orthogonal to the ridge
	   *        - the angle between the faces
	   *   if the isolated fixed point is a vertex, also need to compare
	   *        - the angle between the edges in the ridge adjacent to that vertex
	   *        - the rotation angle in the direction 
	   * So far, the second one was NOT implemented...
	   */

	  // ALSO NEED TO INCLUDE COMMUTATION RELATION, BETWEEN CYCLE TSF AND Ppower, IF RELEVANT!

	  int oc = K.order(C);
	  if (oc<3) {
	    if (oc==2) {
	      cout << "Order 2, nothing to check for local tiling" << endl;
	    }
	  }
	  else {

	    // The following will not work when C is a complex reflection!
	    //  should start by checking if that is the case or not (to first approx, this is pow=1?)

	    if (pow==1) {

	      gen XtImage = C*Xt;
	      K.vectred(XtImage);
	      if (!K.areDep(Xt,XtImage)) {
		cerr << "Xt=" << Xt << " is not fixed, but pow=1, this makes no sense" << endl;
		exit(1);
	      }
	      else {

		// compare rotation angle with angle between the bisectors?
		//  one positive eigenvector is the polar to the ridge
		gen miImage = C*mi0;
		K.vectred(miImage);
		if (!K.areDep(mi0,miImage)) {
		  cerr << "Mirror perp is not fixed, oops!" << endl;
		  exit(1);
		}
		else {

		  // want to compute the rotation angle from mi0 and Xt, it is given by the ratio of their eigenvalues...
		  int mik;
		  bool fd = false;

		  int ik = -1;
		  while (ik!=2 && !fd) {
		    ik++;
		    fd = !operator_equal(mi0[ik],K.zeronumber,&ct);
		  }
		  if (!fd) {
		    cerr << "This vector should not be zero!" << endl;
		    exit(1);
		  }

		  gen ev2 = miImage[ik]*K.inverse(mi0[ik]);
		  K.red(ev2);

		  if (verbose) {
		    cout << "This should be negative: "<<K.evali(SqNorm(Xt))<<endl;
		  }
		  
		  int Xk;
		  fd = false;
		  ik = -1;
		  while (ik!=2 && !fd) {
		    ik++;
		    fd = !operator_equal(Xt[ik],K.zeronumber,&ct);
		  }
		  if (!fd) {
		    cerr << "This vector should not be zero!" << endl;
		    exit(1);
		  }
		  gen ev1 = XtImage[ik]*K.inverse(Xt[ik]);
		  K.red(ev1);

		  gen an = ev2*K.inverse(ev1);
		  K.red(an);
		  
		  if (verbose) {
		    cout << "an=" << an << endl;
		    cout << "genasrootof=" << K.genasrootof << endl;
		  }
		  
		  gen anro = eval(_subst(makesequence(an,x__IDNT_e,K.genasrootof),&ct),&ct);

		  if (verbose) {
		    cout << "pmin(anro): " << _pmin(anro,&ct) << endl;
		    cout << "Rotation angle of cx refl is arg of " << anro << endl;
		  }
		 
		  if (operator_equal(anro,K.onenumber,&ct)) {
		    cout << "Rotation by angle 0 (this should not happen!?)" << endl;
		  }
		  else {
		    if (operator_equal(anro,gen("-1",&ct),&ct)) {
		      cout << "Rotation by pi (for complex refl)" << endl;
		    }
		    else {
		      gen po = _simplify(_pmin(anro,&ct) - ipmin,&ct);
			  
		      if (verbose) {
			cout << "po=" << po << endl;
		      }			     
		      if (is_zero(po,&ct)) {
			if (is_greater(_evalf(im(anro,&ct),&ct),K.zeronumber,&ct)) {
			  cout << "Rotation by pi/2 (for complex refl)" << endl;
			}
			else {
			  if (is_greater(K.zeronumber,_evalf(im(anro,&ct),&ct),&ct)) {
			    cout << "Rotation by -pi/2 (for complex refl)" << endl;
			  }
			}
		      }
		      else {			  
			
			if (verbose) {
			  cout << "anro=" << anro << endl;
			  cout << _evalf(anro,&ct) << endl;
			}
			gen te0 = _simplify(_eval(anro,&ct),&ct);
			if (verbose) {
			  cout << "te0=" << te0 << endl;
			  cout << "convert te0 interval: " << convert_interval(te0,K.nd,&ct) << endl;
			  cout << "arg/2pi: " << arg(convert_interval(te0,K.nd,&ct),&ct)/(2*gen("pi",&ct)) << endl;
			}
			
			gen te = K.onenumber/re(arg(convert_interval(eval(te0,1,&ct),K.nd,&ct),&ct)/(2*gen("pi",&ct)),&ct);
			
			gen width = _right(te,&ct) - _left(te,&ct);
			if (is_greater(width,K.onenumber,&ct)) {
			  cerr << "Ambiguous angle computations, interval contains several integers!" << endl;
			}
			else {
			  if (verbose) {
			    cout << "te=" << te << endl;
			    cout << "(will take right, then floor)" << endl;
			  }
			  gen i = _floor(_right(te,&ct),&ct);
			  if (verbose) {
			    cout << "i=" << i << endl;
			  }
			  if (!is_greater(i,_left(te,&ct),&ct)) {
			    cerr << "The angle interval contains no inverse of an integer, perhaps the integrality condition fails!" << endl;	
			    cout << "The angle interval contains no inverse of an integer, perhaps the integrality condition fails!" << endl;
			    exit(1);
			  }
			  else {
			    cout << "Rotation by 2*pi/" << i << "   (for complex refl)" << endl;
			  }
			}
			//			  }
			//		}
		      }
		    }
		  }		  
		}
	      }
	    }
	    else {

	      /*	      gen Hv = normal(_subst(makesequence(H,x__IDNT_e,K.genasrootof),&ct),&ct);
	      if (verbose) {
		cout << "H=" << Hv << endl;
	      }

	      gen Ct = normal(_subst(makesequence(C,x__IDNT_e,K.genasrootof),&ct),&ct);
	      if (verbose) {
		cout << "Ct=" << Ct << endl;
		}*/

	      // A priori, the following will only work if Ct has distinct eigenvalues
	      //   (could easily be fixed, it should be easier if it is a complex reflection)

	      bool success = false;
	      int count = 0;
	      int savend = K.nd;
	      
	      while (!success && count<10) {

		vecteur U,evs;
		int ct_it = 0;
		int loc = K.linearorder(C);

		if (my_jordan_knowing_linear_order_alt(C,loc,U,evs)) {

		  // In new version, the rational numbers encoding angles are already computed, they are in the vecteur "evs"
		  //    while interval versions of the corresponding eigenvectors are in the vecteur "U"
		  //    note that U[0] is the negative vector
		  // Still need to compare U[1] and U[2] with the mirror perp
		  //    the angle inside mirror is given by the eigenvalues for the mirror perp
		  //    the angle transverse to the mirror is the other one

		  bool ambiguous = false;

		  if (verbose) {
		    cout << "xvi=" << K.xvi << endl;
		  }
		  
		  gen Vi = conj(convert_interval(_subst(makesequence(mi0,x__IDNT_e,K.xvi),&ct),K.nd,&ct),&ct);
		  // I don't know if mi0 is correct at this stage, hopefully this is the mirror perp for the ridge
		  if (verbose) {
		    cout << "mi0 num: " << Vi << endl;
		  }
		  
		  gen Hi = convert_interval(_subst(makesequence(H,x__IDNT_e,K.xvi),&ct),K.nd,&ct);
		  if (verbose) {
		    cout << "Hi=" << Hi << endl;
		  }
		  
		  gen ip1 = Vi*Hi*U[1];
		  gen n1 = re(ip1,&ct)*re(ip1,&ct) + im(ip1,&ct)*im(ip1,&ct);
		  gen ip2 = Vi*Hi*U[2];
		  gen n2 = re(ip2,&ct)*re(ip2,&ct) + im(ip2,&ct)*im(ip2,&ct);
		  if (verbose) {
		    cout << "n1=" << n1 << endl;
		    cout << "n2=" << n2 << endl;
		  }
		  // One (and only one) of these should be zero, the other one strictly positive
		  gen longitudinal_angle, transversal_angle;
		  
		  bool test1 = (is_greater(_left(n1,&ct),K.prec,&ct) && is_greater(K.zeronumber,_left(n2,&ct),&ct));
		  // test1 means n1 can be zero
		  
		  bool test2 = (is_greater(_left(n2,&ct),K.prec,&ct) && is_greater(K.zeronumber,_left(n1,&ct),&ct));
		  // test1 means n2 can be zero
		  
		  if(verbose) {
		    cout << "evs= " << evs << endl;
		  }
	      
		  if (test1) {
		    if (!test2) {
		      // U[1] is Vi (?)

		      gen al = evs[2]-evs[0];
		      if (verbose) {
			cout << "al=" << al << endl;
		      }
		      int aln = _numer(al,&ct).val;
		      int ald = _denom(al,&ct).val;
		      if (verbose) {
			cout << "(" << aln << "/" << ald << ")" << endl;
		      }
		      if (2*ald<-aln) { // do we want to allow negative angles?
			aln = aln+ald;
		      }
		      if (verbose) {
			cout << " after modif (" << aln << "/" << ald << ")" << endl;
		      }
		      longitudinal_angle = aln*K.onenumber/ald;
		      
		      gen tl = evs[1]-evs[0];
		      if (verbose) {
			cout << "tl=" << tl << endl;
		      }
		      int tln = _numer(tl,&ct).val;
		      int tld = _denom(tl,&ct).val;
		      if (verbose) {
			cout << "(" << tln << "/" << tld << ")" << endl;
		      }
		      if (2*tld<-tln) {
			tln = tln+tld;
		      }
		      if (verbose) {
			cout << " after modif (" << tln << "/" << tld << ")" << endl;
		      }
		      transversal_angle = tln*K.onenumber/tld;
		      
		    }
		    else {
		      cout << "Interval computations are ambiguous, you may want to try using better precision" << endl;
		      ambiguous = true;
		    }
		  }
		  else {
		    if (test2) {
		      // U[2] is Vi
		      
		      gen al = evs[1]-evs[0];
		      if (verbose){
			cout << "al=" << al << endl;
		      }
		      int aln = _numer(al,&ct).val;
		      int ald = _denom(al,&ct).val;
		      if (verbose) {
			cout << "(" << aln << "/" << ald << ")" << endl;
		      }
		      if (2*ald<-aln) { // do we want to allow negative angles?
			aln = aln+ald;
		      }
		      if (verbose){
			cout << " after modif (" << aln << "/" << ald << ")" << endl;
		      }
		      longitudinal_angle = aln*K.onenumber/ald;
		      
		      gen tl = evs[2]-evs[0];
		      if (verbose) {
			cout << "tl=" << tl << endl;
		      }
		      int tln = _numer(tl,&ct).val;
		      int tld = _denom(tl,&ct).val;
		      if (verbose) {
			cout << "(" << tln << "/" << tld << ")" << endl;
		      }
		      if (2*tld<-tln) {
			tln = tln+tld;
		      }
		      if (verbose) {
			cout << " after modif (" << tln << "/" << tld << ")" << endl;
		      }
		      transversal_angle = tln*K.onenumber/tld;
		      
		    }
		    else {
		      cout << "Interval computations are ambiguous, you may want to try using better precision" << endl;
		      ambiguous = true;
		    }
		  }

		  if (!ambiguous) {

		    success = true;
		    
		    cout << "Longitudinal angle 2*pi*(" << longitudinal_angle << ")" << endl;
		    cout << "Transversal angle 2*pi*(" << transversal_angle << ")" << endl;

		    gen tapow = pow*transversal_angle;
		    if (verbose) {
		      cout << "tapow=" << tapow << endl;
		    }
		    int tapow_num = _abs(_numer(tapow,&ct),&ct).val;
		    int tapow_den = _denom(tapow,&ct).val;
		    if (verbose) {
		      cout << "(" << tapow_num << "/" << tapow_den << ")" << endl;
		    }
	      
		    if (tapow_num!=1) {
		      cout << "Numerator is not equal to one, integrality condition fails" << endl;
		      exit(1);
		    }
		    else {
		      if (tapow_den!=o) {
			cout << "The angle is not consistent with the order of the cycle transformation" << endl;
			cout << " Integrality condition fails" << endl;
			exit(1);
		      }
		      else {
			cout << "Integrality condition holds on this ridge!" << endl;
		      }
		    }

		    if (K.nd>savend) {
		      K.nd = savend;
		      K.lowerPrecision();
		    }
		  }
		}
		if (!success) {
		  cout << "Found ambiguity in interval computations (of angles of cycle tsf), will now increase precision and try again" << endl;
		  cout << " count=" << count << endl;
		  K.increasePrecision();
		}
		count++;
	      }
	      if (!success) {
		cout << "Found ambiguity in interval computations even after significantly increasing precision" << endl;
		cout << " I will stop now, sorry" << endl;
		exit(1);
	      }
	    }
	  }
	}


	
	string rel;
	if (op==1) {
	  rel = convertwordgap(cw);
	  bool isnew = true;
	  int uu = 0;
	  while (isnew && uu<rels.size()) {
	    isnew = (rel!=rels[uu]);
	    uu++;
	  }
	  if (isnew) {
	    rels.push_back(rel);
	  }
	}
	else {
	  stringstream powstring;
	  powstring << op;
	  rel = "(" + convertwordgap(cw) + ")^" + powstring.str();	
	  bool isnew = true;
	  int uu = 0;
	  while (isnew && uu<rels.size()) {
	    isnew = (rel!=rels[uu]);
	    uu++;
	  }
	  if (isnew) {
	    rels.push_back(rel);
	  }
	}
	gen D = Cp;
	for (int u=1; u<o; u++) {
	  D = D*Cp;
	  K.matred(D);
	}
	if (K.isScalar(D)) {
	  cout << "Check matrix OK" << endl;
	}
	else {
	  cerr << "Problem with cycle tsf, found a non-scalar matrix!" << endl;
	  exit(1);
	}

	// may need to include a commutation relation?
	// * check id ridge is preserved by power of P
	// * if so, check whether this power commutes with C
	// *     (if it doesn't, QUIT, at least for now)
	int j0 = ridgePOrbits[init_orbit_index][0];
	int j1 = applyPToRidge(orderofppower,j0);
	bool testInvariance = (j0==j1);
	if (testInvariance) {
	  cout << "Ridge is invariant by P^" << orderofppower << endl;
	  if (verbose) {
	    cout << "C=" << C << endl;
	    cout << "Ppower=" << Ppower << endl;
	    cout << "C^-1=" << K.matinverse(C) << endl;
	    cout << "Ppower^-1=" << K.matinverse(Ppower) << endl;
	  }
	  gen M = C*Ppower*K.matinverse(C)*K.matinverse(Ppower);
	  K.matred(M);
	  if (K.isScalar(M)) {
	    stringstream powstring1;
	    powstring1 << orderofppower;
	    vector<int> cwi = invertword(cw);
	    string commrel = "P^" + powstring1.str() + "*" + convertwordgap(cw) + "*P^-" + powstring1.str() + "*" + convertwordgap(cwi);	
	    bool isnew = true;
	    int uu = 0;
	    while (isnew && uu<rels.size()) {
	      isnew = (commrel!=rels[uu]);
	      uu++;
	    }
	    if (isnew) {
	      rels.push_back(commrel);
	    }
	    cout << "Adding commutation relation with power of P: " << commrel << endl;
	  }
	  else {
	    cerr << "P power does not commute with cycle tsf, don't know what to do" << endl;
	    exit(1);
	  }
	}

	cout << "" << endl;
      }
    }    
  }
  for (int uu=0; uu<rels.size(); uu++) {
    file << "," << rels[uu];
  }
  file << "];" << endl;
  file.close();
  cout << "Wrote the presentation in GAP style in the file: " << filename << endl;

}

void Polytope::drawFace(int j) {

  ofstream file;
  stringstream ss;
  ss << j;
  string filename = "pics/pic"+K.tag+"_face"+ss.str()+".asy";
  file.open(filename.c_str());
  file << "import graph3;" << endl;
  file << "import three;" << endl;
  file << "size(7.5cm,0);" << endl;
  file << "pen p=black+1;" << endl;
  file << "draw(unithemisphere,white+opacity(0.1));" << endl;

  gen Hn = K.matevaln(H);
  gen V0 = faces[j].apexProjection;
  gen V1 = Proj(faces[j].apex.coords,faces[j].apexProjection);
  gen V2 = faces[j].cspine;
  
  gen W0 = K.vectevaln(V0) / sqrt(re(-K.evaln(Inn(V0,V0)),&ct),&ct);
  gen W1 = K.vectevaln(V1) / sqrt(re(K.evaln(Inn(V1,V1)),&ct),&ct);
  gen W2 = K.vectevaln(V2) / sqrt(re(K.evaln(Inn(V2,V2)),&ct),&ct);

  vecteur te;
  te.push_back(W0);
  te.push_back(W1);
  te.push_back(W2);
  gen A = _tran(gen(te),&ct);

  gen Ai = _inverse(A,&ct);
 
  
  int k1 = 0;
  int l1 = faces[j].sides[0].word.size();
  for (int k=1; k<faces[j].sides.size(); k++) {
    int l = faces[j].sides[k].word.size();
    if (l<l1) {
      l1 = l;
      k1 = k;
    }
  }
  int k2;
  if (k1==0) {
    if (faces[j].sides[faces[j].sides.size()-1].word.size()<faces[j].sides[1].word.size()) {
      k2 = faces[j].sides.size()-1;
    }
    else {
      k2 = 1;
    }
  }
  else {
    k2 = (k1+1)%faces[j].sides.size();
  }
  gen zo,to,zt,tt;

  gen U = Ai*K.vectevaln(faces[j].bottomface[k1].coords);
  gen zu = U[2]/U[0];
  gen tu = re(U[1]/U[0],&ct);
  gen V = Ai*K.vectevaln(faces[j].bottomface[k2].coords);
  gen zv = V[2]/V[0];
  gen tv = re(V[1]/V[0],&ct);
  zo = (zu+zv)/2;
  to = (tu+tv)/2;
  int n = faces[j].sides.size();
  if (n%2==0) {
    int k3 = (k1 + n/2)%n;
    int k4 = (k2 + n/2)%n;
    U = Ai*K.vectevaln(faces[j].bottomface[k3].coords);
    zu = U[2]/U[0];
    tu = re(U[1]/U[0],&ct);
    V = Ai*K.vectevaln(faces[j].bottomface[k4].coords);
    zv = V[2]/V[0];
    tv = re(V[1]/V[0],&ct);
    zt = (zu+zv)/2;
    tt = (tu+tv)/2;
  }
  else {
    int k3;
    if (k2>k1) {
      k3 = (k2 + n/2)%n;
    }
    else {
      k3 = (k1 + n/2)%n;
    }
    gen U = Ai*K.vectevaln(faces[j].bottomface[k3].coords);
    zt = U[2]/U[0];
    tt = re(U[1]/U[0],&ct);
  }

  gen Xp = Ai*K.vectevaln(faces[j].topface[0].coords);
  gen top_level = re(Xp[1]/Xp[0],&ct);
  gen zk = zo-zt;
  zk = zk/abs(zk,&ct);  
  file << "currentprojection=orthographic((" << re(zk,&ct) << "," << im(zk,&ct) << "," << 0.3 << "));" << endl;

  string norm = "(0,0,1)";
  string ccw = "false";
  gen z1,z2,den,X;
  if (faces[j].topface.size()>1) {
    vecteur top_verts;
    gen X = Ai*K.vectevaln(faces[j].topface[0].coords);
    gen top_rv = K.onenumber - top_level*top_level;;
    top_verts.push_back(X[2]/X[0]);
    for (int k=1; k<faces[j].topface.size(); k++) {
      X = Ai*K.vectevaln(faces[j].topface[k].coords);
      top_verts.push_back(X[2]/X[0]);
    }
    for (int k=0; k<top_verts.size()-1; k++) {
      z1 = top_verts[k];
      z2 = top_verts[k+1];
      den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
      if (is_greater(1e-4,abs(den,&ct),&ct)) {
	// draw straight line
	file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),p);" << endl;
      }
      else {
	gen ctr = (top_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
	gen xc = re(ctr,&ct);
	gen yc = im(ctr,&ct);
	file << "draw(Arc(("<<xc<<","<<yc<<","<<top_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
      }
    }
    z1 = top_verts[top_verts.size()-1];
    z2 = top_verts[0];
    den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
    if (is_greater(1e-4,abs(den,&ct),&ct) && is_greater(abs(z1-z2,&ct),1e-4,&ct)) {

      // draw straight line
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),p);" << endl;

    }
    else {
      gen ctr = (top_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
      gen xc = re(ctr,&ct);
      gen yc = im(ctr,&ct);
      file << "draw(Arc(("<<xc<<","<<yc<<","<<top_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
    }
  }

  for (int k=0; k<faces[j].midvertices.size(); k++) {
    X = Ai*K.vectevaln(faces[j].midvertices[k].coords);
  }

  vecteur bottom_verts;
  X = Ai*K.vectevaln(faces[j].bottomface[0].coords);
  gen bottom_level = re(X[1]/X[0],&ct);
  gen bottom_rv = K.onenumber - bottom_level*bottom_level;
  bottom_verts.push_back(X[2]/X[0]);
  for (int k=1; k<faces[j].bottomface.size(); k++) {
    X = Ai*K.vectevaln(faces[j].bottomface[k].coords);
    bottom_verts.push_back(X[2]/X[0]);
  }
  for (int k=0; k<bottom_verts.size()-1; k++) {
    z1 = bottom_verts[k];
    z2 = bottom_verts[k+1];
    den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
    if (is_greater(1e-4,abs(den,&ct),&ct)) {

      // draw straight line
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),p);" << endl;
      vector<int> w = faces[j].mirror.word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z1+z2)/2;
	file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << bottom_level << "));" << endl;
      }

    }
    else {
      gen ctr = (bottom_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
      gen xc = re(ctr,&ct);
      gen yc = im(ctr,&ct);

      file << "draw(Arc(("<<xc<<","<<yc<<","<<bottom_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
      
      vector<int> w = faces[j].mirror.word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z1+z2)/2;
	gen zma = ctr + (zm-ctr)*abs(z1-ctr,&ct)/abs(zm-ctr,&ct);
	file << "$\",(" << re(zma,&ct) << "," << im(zma,&ct) << "," << bottom_level << "));" << endl;
      }
    }
  }
  z1 = bottom_verts[bottom_verts.size()-1];
  z2 = bottom_verts[0];
  den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
  if (is_greater(1e-4,abs(den,&ct),&ct)) {

    // draw straight line
    file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),p);" << endl;
    vector<int> w = faces[j].mirror.word;
    if (w.size()<10) {
      file << "label(\"$";
      for (int u=0; u<w.size(); u++) {
	if (w[u]<0) {
	  file << "\\bar" << -w[u];
	}
	else {
	  file << w[u];
	}
      }
      gen zm = (z1+z2)/2;
      file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << bottom_level << "));" << endl;
    }

  }
  else {
    gen ctr = (bottom_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
    gen xc = re(ctr,&ct);
    gen yc = im(ctr,&ct);

    file << "draw(Arc(("<<xc<<","<<yc<<","<<bottom_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
    vector<int> w = faces[j].mirror.word;
    if (w.size()<10) {
      file << "label(\"$";
      for (int u=0; u<w.size(); u++) {
	if (w[u]<0) {
	  file << "\\bar" << -w[u];
	}
	else {
	  file << w[u];
	}
      }
      gen zm = (z1+z2)/2;
      gen zma = ctr + (zm-ctr)*abs(z1-ctr,&ct)/abs(zm-ctr,&ct);
      file << "$\",(" << re(zma,&ct) << "," << im(zma,&ct) << "," << bottom_level << "));" << endl;
    }
  }

  // then draw vertical edges...  
  if (faces[j].topface.size()>1) {
    for (int l=0; l<faces[j].bottomface.size(); l++) {
      if (find(faces[j].midvertindices.begin(),faces[j].midvertindices.end(),l)==faces[j].midvertindices.end()) {
	// draw straight line from tf[l] to bf[l]
	gen X1 = Ai*K.vectevaln(faces[j].topface[l].coords);
	gen z1 = X1[2]/X1[0];
	gen t1 = re(X1[1]/X1[0],&ct);
	gen X2 = Ai*K.vectevaln(faces[j].bottomface[l].coords);
	gen z2 = X2[2]/X2[0];
	gen t2 = re(X2[1]/X2[0],&ct);
	file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
	vector<int> w = faces[j].sides[l].word;
	if (w.size()<10) {
	  file << "label(\"$";
	  for (int u=0; u<w.size(); u++) {
	    if (w[u]<0) {
	      file << "\\bar" << -w[u];
	    }
	    else {
	      file << w[u];
	    }
	  }
	  gen zm = (z1+z2)/2;
	  gen tm = (t1+t2)/2;
	  file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
	}
      }
    }
  }
  else {
    for (int l=0; l<faces[j].bottomface.size(); l++) {
      if (find(faces[j].midvertindices.begin(),faces[j].midvertindices.end(),l)==faces[j].midvertindices.end()) {
	// draw straight line from bf[l] to tf[0]
	gen X1 = Ai*K.vectevaln(faces[j].topface[0].coords);
	gen z1 = X1[2]/X1[0];
	gen t1 = re(X1[1]/X1[0],&ct);
	gen X2 = Ai*K.vectevaln(faces[j].bottomface[l].coords);
	gen z2 = X2[2]/X2[0];
	gen t2 = re(X2[1]/X2[0],&ct);
	file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
	vector<int> w = faces[j].sides[l].word;
	if (w.size()<10) {
	  file << "label(\"$";
	  for (int u=0; u<w.size(); u++) {
	    if (w[u]<0) {
	      file << "\\bar" << -w[u];
	    }
	    else {
	      file << w[u];
	  }
	  }
	  gen zm = (z1+z2)/2;
	  gen tm = (t1+t2)/2;
	  file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
	}
      }
    }
  }
  
  for (int l=0; l<faces[j].midvertices.size(); l++) {
    if (faces[j].topface.size()>1) {
      
      gen X1 = Ai*K.vectevaln(faces[j].bottomface[faces[j].midvertindices[l]].coords);
      gen z1 = X1[2]/X1[0];
      gen t1 = re(X1[1]/X1[0],&ct);
      
      gen X2 = Ai*K.vectevaln(faces[j].midvertices[l].coords);
      gen z2 = X2[2]/X2[0];
      gen t2 = re(X2[1]/X2[0],&ct);
      
      gen X3 = Ai*K.vectevaln(faces[j].topface[faces[j].midvertindices[l]].coords);
      gen z3 = X3[2]/X3[0];
      gen t3 = re(X3[1]/X3[0],&ct);
      
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
      file << "draw(("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<")--("<<re(z3,&ct)<<","<<im(z3,&ct)<<","<<t3<<"),p);" << endl;	

      vector<int> w = faces[j].sides[faces[j].midvertindices[l]].word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z2+z3)/2;
	gen tm = (t2+t3)/2;
	file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
      } 
    }
    else {

      gen X1 = Ai*K.vectevaln(faces[j].bottomface[faces[j].midvertindices[l]].coords);
      gen z1 = X1[2]/X1[0];
      gen t1 = re(X1[1]/X1[0],&ct);
      
      gen X2 = Ai*K.vectevaln(faces[j].midvertices[l].coords);
      gen z2 = X2[2]/X2[0];
      gen t2 = re(X2[1]/X2[0],&ct);
      
      gen X3 = Ai*K.vectevaln(faces[j].topface[0].coords);
      gen z3 = X3[2]/X3[0];
      gen t3 = re(X3[1]/X3[0],&ct);
      
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
      file << "draw(("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<")--("<<re(z3,&ct)<<","<<im(z3,&ct)<<","<<t3<<"),p);" << endl;	

      vector<int> w = faces[j].sides[faces[j].midvertindices[l]].word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z2+z3)/2;
	gen tm = (t2+t3)/2;
	file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
      }
    }
    
  }

  file << "pen dp = blue+4;"<< endl;
  file << "pen dpi = red+4;"<< endl;
  for (int k=0; k<faces[j].topface.size(); k++) {
    gen X = Ai*K.vectevaln(faces[j].topface[k].coords);
    gen z = X[2]/X[0];
    gen t = re(X[1]/X[0],&ct);

    gen te = SqNorm(faces[j].topface[k].coords);
    if (operator_equal(te,K.zeronumber,&ct)) {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dpi);" << endl;
    }
    else {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dp);" << endl;
    }

  }
  for (int k=0; k<faces[j].bottomface.size(); k++) {
    gen X = Ai*K.vectevaln(faces[j].bottomface[k].coords);
    gen z = X[2]/X[0];
    gen t = re(X[1]/X[0],&ct);

    gen te = SqNorm(faces[j].bottomface[k].coords);
    if (operator_equal(te,K.zeronumber,&ct)) {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dpi);" << endl;
    }
    else {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dp);" << endl;
    }

  }
  for (int k=0; k<faces[j].midvertices.size(); k++) {
    gen X = Ai*K.vectevaln(faces[j].midvertices[k].coords);
    gen z = X[2]/X[0];
    gen t = re(X[1]/X[0],&ct);

    gen te = SqNorm(faces[j].midvertices[k].coords);
    if (operator_equal(te,K.zeronumber,&ct)) {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dpi);" << endl;
    }
    else {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dp);" << endl;
    }

  }


  file.close();

}

void Polytope::drawFaceSector(int j, int kv1, int kv2) {
  // draw face j, marking sector between kv1 and kv2

  ofstream file;
  stringstream ss;
  ss << j;
  string filename = "pics/pic"+K.tag+"_face"+ss.str()+"_alt.asy";
  file.open(filename.c_str());
  file << "import graph3;" << endl;
  file << "import three;" << endl;
  file << "size(7.5cm,0);" << endl;
  file << "pen p=black+1;" << endl;
  file << "draw(unithemisphere,white+opacity(0.1));" << endl;

  gen Hn = K.matevaln(H);
  gen V0 = faces[j].apexProjection;
  gen V1 = Proj(faces[j].apex.coords,faces[j].apexProjection);
  gen V2 = faces[j].cspine;
  
  gen W0 = K.vectevaln(V0) / sqrt(re(-K.evaln(Inn(V0,V0)),&ct),&ct);
  gen W1 = K.vectevaln(V1) / sqrt(re(K.evaln(Inn(V1,V1)),&ct),&ct);
  gen W2 = K.vectevaln(V2) / sqrt(re(K.evaln(Inn(V2,V2)),&ct),&ct);

  vecteur te;
  te.push_back(W0);
  te.push_back(W1);
  te.push_back(W2);
  gen A = _tran(gen(te),&ct);

  gen Ai = _inverse(A,&ct);
 
  
  int k1 = 0;
  int l1 = faces[j].sides[0].word.size();
  for (int k=1; k<faces[j].sides.size(); k++) {
    int l = faces[j].sides[k].word.size();
    if (l<l1) {
      l1 = l;
      k1 = k;
    }
  }
  int k2;
  if (k1==0) {
    if (faces[j].sides[faces[j].sides.size()-1].word.size()<faces[j].sides[1].word.size()) {
      k2 = faces[j].sides.size()-1;
    }
    else {
      k2 = 1;
    }
  }
  else {
    k2 = (k1+1)%faces[j].sides.size();
  }
  gen zo,to,zt,tt;

  gen U = Ai*K.vectevaln(faces[j].bottomface[k1].coords);
  gen zu = U[2]/U[0];
  gen tu = re(U[1]/U[0],&ct);
  gen V = Ai*K.vectevaln(faces[j].bottomface[k2].coords);
  gen zv = V[2]/V[0];
  gen tv = re(V[1]/V[0],&ct);
  zo = (zu+zv)/2;
  to = (tu+tv)/2;
  int n = faces[j].sides.size();
  if (n%2==0) {
    int k3 = (k1 + n/2)%n;
    int k4 = (k2 + n/2)%n;
    U = Ai*K.vectevaln(faces[j].bottomface[k3].coords);
    zu = U[2]/U[0];
    tu = re(U[1]/U[0],&ct);
    V = Ai*K.vectevaln(faces[j].bottomface[k4].coords);
    zv = V[2]/V[0];
    tv = re(V[1]/V[0],&ct);
    zt = (zu+zv)/2;
    tt = (tu+tv)/2;
  }
  else {
    int k3;
    if (k2>k1) {
      k3 = (k2 + n/2)%n;
    }
    else {
      k3 = (k1 + n/2)%n;
    }
    gen U = Ai*K.vectevaln(faces[j].bottomface[k3].coords);
    zt = U[2]/U[0];
    tt = re(U[1]/U[0],&ct);
  }

  gen Xp = Ai*K.vectevaln(faces[j].topface[0].coords);
  gen top_level = re(Xp[1]/Xp[0],&ct);
  gen zk = zo-zt;
  zk = zk/abs(zk,&ct);  
  file << "currentprojection=orthographic((" << re(zk,&ct) << "," << im(zk,&ct) << "," << 0.3 << "));" << endl;

  string norm = "(0,0,1)";
  string ccw = "false";
  //  cout << "Top" << endl;
  gen z1,z2,den,X;
  if (faces[j].topface.size()>1) {
    vecteur top_verts;
    gen X = Ai*K.vectevaln(faces[j].topface[0].coords);
    gen top_rv = K.onenumber - top_level*top_level;;
    top_verts.push_back(X[2]/X[0]);
    for (int k=1; k<faces[j].topface.size(); k++) {
      X = Ai*K.vectevaln(faces[j].topface[k].coords);
      top_verts.push_back(X[2]/X[0]);
    }
    for (int k=0; k<top_verts.size()-1; k++) {
      z1 = top_verts[k];
      z2 = top_verts[k+1];
      den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
      if (is_greater(1e-4,abs(den,&ct),&ct)) {
	// draw straight line
	file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),p);" << endl;
      }
      else {
	gen ctr = (top_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
	gen xc = re(ctr,&ct);
	gen yc = im(ctr,&ct);

	file << "draw(Arc(("<<xc<<","<<yc<<","<<top_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
      }
    }
    z1 = top_verts[top_verts.size()-1];
    z2 = top_verts[0];
    den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
    if (is_greater(1e-4,abs(den,&ct),&ct) && is_greater(abs(z1-z2,&ct),1e-4,&ct)) {

      // draw straight line
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),p);" << endl;

    }
    else {
      gen ctr = (top_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
      gen xc = re(ctr,&ct);
      gen yc = im(ctr,&ct);

      file << "draw(Arc(("<<xc<<","<<yc<<","<<top_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<top_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<top_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
    }
  }

  for (int k=0; k<faces[j].midvertices.size(); k++) {
    X = Ai*K.vectevaln(faces[j].midvertices[k].coords);
  }

  vecteur bottom_verts;
  X = Ai*K.vectevaln(faces[j].bottomface[0].coords);
  gen bottom_level = re(X[1]/X[0],&ct);
  gen bottom_rv = K.onenumber - bottom_level*bottom_level;
  bottom_verts.push_back(X[2]/X[0]);
  for (int k=1; k<faces[j].bottomface.size(); k++) {
    X = Ai*K.vectevaln(faces[j].bottomface[k].coords);
    bottom_verts.push_back(X[2]/X[0]);
  }
  for (int k=0; k<bottom_verts.size()-1; k++) {
    z1 = bottom_verts[k];
    z2 = bottom_verts[k+1];
    den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
    if (is_greater(1e-4,abs(den,&ct),&ct)) {

      // draw straight line
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),p);" << endl;
      vector<int> w = faces[j].mirror.word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z1+z2)/2;
	file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << bottom_level << "));" << endl;
      }

    }
    else {
      gen ctr = (bottom_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
      gen xc = re(ctr,&ct);
      gen yc = im(ctr,&ct);

      file << "draw(Arc(("<<xc<<","<<yc<<","<<bottom_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
      
      vector<int> w = faces[j].mirror.word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z1+z2)/2;
	gen zma = ctr + (zm-ctr)*abs(z1-ctr,&ct)/abs(zm-ctr,&ct);
	file << "$\",(" << re(zma,&ct) << "," << im(zma,&ct) << "," << bottom_level << "));" << endl;
      }
    }
  }
  z1 = bottom_verts[bottom_verts.size()-1];
  z2 = bottom_verts[0];
  den = conj(z1,&ct)*z2 - z1*conj(z2,&ct);
  if (is_greater(1e-4,abs(den,&ct),&ct)) {

    // draw straight line
    file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),p);" << endl;
    vector<int> w = faces[j].mirror.word;
    if (w.size()<10) {
      file << "label(\"$";
      for (int u=0; u<w.size(); u++) {
	if (w[u]<0) {
	  file << "\\bar" << -w[u];
	}
	else {
	  file << w[u];
	}
      }
      gen zm = (z1+z2)/2;
      file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << bottom_level << "));" << endl;
    }

  }
  else {
    gen ctr = (bottom_rv*(z2-z1) + abs(z1,&ct)*abs(z1,&ct)*z2 - abs(z2,&ct)*abs(z2,&ct)*z1 )/den;
    gen xc = re(ctr,&ct);
    gen yc = im(ctr,&ct);

    file << "draw(Arc(("<<xc<<","<<yc<<","<<bottom_level<<"),("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<bottom_level<<"),("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<bottom_level<<"),"<<norm<<","<<ccw<<"),p);" << endl;      
    vector<int> w = faces[j].mirror.word;
    if (w.size()<10) {
      file << "label(\"$";
      for (int u=0; u<w.size(); u++) {
	if (w[u]<0) {
	  file << "\\bar" << -w[u];
	}
	else {
	  file << w[u];
	}
      }
      gen zm = (z1+z2)/2;
      gen zma = ctr + (zm-ctr)*abs(z1-ctr,&ct)/abs(zm-ctr,&ct);
      file << "$\",(" << re(zma,&ct) << "," << im(zma,&ct) << "," << bottom_level << "));" << endl;
    }
  }

  // then draw vertical edges...  
  if (faces[j].topface.size()>1) {
    for (int l=0; l<faces[j].bottomface.size(); l++) {
      if (find(faces[j].midvertindices.begin(),faces[j].midvertindices.end(),l)==faces[j].midvertindices.end()) {
	// draw straight line from tf[l] to bf[l]
	gen X1 = Ai*K.vectevaln(faces[j].topface[l].coords);
	gen z1 = X1[2]/X1[0];
	gen t1 = re(X1[1]/X1[0],&ct);
	gen X2 = Ai*K.vectevaln(faces[j].bottomface[l].coords);
	gen z2 = X2[2]/X2[0];
	gen t2 = re(X2[1]/X2[0],&ct);
	file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
	vector<int> w = faces[j].sides[l].word;
	if (w.size()<10) {
	  file << "label(\"$";
	  for (int u=0; u<w.size(); u++) {
	    if (w[u]<0) {
	      file << "\\bar" << -w[u];
	    }
	    else {
	      file << w[u];
	    }
	  }
	  gen zm = (z1+z2)/2;
	  gen tm = (t1+t2)/2;
	  file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
	}
      }
    }
  }
  else {
    for (int l=0; l<faces[j].bottomface.size(); l++) {
      if (find(faces[j].midvertindices.begin(),faces[j].midvertindices.end(),l)==faces[j].midvertindices.end()) {
	// draw straight line from bf[l] to tf[0]
	gen X1 = Ai*K.vectevaln(faces[j].topface[0].coords);
	gen z1 = X1[2]/X1[0];
	gen t1 = re(X1[1]/X1[0],&ct);
	gen X2 = Ai*K.vectevaln(faces[j].bottomface[l].coords);
	gen z2 = X2[2]/X2[0];
	gen t2 = re(X2[1]/X2[0],&ct);
	file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
	vector<int> w = faces[j].sides[l].word;
	if (w.size()<10) {
	  file << "label(\"$";
	  for (int u=0; u<w.size(); u++) {
	    if (w[u]<0) {
	      file << "\\bar" << -w[u];
	    }
	    else {
	      file << w[u];
	  }
	  }
	  gen zm = (z1+z2)/2;
	  gen tm = (t1+t2)/2;
	  file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
	}
      }
    }
  }
  
  for (int l=0; l<faces[j].midvertices.size(); l++) {
    if (faces[j].topface.size()>1) {
      
      gen X1 = Ai*K.vectevaln(faces[j].bottomface[faces[j].midvertindices[l]].coords);
      gen z1 = X1[2]/X1[0];
      gen t1 = re(X1[1]/X1[0],&ct);
      
      gen X2 = Ai*K.vectevaln(faces[j].midvertices[l].coords);
      gen z2 = X2[2]/X2[0];
      gen t2 = re(X2[1]/X2[0],&ct);
      
      gen X3 = Ai*K.vectevaln(faces[j].topface[faces[j].midvertindices[l]].coords);
      gen z3 = X3[2]/X3[0];
      gen t3 = re(X3[1]/X3[0],&ct);
      
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
      file << "draw(("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<")--("<<re(z3,&ct)<<","<<im(z3,&ct)<<","<<t3<<"),p);" << endl;	

      vector<int> w = faces[j].sides[faces[j].midvertindices[l]].word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z2+z3)/2;
	gen tm = (t2+t3)/2;
	file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
      } 
    }
    else {

      gen X1 = Ai*K.vectevaln(faces[j].bottomface[faces[j].midvertindices[l]].coords);
      gen z1 = X1[2]/X1[0];
      gen t1 = re(X1[1]/X1[0],&ct);
      
      gen X2 = Ai*K.vectevaln(faces[j].midvertices[l].coords);
      gen z2 = X2[2]/X2[0];
      gen t2 = re(X2[1]/X2[0],&ct);
      
      gen X3 = Ai*K.vectevaln(faces[j].topface[0].coords);
      gen z3 = X3[2]/X3[0];
      gen t3 = re(X3[1]/X3[0],&ct);
      
      file << "draw(("<<re(z1,&ct)<<","<<im(z1,&ct)<<","<<t1<<")--("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<"),p);" << endl;	
      file << "draw(("<<re(z2,&ct)<<","<<im(z2,&ct)<<","<<t2<<")--("<<re(z3,&ct)<<","<<im(z3,&ct)<<","<<t3<<"),p);" << endl;	

      vector<int> w = faces[j].sides[faces[j].midvertindices[l]].word;
      if (w.size()<10) {
	file << "label(\"$";
	for (int u=0; u<w.size(); u++) {
	  if (w[u]<0) {
	    file << "\\bar" << -w[u];
	  }
	  else {
	    file << w[u];
	  }
	}
	gen zm = (z2+z3)/2;
	gen tm = (t2+t3)/2;
	file << "$\",(" << re(zm,&ct) << "," << im(zm,&ct) << "," << tm << "),align=right);" << endl;
      }
    }
    
  }

  file << "pen dp = blue+4;"<< endl;
  file << "pen dpi = red+4;"<< endl;
  for (int k=0; k<faces[j].topface.size(); k++) {
    gen X = Ai*K.vectevaln(faces[j].topface[k].coords);
    gen z = X[2]/X[0];
    gen t = re(X[1]/X[0],&ct);

    gen te = SqNorm(faces[j].topface[k].coords);
    if (operator_equal(te,K.zeronumber,&ct)) {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dpi);" << endl;
    }
    else {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dp);" << endl;
    }

  }
  for (int k=0; k<faces[j].bottomface.size(); k++) {
    gen X = Ai*K.vectevaln(faces[j].bottomface[k].coords);
    gen z = X[2]/X[0];
    gen t = re(X[1]/X[0],&ct);

    gen te = SqNorm(faces[j].bottomface[k].coords);
    if (operator_equal(te,K.zeronumber,&ct)) {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dpi);" << endl;
    }
    else {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dp);" << endl;
    }

  }
  for (int k=0; k<faces[j].midvertices.size(); k++) {
    gen X = Ai*K.vectevaln(faces[j].midvertices[k].coords);
    gen z = X[2]/X[0];
    gen t = re(X[1]/X[0],&ct);

    gen te = SqNorm(faces[j].midvertices[k].coords);
    if (operator_equal(te,K.zeronumber,&ct)) {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dpi);" << endl;
    }
    else {
      file << "dot(("<<re(z,&ct)<<","<<im(z,&ct)<<","<<t<<"),dp);" << endl;
    }

  }


  gen Xt = Ai*K.vectevaln(faces[j].topface[0].coords);
  gen top_lev = re(Xt[1]/Xt[0],&ct);
  file << "draw((0,0,0)--(0,0," << top_lev << "),black+dashed);" << endl;
  
  if (faces[j].topface.size()>1) {
    gen Xt1 = Ai*K.vectevaln(faces[j].topface[kv1].coords);
    gen Xt2 = Ai*K.vectevaln(faces[j].topface[kv2].coords);
    gen zt1 = Xt1[2]/Xt1[0];
    gen tt1 = re(Xt1[1]/Xt1[0],&ct);
    gen zt2 = Xt2[2]/Xt2[0];
    gen tt2 = re(Xt2[1]/Xt2[0],&ct);
    file << "draw(("<<re(zt1,&ct)<<","<<im(zt1,&ct)<<","<<tt1<<")--(0,0,"<<top_lev<<"),black+dashed);" << endl;	
    file << "draw(("<<re(zt2,&ct)<<","<<im(zt2,&ct)<<","<<tt2<<")--(0,0,"<<top_lev<<"),black+dashed);" << endl;	
  }

  gen Xb1 = Ai*K.vectevaln(faces[j].bottomface[kv1].coords);
  gen Xb2 = Ai*K.vectevaln(faces[j].bottomface[kv2].coords);
  gen zb1 = Xb1[2]/Xb1[0];
  gen tb1 = re(Xb1[1]/Xb1[0],&ct);
  gen zb2 = Xb2[2]/Xb2[0];
  gen tb2 = re(Xb2[1]/Xb2[0],&ct);
  file << "draw(("<<re(zb1,&ct)<<","<<im(zb1,&ct)<<","<<tb1<<")--(0,0,0),black+dashed);" << endl;	
  file << "draw(("<<re(zb2,&ct)<<","<<im(zb2,&ct)<<","<<tb2<<")--(0,0,0),black+dashed);" << endl;	

  file.close();

}


void Polytope::drawProjection(int axis) {

  if (axis!=1 && axis!=2) {
    axis = 1;
  }

  ofstream file;
  string ax = static_cast<ostringstream*>( &(ostringstream() << axis) )->str();

  string filename = "pics/pic"+K.tag+"_"+ax+".asy";
  file.open(filename.c_str());
  file << "import graph;" << endl;
  file << "size(7.5cm,0);" << endl;

  gen Hn = K.matevaln(H);
  gen Qn = K.matevaln(Q);

  gen D = _tran(conj(Qn,&ct),&ct)*Hn*Qn;
  vecteur te;
  te.push_back(K.onenumber/sqrt(-re(D[0][0],&ct),&ct));
  te.push_back(K.onenumber/sqrt(-re(D[1][1],&ct),&ct));
  te.push_back(K.onenumber/sqrt(-re(D[2][2],&ct),&ct));
  gen KK = _diag(te,&ct);
  Qn = Qn*KK;

  Qn = _inverse(Qn,&ct);

  gen fa = gen("180/pi",&ct);
  for (int k=0; k<edges.size(); k++) {
    gen X = _subst(makesequence(vertices[edges[k].origin].coords,x__IDNT_e,K.xv),&ct);
    gen Y = _subst(makesequence(vertices[edges[k].end].coords,x__IDNT_e,K.xv),&ct);
    gen te = _subst(makesequence(Inn(vertices[edges[k].origin].coords,vertices[edges[k].end].coords),x__IDNT_e,K.xv),&ct);
    gen Y1 = -Y*te/abs(te);
    gen X2 = Qn*X;
    gen Y2 = Qn*Y1;

    gen a1 = X2[axis]/X2[0];
    gen a3 = Y2[axis]/Y2[0];
    if (is_greater(abs(a1-a3,&ct),1e-4,&ct)) {

      gen a2 = (X2[axis]+Y2[axis])/(X2[0]+Y2[0]);
      
      gen b1 = conj(a2-a1,&ct);
      gen b2 = conj(a3-a1,&ct);
      gen l1 = abs(a2)*abs(a2)-abs(a1)*abs(a1);
      gen l2 = abs(a3)*abs(a3)-abs(a1)*abs(a1);

      gen denc = (b1*conj(b2,&ct)-conj(b1,&ct)*b2);
      //      cout << "denc=" << denc << endl;
      if (is_greater(abs(denc),1e-4,&ct)) {
	gen c = (conj(b2,&ct)*l1-conj(b1,&ct)*l2)/denc;
	gen r = sqrt(abs(a1)*abs(a1)-2*re(a1*conj(c,&ct),&ct)+abs(c)*abs(c),&ct);
	
	gen u1 = a1-c;
	gen u3 = a3-c;
	gen th1 = arg(u1,&ct)*fa;
	gen th2 = arg(u3,&ct)*fa;

	if (is_greater(th2,th1,&ct)) {
	  if (is_greater(th2-th1,180,&ct)) {
	    th2 = th2-360;
	  }
	}
	else {
	  if (is_greater(th1-th2,180,&ct)) {
	    th1 = th1-360;
	  }
	}
	
	if (is_greater(abs(th1-th2,&ct),180,&ct)) {
	  cout << "OOPS, unexpected situation.. this is not a serious problem" << endl;
	  cout << " (some pictures may be wrong)" << endl;
	}
	
	file << "draw(arc(("<<re(c,&ct)<<","<<im(c,&ct)<<"),"<<r<<","<<th1<<","<<th2<<"),black);" << endl;
      }
      else {
	file << "draw(("<<re(a1,&ct)<<","<<im(a1,&ct)<<")--("<<re(a3,&ct)<<","<<im(a3,&ct)<<"),black);" << endl;	
      }
	
    }
  }
  file.close();
}

bool Polytope::checkSide() {
  vector<int> inds;
  for (int k=0; k<faces.size(); k++) {
    inds.push_back(k);
  }
  while (inds.size()>0) {
    int k = inds[0];
    bool foundbuddy = false;
    int u = 0;
    while (!foundbuddy && u<inds.size()-1) {
      u++;
      gen X = BoxProd(faces[k].cspine,faces[inds[u]].cspine);
      if (!operator_equal(X,K.zerovector,&ct)) {
	int sx;
	if (K.checkSign(SqNorm(X),sx)) {
	  foundbuddy = sx<0;
	  if (foundbuddy) {
	    faces[k].X0 = X;
	    faces[k].X1 = reflectAcrossSpine(X,faces[k]);
	    faces[inds[u]].X0 = X;
	    faces[inds[u]].X1 = reflectAcrossSpine(X,faces[inds[u]]); 
	  }
	}
      }
    }
    if (!foundbuddy) {
      // may have to use other faces as buddies...
      u=-1;
      while (!foundbuddy && u!=faces.size()-1) {
	u++;
	gen X = BoxProd(faces[k].cspine,faces[u].cspine);
	if (!operator_equal(X,K.zerovector,&ct)) {
	  int sx;
	  if (K.checkSign(SqNorm(X),sx)) {
	    foundbuddy = sx<0;
	    if (foundbuddy) {
	      faces[k].X0 = X;
	      faces[k].X1 = reflectAcrossSpine(X,faces[k]);
	    }
	  }
	}
      }
      if (!foundbuddy) {
	cerr << "Face #" << k << " is not coequidistant from anyone..." << endl;
	cerr << " this may not be a serious issue, but I will stop now." << endl;
	exit(1);
      }
      else {
	inds.erase(inds.begin());
      }
    }
    else {
      inds.erase(inds.begin()+u);
      if (u!=0) {
	inds.erase(inds.begin());
      }
      else {
	cerr << "Trouble in checkSide()" << endl;
	exit(1);
      }
    }
  }
  gen Hn = K.evali(H);
  gen p0n = evalf(p0,20,&ct);
  bool res = true;
  for (int k=0; k<faces.size() && res; k++) {
    gen u1 = K.evali(faces[k].X0);
    gen v1 = K.evali(faces[k].X1);
    gen te = abs( conj(p0n,&ct)*Hn*u1 ) - abs( conj(p0n,&ct)*Hn*v1 );
    res = is_greater(K.zeronumber,_right(te,&ct),&ct); 
    if (!res) {
      cout << "Test face " << k << ": " << te << endl;
    }
  }
  return res;

}

void Polytope::checkEdge(int j) {
  
  cout << "Studying edge #" << j << ", whose endpoints are vertices of: " << convertword(edges[j].faceindices) << "... ";

  gen X = vertices[edges[j].origin].coords;
  gen Y = vertices[edges[j].end].coords;

  gen te = Inn(X,Y);
  Y = -te*Y;
  K.vectred(Y);
  gen Z = (1-K.t1)*X+K.t1*Y;
  K.vectred(Z);
  vector<int> inds;
  for (int k=0; k<faces.size(); k++) {
    if (find(edges[j].faceindices.begin(),edges[j].faceindices.end(),k)==edges[j].faceindices.end()) {
      inds.push_back(k);
    }
  }

  for (int k=0; k<inds.size(); k++) {
    gen eq = simplify(Inn(Z,faces[inds[k]].X0)*Inn(faces[inds[k]].X0,Z) - Inn(Z,faces[inds[k]].X1)*Inn(faces[inds[k]].X1,Z),&ct);
    K.red(eq);
    eq = K.convertToReal(eq);
    if (operator_equal(eq,K.zeronumber,&ct)) {
      cout << "Equation is 0, edge is on a bisector of which it is not a facet (maybe because of reflections of order 2)" << endl;
    }
    else {

      gen eq0 = _coeff(makesequence(eq,K.t1,0),&ct);
      gen eq1 = simplify(_subst(makesequence(eq,K.t1,K.onenumber),&ct),&ct);
      simplify(eq1,&ct);
      K.red(eq1);

      int sgn0, sgn1;
      if (!K.checkSign(eq0,sgn0)){
	cerr << "Problem checking sign of equation at origin, in edge check" << endl;
	exit(1);      
      }
      if (!K.checkSign(eq1,sgn1)){
	cerr << "Problem checking sign of equation at endpt, in edge check" << endl;
	exit(1);      
      }
      if (sgn0>0 || sgn1>0) {
	cout << "Edge is on WRONG side! (found this at an endpoint)" << endl;
	cerr << "Edge is on WRONG side! (found this at an endpoint)" << endl;
	exit(1);      
      }
      
      if (sgn0==0){

	if (sgn1==0){
	  cerr << "Both t=0 and t=1 are roots, this should mean edge is in the bisector!" << endl;
	  // ACTUALLY NOT QUITE, JUST WANT TO CHECK MIDPOINT!
	  gen tet = simplify(_subst(makesequence(eq,K.t1,K.onenumber/2),&ct),&ct);
	  int sgn2;
	  if (!K.checkSign(tet,sgn2)) {
	    cerr << "Problem checking sign of equation at midpt, in edge check" << endl;
	    exit(1);
	  }
	  if (sgn2>0) {
	    cout << "Edge is on WRONG side! (found this at an midpoint)" << endl;
	    cerr << "Edge is on WRONG side! (found this at an midpoint)" << endl;
	    exit(1);
	  }
	}
	else {
	  gen a = _coeff(makesequence(eq,K.t1,2),&ct);
	  if (operator_equal(a,K.zeronumber,&ct)) {
	    //	  cout << "Only t=0 gives a root" << endl;
	    //	  cout << "Edge is on negative side" << endl;
	    //	  return true;
	  }
	  else {
	    // check if te=-b/a is in [0,1]
	    gen te = -_coeff(makesequence(eq,K.t1,1),&ct)*K.inverse(a);
	    K.red(te);
	    //	  cout << "te=" << te << endl;
	    //	  cout << "Need to check if " << K.evali(te) << " is in [0,1]..." << endl;
	    int sc;
	    if (!K.checkSign(te,sc)) {
	      cerr << "Problem checking sign of t-value in edge parameter" << endl;
	      exit(1);
	    }
	    else {
	      if (sc>0) {
		if (!K.checkSign(te-1,sc)) {
		  cerr << "Problem checking whether t<=1 in edge parameter" << endl;
		  exit(1);
		}
		else {
		  if (sc<0) {
		    cerr << "The 1-skeleton is not embedded!" << endl;
		    exit(1);
		  }
		}
	      }
	      else {
		//  cout << "Only t=0 gives a root in [0,1]" << endl;	      
		//  cout << "Edge is on negative side" << endl;
		//	      return true;
	      }
	    }
	  }	
	}
      }
      else {
	// sgn0 != 0
	if (sgn1==0){
	  //	cout << "t=1 is a root" << endl;
	  gen a = _coeff(makesequence(eq,K.t1,2),&ct);
	  if (operator_equal(a,K.zeronumber,&ct)) {
	    //	  cout << "Only t=1 gives a root" << endl;
	    //	  return true;
	  }
	  else {
	    // check if te=-1-b/a is in [0,1]
	    gen te = -1-_coeff(makesequence(eq,K.t1,1),&ct)*K.inverse(a);
	    K.red(te);
	    //	  cout << "Need to check if " << K.evali(te) << " is in [0,1]..." << endl;
	    int sc;
	    if (!K.checkSign(te,sc)) {
	      cerr << "Problem checking sign of t-value in edge parameter" << endl;
	      exit(1);
	    }
	    else {
	      if (sc>0) {
		if (!K.checkSign(te-1,sc)) {
		  cerr << "Problem checking whether t<=1 in edge parameter" << endl;
		  exit(1);
		}
		else {
		  if (sc<0) {
		    cerr << "The 1-skeleton is not embedded!" << endl;
		    exit(1);
		  }
		  else {
		    //		  cout << "Only t=1 gives a root in [0,1]" << endl;	      
		    //		  cout << "Edge is on negative side" << endl;
		    //		  return true;
		  }
		}
	      }
	      else {
		//	      cout << "Only t=1 gives a root in [0,1]" << endl;	      
		//	      cout << "Edge is on negative side" << endl;
		//	      return true;
	      }
	    }
	  }	
	}
	else {
	  // no endpoint is on bisector, need to work harder?
	  // no, will simply evaluate value at maximum?  //2at+b -> -b/2a
	  gen a = _coeff(makesequence(eq,K.t1,2),&ct);
	  int sga;
	  if (!K.checkSign(a,sga)) {
	    cerr << "Problem checking sign of a, in edge check" << endl;
	    exit(1);      
	  }
	  if (sga==0) {
	    //	  cout << "Linear equation, enough to check endpoints!" << endl;	      
	    //	  cout << "Edge is on negative side" << endl;
	    //	  return true;
	  }
	  else {
	    if (sga>0) {
	      //	    cout << "Concavity is up, checking endpoints is enough..." << endl;	    
	      //	    cout << "Edge is on negative side" << endl;
	      //	    return true;
	    }
	    else {
	      gen tm = -_coeff(makesequence(eq,K.t1,1),&ct)*K.inverse(2*a);
	      K.red(tm);
	      
	      //	    cout << "t-max: " << K.evali(tm) << endl;
	      
	      // check if max is in [0,1], and if so, check that the max value is negative
	      int sg;
	      if (!K.checkSign(tm,sg)) {
		cerr << "Problem checking sign of t-value of max of parabola, in edge check" << endl;
		exit(1);      	      
	      }
	      else {
		if (sg>0) {
		  // tmax is > 0
		  if (!K.checkSign(tm-1,sg)) {
		    cerr << "Problem checking t-value of max of parabola, in edge check" << endl;
		    exit(1);      	      
		  }
		  else {
		    if (sg<0) {
		      // t-max is between 0 and 1...
		      gen yo = _subst(makesequence(eq,K.t1,tm),&ct);
		      if (!K.checkSign(yo,sg)) {
			cerr << "Problem checking sign of max value, in edge check" << endl;
			exit(1);      	      		      
		      }
		      else {
			if (sg<=0) {
			  //		cout << "Max of parabola occurs in [0,1], but value is negative." << endl;	    
			  //		cout << "Edge is on negative side" << endl;
			  //	    return true;
			}
			else {
			  cout << "Edge is on WRONG side! (found this at an endpoint)" << endl;
			  cerr << "Edge is on WRONG side! (found this at an endpoint)" << endl;
			  exit(1);      
			}
		      }
		    }
		    else {
		      // tmax is >1
		      //		    cout << "t-max is > 1" << endl;	    
		      //		    cout << "Edge is on negative side" << endl;
		    }
		  }
		}
		else {
		  // tmax is < 0
		  //		cout << "t-max is < 0" << endl;	    
		  //		cout << "Edge is on negative side" << endl;
		}
	      }	    
	    }	  
	  }
	}
      }
    }
  }
  cout << " check!" << endl;
}

void Polytope::checkRidge(int i) {

  int j =  ridges[i].faceindices[0];
  int k =  ridges[i].faceindices[1];
  cout << endl;
  cout << "Checking ridge on faces #" << j << " and " << k << endl;

  bool genparam = true;
  gen X0 = BoxProd(faces[j].cspine,faces[k].cspine);
  gen V0, W0, Z0; // Orthogonal (but not orthonormal) basis, with V0, W0 spanning common slice
  // not sure I use this later...
  if (operator_equal(X0,K.zerovector,&ct)) {
    // cospinal
    genparam = false;
    gen Xte = BoxProd(faces[j].mirror.coords,faces[k].mirror.coords);
    if (operator_equal(Xte,K.zerovector,&ct)) {
      cout << "Same mirror, this gives the common slice" << endl;
      V0 = faces[j].apexProjection;
      W0 = Proj(faces[j].bottomface[0].coords,V0);	
      Z0 = BoxProd(V0,W0); // not clear if we need this
    }
    else {
      Xte = BoxProd(faces[j].mirror.coords,faces[k].apex.coords);
      if (operator_equal(Xte,K.zerovector,&ct)) {
	cout << "Mirror of face#" << j << " is same as top face of #" << k << endl;
	cout << "     this gives the common slice" << endl;	
	V0 = faces[j].apexProjection;
	W0 = Proj(faces[j].bottomface[0].coords,V0);
	Z0 = BoxProd(V0,W0); // not clear if we need this
      }
      else {
	Xte = BoxProd(faces[k].mirror.coords,faces[j].apex.coords);
	if (operator_equal(Xte,K.zerovector,&ct)) {
	  cout << "Mirror of face#" << k << " is same as top face of #" << j << endl;
	  cout << "     this gives the common slice" << endl;	
	  V0 = faces[k].apexProjection;
	  W0 = Proj(faces[k].bottomface[0].coords,V0);
	  Z0 = BoxProd(V0,W0); // not clear if we need this
	}
	else {
	  Xte = BoxProd(faces[j].apex.coords,faces[k].apex.coords);
	  if (operator_equal(Xte,K.zerovector,&ct)) {
	    cout << "Top of face#" << k << " is same as top face of #" << j << endl;
	    cout << "     this gives the common slice" << endl;	
	    V0 = BoxProd(faces[j].cspine,faces[j].apex.coords);
	    W0 = Proj(faces[j].topface[0].coords,V0);
	    Z0 = BoxProd(V0,W0); // not clear if we need this
	  }
	  else {

	    cerr << "There is probably no common slice, will need to check this.." << endl;	  
	    cout << "There is probably no common slice, will need to check this.." << endl;	  
	    
	    gen vj, wj, vk, wk;
	    
	    wj = faces[j].apexProjection;
	    vj = faces[j].apex.coords;
	    vj = Inn(wj,vj)*vj;
	    K.vectred(vj);
	    vj = Proj(vj,wj);
	    
	    gen vt = vj + K.t1*wj;
	    
	    // take vj+t*wj, and find t-values for this to be on face k
	    //   
	    gen eq = Inn(vt,faces[k].X0)*Inn(faces[k].X0,vt) - Inn(vt,faces[k].X1)*Inn(faces[k].X1,vt); 
	    K.red(eq);
	    eq = K.convertToReal(eq);
	    K.red(eq);
	    cout << "Equation to solve: " << eq << endl;
	    
	    gen eqro = eval(_subst(makesequence(eq,s__IDNT_e,K.realgenasrootof),&ct),1,&ct);
	    
	    cout << "eqro:" << eqro << endl;
	    cout << "realgenasrootof: " << K.realgenasrootof << endl;
	    cout << "   " << _evalf(K.realgenasrootof,&ct) << endl;
	    
	    gen fa = _factors(makesequence(eqro,K.realgenasrootof),&ct);
	    for (int l=0; 2*l<(*fa._VECTptr).size(); l++) {
	      cout << "fa[" << (2*l) << "]=" << fa[2*l] << endl;
	      int deg = _degree(makesequence(fa[2*l],K.t1),&ct).val;
	      if (deg>1) {
		cout << "Intersection of the real spines is not defined over the ambient number field" << endl;
		cout << "  (more programming to do!)" << endl;
		exit(1);
	      }
	      else {
		if (deg>0) {
		  gen te = normal(-_coeff(makesequence(fa[2*l],K.t1,0),&ct)/_coeff(makesequence(fa[2*l],K.t1,1),&ct),&ct);
		  if (verbose) {
		    cout << "te=" << te << endl;
		    cout << " (" << _evalf(te,&ct) << ")" << endl;
		  }
		}
	      }
	    }
	    
	    gen expr = SqNorm(vt);
	    K.red(expr);
	    expr = K.convertToReal(expr);
	    K.red(expr);
	    cout << "Then need to check sign of: " << expr << endl;
	  
	  }
	}
      }
    }
  }
  else {
    // complex spines intersect in projective space, need to check if they intersect on the real spines
    if (isOnPrism(X0,faces[j])) {
      cout << "Extended real spine of face #" << j << " intersects extor #" << k << endl;
      if (isOnPrism(X0,faces[k])) {
	cout << "Extended real spine of face #" << k << " intersects extor #" << j << endl;
	cout << "Real spines intersect... "; //either cotranchal, or linear?
	int sx;
	if (!K.checkSign(SqNorm(X0),sx)) {
	  cerr << "Trouble computing sign of square norm (in checkRidge)" << endl;
	  exit(1);
	}
	else {
	  if (sx>0) {
	    cout << "Cotranchal pair" << endl;

	    gen Y1 = faces[j].apexProjection;
	    Y1 = Inn(X0,Y1)*Y1;
	    K.red(Y1);
	    Y1 = Proj(Y1,X0);
	    gen Y2 = faces[k].apexProjection;
	    Y2 = Inn(X0,Y2)*Y2;
	    K.red(Y2);
	    Y2 = Proj(Y2,X0);
	    gen tee = Inn(Y1,faces[j].X0)*Inn(faces[j].X0,Y1)-Inn(Y1,faces[j].X1)*Inn(faces[j].X1,Y1);
	    K.red(tee);
	    tee = Inn(Y2,faces[k].X0)*Inn(faces[k].X0,Y2)-Inn(Y2,faces[k].X1)*Inn(faces[k].X1,Y2);
	    K.red(tee);
	    gen tt = (Inn(Y1,Y2)+Inn(Y2,Y1))/2;
	    gen te = SqNorm(Y1)*SqNorm(Y2) - tt*tt;
	    K.red(te);
	    te = K.convertToReal(te);
	    int sgn;
	    if (!K.checkSign(te,sgn)) {
	      cerr << "Trouble checking sign (is intersection is slice only)" << endl;
	      exit(1);
	    }
	    else {
	      if (sgn<0) {
		cerr << "Intersection is not just the common slice!?" << endl;
		//		exit(1);
		genparam = false; // THIS IS NOT WHAT WE WANT, ONLY OK IF WE DON'T NEED TO CHECK EXTRA COMPONENT...
	      }
	      else {
		cout << "intersection is slice only!" << endl;
		genparam = false;
	      }
	    }
	      
	    V0 = X0;
	    if (K.areDep(X0,faces[j].apex.coords)) {
	      cout << "Common slice is top face of face #" << j << endl;
	      W0 = Proj(faces[j].topface[0].coords,V0);
	    }
	    else {
	      if (K.areDep(X0,faces[j].mirror.coords)) {
		cout << "Common slice is mirror of face #" << j << endl;
		W0 = Proj(faces[j].bottomface[0].coords,V0);
	      }
	      else {
		cerr << "Unidentified common slice, this is odd.." << endl;
		exit(1);
	      }
	    }
	    Z0 = BoxProd(V0,W0); // not clear if we need this
	    if (K.areDep(X0,faces[k].apex.coords)) {
	      cout << "Common slice is top face of face #" << k << endl;
	    }
	    else {
	      if (K.areDep(X0,faces[k].mirror.coords)) {
		cout << "Common slice is mirror of face #" << k << endl;
	      }
	      else {
		cerr << "Unidentified common slice, this is odd.." << endl;
		exit(1);
	      }
	    }
	  }
	  else {
	    if (sx==0) {
	      cerr << "Strange, real spines intersect at infinity..." << endl;
	      exit(1);
	    }
	    else {
	      //	      cerr << "Real spines intersect inside, linear intersection (not implemented yet)" << endl;
	      
	      if (K.areDep(faces[j].cspine,faces[k].cspine)) {
		cout << "Same complex spine!" << endl;
		exit(1);
	      }
	
	      gen te = Inn(faces[j].cspine,faces[k].cspine);
	      if (is_zero(te,&ct)) {
		cout << "Orthogonal complex spines" << endl;
	      }
	
	      genparam = true;
	    }
	  }
	}
      }	
      else {
	cout << "Real spines don't intersect" << endl;
	genparam = true;
      }
    }
    else {
      cout << "Real spines don't intersect" << endl;
      genparam = true;
    }
  }

  if (genparam) {
    gen vj, wj, vk, wk, vl, wl;

    wj = faces[j].apexProjection;
    vj = faces[j].apex.coords;
    vj = Inn(wj,vj)*vj;
    K.vectred(vj);
    vj = Proj(vj,wj);

    wk = faces[k].apexProjection;
    vk = faces[k].apex.coords;
    vk = Inn(wk,vk)*vk;
    K.vectred(vk);
    vk = Proj(vk,wk);

    // need vl and wl, for third bisector in Giraud's theorem!

    cout << "Will construct third bisector (Giraud)" << endl;

    bool fd = false;
    int u1=-1;
    while (!fd && u1!=faces[j].sides.size()-1) {
      u1++;
      fd = K.areDep(faces[j].sides[u1].coords,faces[k].mirror.coords);
    }
    if (!fd) {
      cerr << "Trouble findind third bisector (mirror)" << endl;
      exit(1);
    }

    int u2=-1;
    int v;
    fd = false;
    while (!fd && u2!=faces[j].sides.size()-1) {
      u2++;
      v=-1;
      while (!fd && v!=faces[k].sides.size()-1) {
	v++;
	fd = K.areDep(faces[j].sides[u2].coords,faces[k].sides[v].coords);
      }
    }
    if (!fd) {
      cerr << "Trouble findind third bisector (side)" << endl;
      exit(1);
    }

    // mirror and apex of third bisector
    gen mi = faces[j].sides[u2].coords;
    gen ap = BoxProd(faces[j].sides[u1].coords,faces[j].mirror.coords);
    gen app = Proj(ap,mi);
    
    wl = app;
    vl = Inn(app,ap)*ap;
    K.vectred(vl);
    vl = Proj(vl,wl);

    gen t1min, t1max, t2min, t2max, t3min, t3max;
    for (int z=0; z<ridges[i].verts.size(); z++) {
      gen t1v = -Inn(vj,vertices[ridges[i].verts[z]].coords)*K.inverse(Inn(wj,vertices[ridges[i].verts[z]].coords));
      gen t2v = -Inn(vk,vertices[ridges[i].verts[z]].coords)*K.inverse(Inn(wk,vertices[ridges[i].verts[z]].coords));
      gen t3v = -Inn(vl,vertices[ridges[i].verts[z]].coords)*K.inverse(Inn(wl,vertices[ridges[i].verts[z]].coords));
      K.red(t1v);
      K.red(t2v);
      K.red(t3v);
      t1v = K.convertToReal(t1v);
      t2v = K.convertToReal(t2v);
      t3v = K.convertToReal(t3v);
      if (z==0) {
	t1min = t1v;
	t1max = t1v;
	t2min = t2v;
	t2max = t2v;
	t3min = t3v;
	t3max = t3v;
      }
      else {
	int sg;
	if (!K.checkSign(t1v-t1min,sg)) {
	  cerr << "Problem checking sign (t1min)" << endl;
	}
	else {
	  if (sg<0) {
	    t1min = t1v;
	  }
	}
	if (!K.checkSign(t1v-t1max,sg)) {
	  cerr << "Problem checking sign (t1max)" << endl;
	}
	else {
	  if (sg>0) {
	    t1max = t1v;
	  }
	}

	if (!K.checkSign(t2v-t2min,sg)) {
	  cerr << "Problem checking sign (t2min)" << endl;
	}
	else {
	  if (sg<0) {
	    t2min = t2v;
	  }
	}
	if (!K.checkSign(t2v-t2max,sg)) {
	  cerr << "Problem checking sign (t2max)" << endl;
	}
	else {
	  if (sg>0) {
	    t2max = t2v;
	  }
	}

	if (!K.checkSign(t3v-t3min,sg)) {
	  cerr << "Problem checking sign (t3min)" << endl;
	}
	else {
	  if (sg<0) {
	    t3min = t3v;
	  }
	}
	if (!K.checkSign(t3v-t3max,sg)) {
	  cerr << "Problem checking sign (t3max)" << endl;
	}
	else {
	  if (sg>0) {
	    t3max = t3v;
	  }
	}
      }
    }

      
    // take (vj+t1*wj)x(vk+t2*wk)
    gen v0, v1, v2, v3; 
    v0 = BoxProd(vj,vk);
    v1 = BoxProd(wj,vk);
    v2 = BoxProd(vj,wk);
    v3 = BoxProd(wj,wk);
    gen vt = v0 + K.t1*v1 + K.t2*v2 + K.t1*K.t2*v3;

    gen t3n = -Inn(vt,vl);
    gen t3d = Inn(vt,wl);

    for (int l=0; l<faces.size(); l++) {
      gen eq = Inn(vt,faces[l].X0)*Inn(faces[l].X0,vt) - Inn(vt,faces[l].X1)*Inn(faces[l].X1,vt); 
      K.red(eq);
      eq = K.convertToReal(eq);
      K.red(eq);
      if (verbose) {
	cout << "eq=" << eq << endl;
      }
      
      if (operator_equal(eq,K.zeronumber,&ct)) {
	cout << "Eq #" << l << " is 0..." << endl;
      }
      else {

	gen eq1 = _derive(makesequence(eq,K.t1),&ct);
	gen eq2 = _derive(makesequence(eq,K.t2),&ct);

	/*	// need to check if one of these is 0, in which case the third bisector intersects the ridge in (one or two) line(s)...

	if ( operator_equal(eq1,K.zeronumber,&ct) ) {

	  cout << "Equation depends only on t2, need to handle this differently" << endl;
	  // need to know if eq is positive between t2min and t2max

	  int d2 = _degree(makesequence(eq,K.t2),&ct).val;
	  
	  gen lc = _coeff(makesequence(eq,K.t2,d2),&ct);
	  cout << "Leading coeff: " << lc << endl;

	  int slc;
	  if (!K.checkSign(lc,slc)) {
	    cout << "Trouble checking sign of leading coefficient" << endl;
	    exit(1);
	  }
	  cout << "Sign of leading coeff: " << slc << endl;
	      						      
	  if (d2==1) {
	    if (slc>0) {
	      gen temin = _subst(makesequence(eq,K.t2,t2min),&ct);
	      int smin;
	      if (!K.checkSign(temin,smin)) {
		cout << "Trouble checking sign of value at t2min" << endl;
		exit(1);
	      }
	      if (smin<0) {
		
	      }
	    }
	  }
						      
	  gen temax = _subst(makesequence(eq,K.t2,t2max),&ct);
	  int smin, smax;
	  if (!K.checkSign(temin,smin) || !K.checkSign(temax,smax)) {
	    cout << "Trouble checking sign of leading coefficient" << endl;
	    exit(1);
	  }
          cout << "Sgn of value at tmin: " << smin << endl;
          cout << "Sgn of value at tmax: " << smax << endl;
	  	  
	  //   if degree 1 in t2min, solve the equation, compare with t2min + sign of leading coeff?
	  //   if degree 2 in t2min, check if t2min 
	  
	  cout << "t1min:" << t1min << endl;
	  cout << "t1max:" << t1max << endl;
	  cout << "t2min:" << t2min << endl;
	  cout << "t2max:" << t2max << endl;

	  exit(1);
	}
	
	if ( operator_equal(eq2,K.zeronumber,&ct) ) {

	  cout << "Equation depends only on t1, need to handle this differently" << endl;
	  cout << "t1min:" << t1min << endl;
	  cout << "t1max:" << t1max << endl;
	  cout << "t2min:" << t2min << endl;
	  cout << "t2max:" << t2max << endl;
	  gen te = _solve(makesequence(eq,K.vars2d[1]),&ct);
	  cout << "te=" << te << endl;
	  exit(1);
	}
	*/

	//	cout << "Realminpol: " << K.realminpol << endl;
 	
	vecteur eqs_rur;
	eqs_rur.push_back(eq1);
	eqs_rur.push_back(eq2);
	eqs_rur.push_back(K.realminpol);

	if (verbose) {
	  cout << "K.vars2d: " << K.vars2d << endl;
	  cout << "eqs_rur: " << eqs_rur << endl;					   
	}

	//	cout << "Start rur" << endl;
	gen gb1 = _gbasis(makesequence(eqs_rur,K.vars2d,change_subtype(_RUR_REVLEX,_INT_GROEBNER)),&ct);
	//	cout << "Done rur" << endl;		      
			       
	if (gb1[0]==-4) {

	  //	  cout << "gb1=" << gb1 << endl;
	  gen varprov = lidnt(gb1[2])[0];
	  //  cout << "varprov=" << varprov << endl;
	  gb1 = _subst(makesequence(gb1,varprov,s__IDNT_e),&ct);
	  //	cout << "gb1=" << gb1 << endl;

	  vecteur valpars;
	  if (!K.safeSift(gb1,valpars)) {
	    cerr << "Problem sifting RUR values... (crit pts)" << endl;
	    exit(1);
	  }
	  else {
	    cout << "For eq#" << l << ", found " << valpars.size() << " critical point(s).. " << endl; 

	    if (verbose) {
	      cout << "gb1: " << gb1 << endl;
	      cout << "valpars: " << valpars << endl; 
	    }

	    //	    cout << "valpars: " << valpars << endl; 

	    for (int u = 0; u<valpars.size(); u++) {
	      
	      //	      cout << "Checking crit pt #" << u << "..  ";
	      //	      cout << "valpars[u]=" << valpars[u] << endl;
	      
	      //	      cout << "Horner" << endl;
	      gen t1vi = _horner(makesequence(gb1[5],valpars[u],s__IDNT_e),&ct)/_horner(makesequence(gb1[3],valpars[u],s__IDNT_e),&ct);
              gen t2vi = _horner(makesequence(gb1[6],valpars[u],s__IDNT_e),&ct)/_horner(makesequence(gb1[3],valpars[u],s__IDNT_e),&ct);
	      //	      cout << "Done with horner" << endl;

	      int stest;
	      if (!checkt1val(gb1,valpars[u],t1min,stest)) {
		cerr << "Trouble comparing with t1min" << endl;
		exit(1);
	      }
	      if (stest<0) {
		cout << "outside (t1<t1min)" << endl;
	      }
	      else {
		if (!checkt1val(gb1,valpars[u],t1max,stest)) {
		  cerr << "Trouble comparing with t1max" << endl;
		  exit(1);
		}
		if (stest>0) {
		  cout << "outside (t1>t1max)" << endl;
		}
		else {
		  if (!checkt2val(gb1,valpars[u],t2min,stest)) {
		    cerr << "Trouble comparing with t2min" << endl;
		    exit(1);
		  }
		  if (stest<0) {
		    cout << "outside (t2<t2min)" << endl;
		  }
		  else {
		    if (!checkt2val(gb1,valpars[u],t2max,stest)) {
		      cerr << "Trouble comparing with t2max" << endl;
		      exit(1);
		    }
		    if (stest>0) {
		      cout << "outside (t2>t2max)" << endl;
		    }
		    else {
		      if (!checkt3val(gb1,valpars[u],t3min,vl,wl,vt,stest)) {
			cerr << "Trouble comparing with t3min" << endl;
			exit(1);
		      }
		      if (stest<0) {
			cout << "outside (t3<t3min)" << endl;
		      }
		      else {
			if (!checkt3val(gb1,valpars[u],t3max,vl,wl,vt,stest)) {
			  cerr << "Trouble comparing with t3max" << endl;
			  exit(1);
			}
			if (stest>0) {
			  cout << "outside (t3>t3max)" << endl;
			}
			else {
			  if (!checkEqSign(gb1,valpars[u],eq,stest)) {
			    cerr << "Trouble checking sign of equation" << endl;
			    exit(1);
			  }
			  if (eq>0) {
			    cerr << "Equation is positive inside the G-polygon..." << endl;
			    cerr << "There seems to be an isolated component!!!!!!!" << endl;
			    exit(1);
			  }
			  else {
			    cout << "CRITICAL POINT IS INSIDE, but the equation is negative there!" << endl;
			  }
			}
		      }
		    }
		  }
		}
	      }
	    }	      
	  }
	}
	else {

	  if(! operator_equal(normal(gb1[0]-K.onenumber,&ct),K.zeronumber,&ct)) {// otherwise, groebner basis contains 1, no solution


	    cout << "Non isolated critical points? the system is not 0-dim" << endl;
	    // could either check if eqn for geodesics give (multiple) factors, or
	    //    use factorization over a number field, or factorization by hand... 
	    // For now, I choose the last option.

	    bool needmore = false;

	    gen co = _coeff(makesequence(eq,K.t2),&ct); 
	    int d2 = _degree(co,&ct).val;
	    
	    // eq may have lower degree in t2!!
	    if (d2==2) {
	      
	      gen co1 = _coeff(makesequence(eq,K.t1),&ct); 
	      int d1 = _degree(co1,&ct).val;
	      if (_degree(_coeff(makesequence(co[0],K.t1),&ct),&ct).val==d1 
		  && _degree(_coeff(makesequence(co[1],K.t1),&ct),&ct).val==d1 
		  && _degree(_coeff(makesequence(co[2],K.t1),&ct),&ct).val==d1 ) {
		cout << "a,b,c all have degree " << d1 << endl;
	      }
	      else {
		cerr << "Non-isolated critical point, unexpected behaviour" << endl;
		exit(1);
	      }
	      
	      // some of these may be 0, in which case I'm quite sure what follows is wrong!
	      // CHECK
	      gen a = _coeff(makesequence(co[0],K.t1,d1),&ct);
	      gen b = _coeff(makesequence(co[1],K.t1,d1),&ct);
	      gen c = _coeff(makesequence(co[2],K.t1,d1),&ct); 
	      
	      gen te1,te2;
	      te1 = _rem(makesequence(a*co[1]-b*co[0],K.realminpol,s__IDNT_e),&ct);
	      if (operator_equal(te1,K.zeronumber,&ct)) {
		te2 = _rem(makesequence(a*co[2]-c*co[0],K.realminpol,s__IDNT_e),&ct);
		if (operator_equal(te2,K.zeronumber,&ct)) {
		  gen discr = _rem(makesequence(b*b-4*a*c,K.realminpol,s__IDNT_e),&ct);
		  if (operator_equal(discr,K.zeronumber,&ct)) {
		    cout << "There is a square factor of the form (t2-r)^2" << endl;
		    gen r = -b*K.inverse(2*a);
		    K.red(r);
		    cout << "  r = " << r << endl;
		    int sg;
		    gen zz = r-t2min;
		    K.red(zz);
		    if (K.checkSign(zz,sg)){
		      if (sg>0) {
			cout << "r>t2min" << endl;
			zz = r-t2max;
			K.red(zz);
			if (K.checkSign(zz,sg)){
			  if (sg<0) {
			    cout << "r<t2max" << endl;
			    cout << "Horizontal line crosses the interior of the ridge!" << endl;
			    exit(1);
			  }
			}
			else {
			  cerr << "Trouble comparing r with t2max" << endl;
			  exit(1);
			}
		      }
		    }
		    else {
		      cerr << "Trouble comparing r with t2min" << endl;
		      exit(1);
		    }
		    
		    // need to check sign of co[0], check it is positive in [t1min,t1max]!
		    gen yo1 = _subst(makesequence(co[0],K.t1,t1min),&ct);
		    K.red(yo1);
		    if (K.checkSign(yo1,sg)) {
		      if (sg>0) {
			cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
			exit(1);
		      }
		      else {
			cout << "g(t1)==eq/(t2-r)^2 is <=0 at t1min" << endl;
			gen yo2 = _subst(makesequence(co[0],K.t1,t1max),&ct);
			K.red(yo2);
			//		    cout << "yo2=" << yo2 << endl;
			if (K.checkSign(yo2,sg)) {
			  if (sg>0) {
			    cout << "g(t1)==eq/(t2-r)^2 is <=0 at t1max" << endl;
			    cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
			    exit(1);
			  }
			  else {
			    // co[0] negative at enpoints, if degree two then still something to do!
			    cout << "co[0] is neg at endpoints, but need to check more" << endl;
			    cout << "co[0]=" << co[0] << endl;
			    gen de = _degree(makesequence(co[0],K.t1),&ct);
			    cout << "degree: " << de << endl;
			    if (operator_equal(de,K.twonumber,&ct)) {
			      gen aa = _coeff(makesequence(co[0],K.t1,2),&ct);
			      K.red(aa);
			      if (K.checkSign(aa,sg)) {
				if (sg<0) {
				  cout << "coeff of t1^2 is <0, will check tv = t1 of vertex of parabola" << endl;
				  gen tmax = _coeff(makesequence(co[0],K.t1,1),&ct)*K.inverse(2*_coeff(makesequence(co[0],K.t1,2),&ct));
				  K.red(tmax);
				  gen zzz = tmax-t1min;
				  K.red(zzz);
				  if (K.checkSign(zzz,sg)) {
				    if (sg>0) {
				      cout << "tv>t1min" << endl;
				      zzz = tmax-t1max;
				      K.red(zzz);
				      if (K.checkSign(zzz,sg)) {
					if (sg<0) {
					  cout << "tv<t1max" << endl;
					  gen val = _subst(makesequence(co[0],K.t1,tmax),&ct);
					  cout << "val=" << val << endl;
					  K.red(val);
					  if (K.checkSign(val,sg)) {
					    if (sg>0) {
					      cout << "Value at tv is > 0!!!" << endl;
					      cout << "Polytope does not seem embedded, sorry!" << endl;
					      exit(1);
					    }
					    else {
					      cout << "Value at tv is <= 0, OK." << endl;
					    }
					  }
					  else {
					    cerr << "Trouble checking sign at vertex of parabola." << endl;
					    exit(1);
					  }
					}
				      }
				      else {
					cerr << "Trouble comparing t-vertex with t1max." << endl;
					exit(1);
				      }
				    }
				  }
				  else {
				    cerr << "Trouble comparing t-vertex with t1min." << endl;
				    exit(1);
				  }
				}
			      }
			      else {
				cerr << "Trouble checking sign of coeff." << endl;
				exit(1);
			      }
			    }
			  }
			}
			else {
			  cerr << "Trouble checking sign of coeff at t1min" << endl;
			  exit(1);		  
			}
		      }
		    }
		    else {
		      cerr << "Trouble checking sign of coeff at t1min" << endl;
		      exit(1);		  
		    }
		  }
		  else {
		    needmore = true;
		  }
		}
		else {
		  needmore = true;
		}
	      }
	      else {
		needmore = true;
	      }
	      
	      if (needmore) {
		// start over with other variable!
		
		bool needevenmore = false;
		
		gen co = _coeff(makesequence(eq,K.t1),&ct);
		int d1 = _degree(co,&ct).val;
		
		if (d1==2) {
		  gen a = _coeff(makesequence(co[0],K.t2,2),&ct);
		  gen b = _coeff(makesequence(co[1],K.t2,2),&ct);
		  gen c = _coeff(makesequence(co[2],K.t2,2),&ct);
		  
		  gen te1,te2;
		  te1 = _rem(makesequence(a*co[1]-b*co[0],K.realminpol,s__IDNT_e),&ct);
		  if (operator_equal(te1,K.zeronumber,&ct)) {
		    te2 = _rem(makesequence(a*co[2]-c*co[0],K.realminpol,s__IDNT_e),&ct);
		    if (operator_equal(te2,K.zeronumber,&ct)) {
		      gen discr = _rem(makesequence(b*b-4*a*c,K.realminpol,s__IDNT_e),&ct);
		      if (operator_equal(discr,K.zeronumber,&ct)) {
			cout << "There is a square factor of the form (t1-r)^2" << endl;
			gen r = -b*K.inverse(2*a);
			K.red(r);
			cout << "  r = " << r << endl;
			int sg;
			gen zzzz = r-t1min;
			K.red(zzzz);
			if (K.checkSign(zzzz,sg)){
			  if (sg>0) {
			    zzzz = r-t1max;
			    K.red(zzzz);
			    if (K.checkSign(zzzz,sg)){
			      if (sg<0) {
				cout << "Vertical line crosses the interior of the ridge!" << endl;
				exit(1);
			      }
			    }
			    else {
			      cerr << "Trouble comparing r with t1max" << endl;
			      exit(1);
			    }
			  }
			}
			else {
			  cerr << "Trouble comparing r with t1min" << endl;
			  exit(1);
			}
			
			// need to check sign of co[0], check it is positive in [t1min,t1max]!
			gen yo1 = _subst(makesequence(co[0],K.t2,t2min),&ct);
			K.red(yo1);
			if (K.checkSign(yo1,sg)) {
			  if (sg>0) {
			    cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
			    exit(1);
			  }
			  else {
			    gen yo2 = _subst(makesequence(co[0],K.t2,t2max),&ct);
			    K.red(yo2);
			    if (K.checkSign(yo2,sg)) {
			      if (sg>0) {
				cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
				exit(1);
			      }
			      else {
				// co[0] negative at enpoints, if degree two then still something to do!
				gen de = _degree(makesequence(co[0],K.t2),&ct);
				if (operator_equal(de,K.twonumber,&ct)) {
				  gen aa = _coeff(makesequence(co[0],K.t2,2),&ct);
				  K.red(aa);
				  if (K.checkSign(aa,sg)) {
				    if (sg<0) {
				      gen tmax = _coeff(makesequence(co[0],K.t2,1),&ct)*K.inverse(2*_coeff(makesequence(co[0],K.t2,2),&ct));
				      K.red(tmax);
				      gen zzzzz = tmax-t2min;
				      K.red(zzzzz);
				      if (K.checkSign(tmax-t2min,sg)) {
					if (sg>0) {
					  zzzzz = tmax-t2max;
					  K.red(zzzzz);
					  //	    cout << "tmax-t2max=" << zzzzz << endl;				    
					  if (K.checkSign(tmax-t2max,sg)) {
					    if (sg<0) {
					      gen val = _subst(makesequence(co[0],K.t2,tmax),&ct);
					      K.red(val);
					      cout << "Val at vert: " << val << endl;
					      if (K.checkSign(val,sg)) {
						if (sg>0) {
						  cout << "Polytope does not seem embedded, sorry!" << endl;
						  exit(1);
						}
					      }
					      else {
						cerr << "Trouble checking sign at vertex of parabola." << endl;
						exit(1);
					      }
					    }
					  }
					  else {
					    cerr << "Trouble comparing t-vertex with t1max." << endl;
					    exit(1);
					  }
					}
				      }
				      else {
					cerr << "Trouble comparing t-vertex with t1min." << endl;
					exit(1);
				      }
				    }
				  }
				  else {
				    cerr << "Trouble checking sign of coeff." << endl;
				    exit(1);
				  }
				}
			      }
			    }
			    else {
			      cerr << "Trouble checking sign of coeff at t1min" << endl;
			      exit(1);		  
			    }
			  }
			}
			else {
			  cerr << "Trouble checking sign of coeff at t1min" << endl;
			  exit(1);		  
			}
			
			
			
		      }
		      else {
			needevenmore = true;
		      }
		    }
		    else {
		      needevenmore = true;
		    }
		  }
		  else {
		    needevenmore = true;
		  }
		  if (needevenmore) {
		    cerr << "Factors (t1-r)^2 or (t2-r)^2 did not explain not 0-dim" << endl;
		    cerr << "It may help to factor over number field, or use t3?" << endl;
		    exit(1);
		  }
		  
		}
		else {
		  
		  if (d1==1) {
		    
		    cout << "Equation has degree 1 in t1" << endl;
		    
		    exit(1);
		    
		  }
		  else {
		    
		    cout << "Equation does not contain t1!" << endl;
		    
		    exit(1);
		    
		  }
		  
		}	      
	      }
	    }
	    else { // d2!=2
	      
	      if (d2==1) {
		
		cout << "Equation has degree 1 in t2" << endl;
		
		int da = _degree(makesequence(co[0],K.t1),&ct).val;
		int db = _degree(makesequence(co[1],K.t1),&ct).val;
		
		cout << "co=" << co << endl;
		cout << "da=" << da <<  endl;
		cout << "db=" << db <<  endl;
		
		if (da==db) {
		  
		  gen a = _coeff(makesequence(co[0],K.t1,da),&ct);
		  gen b = _coeff(makesequence(co[1],K.t1,da),&ct);
		  // check degrees??
		  
		  cout << "a=" << a << endl;
		  cout << "b=" << b << endl;
		  
		  gen te1,te2;
		  te1 = _rem(makesequence(a*co[1]-b*co[0],K.realminpol,s__IDNT_e),&ct);
		  if (operator_equal(te1,K.zeronumber,&ct)) {
		    
		    cout << "There is a single (t2-r) factor" << endl;
		    
		    gen r = -b*K.inverse(a);
		    K.red(r);
		    
		    // equation looks like (t2-r)*p(t1), need to:
		    //   * check sign of p(t1) in the polygon 
		    //   * compare r with t2min and t2max
		    
		    cout << "  r = " << r << endl;
		    int sg;
		    gen zzzz = r-t2min;
		    K.red(zzzz);
		    if (K.checkSign(zzzz,sg)){
		      // could check sign of r-t2min
		      
		      if (sg>0) {
			// r>t2min
			
			zzzz = r-t2max;
			K.red(zzzz);
			if (K.checkSign(zzzz,sg)){
			  // could check sign of r-t2max
			  
			  if (sg<0) {
			    cout << "Vertical line crosses the interior of the ridge!" << endl;
			    exit(1);
			  }
			  else {
			    
			    // r>=t2max, so the t2-r factor is negative
			    // need to check that p(t1)>=0 on [t1min,t1max]
			    
			    
			    // CHECKING p>0
			    gen yo1 = _subst(makesequence(co[0],K.t1,t1min),&ct);
			    K.red(yo1);
			    if (K.checkSign(yo1,sg)) {
			      if (sg<0) {
				cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
				exit(1);
			      }
			      else {
				gen yo2 = _subst(makesequence(co[0],K.t1,t1max),&ct);
				K.red(yo2);
				if (K.checkSign(yo2,sg)) {
				  if (sg<0) {
				    cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
				    exit(1);
				  }
				  else {
				    // co[0] positive at enpoints, if degree two then still something to do!
				    gen de = _degree(makesequence(co[0],K.t1),&ct);
				    if (operator_equal(de,K.twonumber,&ct)) {
				      // otherwise nothing to do!
				      
				      gen aa = _coeff(makesequence(co[0],K.t1,2),&ct);
				      K.red(aa);
				      if (K.checkSign(aa,sg)) {
					if (sg>0) {
					  gen tmax = _coeff(makesequence(co[0],K.t1,1),&ct)*K.inverse(2*_coeff(makesequence(co[0],K.t1,2),&ct));
					  K.red(tmax);
					  gen zzzzz = tmax-t1min;
					  K.red(zzzzz);
					  if (K.checkSign(tmax-t1min,sg)) {
					    if (sg>0) {
					      zzzzz = tmax-t1max;
					      K.red(zzzzz);
					      if (K.checkSign(tmax-t1max,sg)) {
						if (sg<0) { // tmax is in the interval [t1min,t1max]
						  gen val = _subst(makesequence(co[0],K.t1,tmax),&ct);
						  K.red(val);
						  cout << "Val at vert: " << val << endl;
						  
						  if (K.checkSign(val,sg)) {
						    if (sg<0) {
						      cout << "Polytope does not seem embedded, sorry!" << endl;
						      exit(1);
						    }
						    else {
						      if (sg==0) {
							cout << "Equation cuts face in vertical line (tangency)!" << endl;
							cout << "Polytope does not seem embedded, sorry!" << endl;
							exit(1);
						      }
						    }
						  }
						  else {
						    cerr << "Trouble checking sign at vertex of parabola." << endl;
						    exit(1);
						  }
						}
					      }
					      else {
						cerr << "Trouble comparing t-vertex with t1max." << endl;
						exit(1);
					      }
					    }
					  }
					  else {
					    cerr << "Trouble comparing t-vertex with t1min." << endl;
					    exit(1);
					  }
					}
				      }
				      else {
					cerr << "Trouble checking sign of coeff." << endl;
					exit(1);
				      }
				    }
				  }
				  
				}
				else {
				  cerr << "Trouble checking sign at t1max" << endl;
				  exit(1);		  
				}
			      }
			    }
			    else {
			      cerr << "Trouble checking sign at t1min" << endl;
			      exit(1);		  
			    }			  
			  }
			  
			}
			else {
			  cerr << "Trouble comparing r with t2max" << endl;
			  exit(1);		  
			}
			
		      }
		      else {
			
			// r<=t2min, so the t2-r factor is positive
			// need to check that p(t1)<=0 on [t1min,t1max]		      
			
			
			// CHECKING SIGN OF p(t1)
			gen yo1 = _subst(makesequence(co[0],K.t1,t1min),&ct);
			K.red(yo1);
			if (K.checkSign(yo1,sg)) {
			  if (sg>0) {
			    cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
			    exit(1);
			  }
			  else {
			    gen yo2 = _subst(makesequence(co[0],K.t1,t1max),&ct);
			    K.red(yo2);
			    //     cout << "yo2=" << yo2 << endl;
			    if (K.checkSign(yo2,sg)) {
			      if (sg>0) {
				cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
				exit(1);
			      }
			      else {
				// co[0] negative at enpoints, if degree two then still something to do!
				gen de = _degree(makesequence(co[0],K.t1),&ct);
				if (operator_equal(de,K.twonumber,&ct)) {
				  gen aa = _coeff(makesequence(co[0],K.t1,2),&ct);
				  K.red(aa);
				  if (K.checkSign(aa,sg)) {
				    if (sg<0) {
				      gen tmax = _coeff(makesequence(co[0],K.t1,1),&ct)*K.inverse(2*_coeff(makesequence(co[0],K.t1,2),&ct));
				      K.red(tmax);
				      gen zzzzz = tmax-t1min;
				      K.red(zzzzz);
				      
				      if (K.checkSign(tmax-t1min,sg)) {
					if (sg>0) {
					  zzzzz = tmax-t1max;
					  K.red(zzzzz);
					  //	    cout << "tmax-t2max=" << zzzzz << endl;				    
					  
					  if (K.checkSign(tmax-t1max,sg)) {
					    if (sg<0) { // tmax is in the interval [t1min,t1max]
					      gen val = _subst(makesequence(co[0],K.t1,tmax),&ct);
					      K.red(val);
					      cout << "Val at vert: " << val << endl;
					      
					      if (K.checkSign(val,sg)) {
						if (sg>0) {
						  cout << "Polytope does not seem embedded, sorry!" << endl;
						  exit(1);
						}
						else {
						  if (sg==0) {
						    cout << "Equation cuts face in vertical line (tangency)!" << endl;
						    cout << "Polytope does not seem embedded, sorry!" << endl;
						    exit(1);
						  }
						}
					      }
					      else {
						cerr << "Trouble checking sign at vertex of parabola." << endl;
						exit(1);
					      }
					      
					    }
					  }
					  else {
					    cerr << "Trouble comparing t-vertex with t1max." << endl;
					    exit(1);
					  }
					  
					}
				      }
				      else {
					cerr << "Trouble comparing t-vertex with t1min." << endl;
					exit(1);
				      }
				    }
				  }
				  else {
				    cerr << "Trouble checking sign of coeff." << endl;
				    exit(1);
				  }
				}
			      }
			    }
			    else {
			      cerr << "Trouble checking sign of coeff at t1max" << endl;
			      exit(1);		  
			    }
			  }
			}
			else {
			  cerr << "Trouble checking sign of coeff at t1min" << endl;
			  exit(1);		  
			}
			// done with checking p(t1)<0
			
		      }
		      
		    }
		    else {
		      cerr << "Trouble comparing r with t2min" << endl;
		      exit(1);
		    }
		  }
		  else {
		    cerr << "Unexpected case, ask Martin to fix this (coeffs are not multiples of each other)!" << endl;
		    cerr << "  (degree in t2 is 1, but no t2=r factor)" << endl;
		    exit(1);
		  }
		}
		else {
		  
		  cout << "Degrees are not the same" << endl;
		  cout << "co[1]=" << co[1] << endl;
		  
		  if (is_zero(co[1],&ct)) {
		    
		    cout << "Term constant in t2 is 0" << endl;
		    gen r = K.zeronumber;
		    
		    // equation looks like t2*p(t1), need to:
		    //   * check sign of p(t1) in the polygon 
		    //   * compare 0 with t2min and t2max
		    
		    cout << "  r = 0 " << endl;
		    int sg;
		    gen zzzz = r-t2min;
		    K.red(zzzz);
		    if (K.checkSign(zzzz,sg)){
		      if (sg>0) {
			zzzz = r-t2max;
			K.red(zzzz);
			if (K.checkSign(zzzz,sg)){
			  if (sg<0) {
			    cout << "Vertical line crosses the interior of the ridge!" << endl;
			    exit(1);
			  }
			  else {
			    // r>=t2max, so the t2-r factor is negative
			    // need to check that p(t1)>=0 on [t1min,t1max]
			    
			  }
			}
			else {
			  cerr << "Trouble comparing r with t1max" << endl;
			  exit(1);
			}
		      }
		      else {
			// r<=t2min, so the t2-r factor is positive
			// need to check that p(t1)<=0 on [t1min,t1max]		      
			
		      }
		    }
		    else {
		      cerr << "Trouble comparing r with t1min" << endl;
		      exit(1);
		    }
		  }
		  else {
		    cerr << "Ask Martin to fix this!" << endl;
		    cerr << "  (degree in t2 is 1, but no t2=r factor)" << endl;
		    exit(1);		  
		  }
		}
	      }
	      else {
		
		cout << "Equation does not contain t2!" << endl;
		// equation looks like p(t1), need to check sign of p(t1) in [t1min,t1max]
		
		int sg;
		gen yo1 = _subst(makesequence(co[0],K.t1,t1min),&ct);
		K.red(yo1);
		if (K.checkSign(yo1,sg)) {
		  if (sg>0) {
		    cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
		    exit(1);
		  }
		  else {
		    gen yo2 = _subst(makesequence(co[0],K.t1,t1max),&ct);
		    K.red(yo2);
		    if (K.checkSign(yo2,sg)) {
		      if (sg>0) {
			cerr << "Equation seems to cut into the ridge, have you checked edges?" << endl;
			exit(1);
		      }
		      else {
			// co[0] negative at enpoints, if degree two then still something to do!
			gen de = _degree(makesequence(co[0],K.t1),&ct);
			if (operator_equal(de,K.twonumber,&ct)) {
			  gen aa = _coeff(makesequence(co[0],K.t1,2),&ct);
			  K.red(aa);
			  if (K.checkSign(aa,sg)) {
			    if (sg<0) {
			      gen tmax = _coeff(makesequence(co[0],K.t1,1),&ct)*K.inverse(2*_coeff(makesequence(co[0],K.t1,2),&ct));
			      K.red(tmax);
			      gen zzzzz = tmax-t1min;
			      K.red(zzzzz);
			      
			      if (K.checkSign(tmax-t1min,sg)) {
				if (sg>0) {
				  zzzzz = tmax-t1max;
				  K.red(zzzzz);
				  
				  if (K.checkSign(tmax-t1max,sg)) {
				    if (sg<0) { // tmax is in the interval [t1min,t1max]
				      gen val = _subst(makesequence(co[0],K.t1,tmax),&ct);
				      K.red(val);
				      cout << "Val at vert: " << val << endl;
				      
				      if (K.checkSign(val,sg)) {
					if (sg>0) {
					  cout << "Polytope does not seem embedded, sorry!" << endl;
					  exit(1);
					}
					else {
					  if (sg==0) {
					    cout << "Equation cuts face in vertical line (tangency)!" << endl;
					    cout << "Polytope does not seem embedded, sorry!" << endl;
					    exit(1);
					  }
					}
				      }
				      else {
					cerr << "Trouble checking sign at vertex of parabola." << endl;
					exit(1);
				      }
				      
				    }
				  }
				  else {
				    cerr << "Trouble comparing t-vertex with t1max." << endl;
				    exit(1);
				  }
				  
				}
			      }
			      else {
				cerr << "Trouble comparing t-vertex with t1min." << endl;
				exit(1);
			      }
			    }
			  }
			  else {
			    cerr << "Trouble checking sign of coeff." << endl;
			    exit(1);
			  }
			}
		      }
		    }
		    else {
		      cerr << "Trouble checking sign of coeff at t1max" << endl;
		      exit(1);		  
		    }
		  }
		}
		else {
		  cerr << "Trouble checking sign of coeff at t1min" << endl;
		  exit(1);		  
		}
		// done checking sign of p(t1)

	      }
	    }
	  }
	}
      }
    }
  }
  else {
    // use V0, W0, Z0
    cout << "For this ridge there is nothing to do, it is enough to check the 1-skeleton!" << endl;
  }
}

void Polytope::drawRidgePic(int i) {
  int j =  ridges[i].faceindices[0];
  int k =  ridges[i].faceindices[1];
  cout << "Trying to draw pic of ridge on faces #" << j << " and " << k << endl;
  cout << " (I will only do it provided this ridge is a complex disk)" << endl;

  bool genparam;
  gen X0 = BoxProd(faces[j].cspine,faces[k].cspine);
  gen V0, W0, Z0; // Orthogonal (but not orthonormal) basis, with V0, W0 spanning common slice
  if (operator_equal(X0,K.zerovector,&ct)) {
    // cospinal
    genparam = false;
    gen Xte = BoxProd(faces[j].mirror.coords,faces[k].mirror.coords);
    cout << "Same mirror?" << endl;
    cout << "Xte=" << Xte << endl;
    cout << "   " << K.evali(Xte) << endl;
    if (operator_equal(Xte,K.zerovector,&ct)) {
      cout << "Same mirror, this gives the common slice" << endl;
      V0 = faces[j].apexProjection;
      W0 = Proj(faces[j].bottomface[0].coords,V0);	
      Z0 = BoxProd(V0,W0); // not clear if we need this
    }
    else {
      Xte = BoxProd(faces[j].mirror.coords,faces[k].apex.coords);
      cout << "Mirror with top?" << endl;
      cout << "Xte=" << Xte << endl;
      cout << "   " << K.evali(Xte) << endl;
      if (operator_equal(Xte,K.zerovector,&ct)) {
	cout << "Mirror of face#" << j << " is same as top face of #" << k << endl;
	cout << "     this gives the common slice" << endl;	
	V0 = faces[j].apexProjection;
	W0 = Proj(faces[j].bottomface[0].coords,V0);
	Z0 = BoxProd(V0,W0); // not clear if we need this
      }
      else {
	Xte = BoxProd(faces[k].mirror.coords,faces[j].apex.coords);
	cout << "Top with mirror?" << endl;
	cout << "Xte=" << Xte << endl;
	cout << "   " << K.evali(Xte) << endl;
	if (operator_equal(Xte,K.zerovector,&ct)) {
	  cout << "Mirror of face#" << k << " is same as top face of #" << j << endl;
	  cout << "     this gives the common slice" << endl;	
	  V0 = faces[k].apexProjection;
	  W0 = Proj(faces[k].bottomface[0].coords,V0);
	  Z0 = BoxProd(V0,W0); // not clear if we need this
	}
	else {
	  Xte = BoxProd(faces[j].apex.coords,faces[k].apex.coords);
	  if (operator_equal(Xte,K.zerovector,&ct)) {
	    cout << "Top of face#" << k << " is same as top face of #" << j << endl;
	    cout << "     this gives the common slice" << endl;	
	    V0 = BoxProd(faces[j].cspine,faces[j].apex.coords);
	    W0 = Proj(faces[j].topface[0].coords,V0);
	    Z0 = BoxProd(V0,W0); // not clear if we need this
	  }
	  else {

	    cerr << "There is probably no common slice, will need to check this.." << endl;
	    cout << "There is probably no common slice, will need to check this.." << endl;

	    gen vj, wj, vk, wk;
	    
	    wj = faces[j].apexProjection;
	    vj = faces[j].apex.coords;
	    vj = Inn(wj,vj)*vj;
	    K.vectred(vj);
	    vj = Proj(vj,wj);
	    
	    gen vt = vj + K.t1*wj;
	    
	    // take vj+t*wj, and find t-values for this to be on face k
	    //   
	    gen eq = Inn(vt,faces[k].X0)*Inn(faces[k].X0,vt) - Inn(vt,faces[k].X1)*Inn(faces[k].X1,vt); 
	    K.red(eq);
	    eq = K.convertToReal(eq);
	    K.red(eq);
	    cout << "Equation to solve: " << eq << endl;
	    
	    gen eqro = eval(_subst(makesequence(eq,s__IDNT_e,K.realgenasrootof),&ct),1,&ct);
	    
	    cout << "eqro:" << eqro << endl;
	    cout << "realgenasrootof: " << K.realgenasrootof << endl;
	    cout << "   " << _evalf(K.realgenasrootof,&ct) << endl;
	    
	    gen fa = _factors(makesequence(eqro,K.realgenasrootof),&ct);
	    for (int l=0; 2*l<(*fa._VECTptr).size(); l++) {
	      cout << "fa[" << (2*l) << "]=" << fa[2*l] << endl;
	      int deg = _degree(makesequence(fa[2*l],K.t1),&ct).val;
	      if (deg>1) {
		cout << "Intersection of the real spines is not defined over the ambient number field" << endl;
		cout << "  (more programming to do!)" << endl;
		exit(1);
	      }
	      else {
		if (deg>0) {
		  gen te = normal(-_coeff(makesequence(fa[2*l],K.t1,0),&ct)/_coeff(makesequence(fa[2*l],K.t1,1),&ct),&ct);
		  if (verbose) {
		    cout << "te=" << te << endl;
		    cout << " (" << _evalf(te,&ct) << ")" << endl;
		  }
		}
	      }
	    }
	    
	    gen expr = SqNorm(vt);
	    K.red(expr);
	    expr = K.convertToReal(expr);
	    K.red(expr);
	    cout << "Then need to check sign of: " << expr << endl;

	  }
	}
      }
    }
  }
  else {
    // complex spines intersect in projective space, need to check if they intersect on the real spines

    if (isOnPrism(X0,faces[j])) {
      cout << "Extended real spine of face #" << j << " intersects extor #" << k << endl;
      if (isOnPrism(X0,faces[k])) {
	cout << "Extended real spine of face #" << k << " intersects extor #" << j << endl;
	cout << "Real spines intersect... "; //either cotranchal, or linear?
	int sx;
	if (!K.checkSign(SqNorm(X0),sx)) {
	  cerr << "Trouble computing sign of square norm (in checkRidge)" << endl;
	  exit(1);
	}
	else {
	  if (sx>0) {
	    cout << "Cotranchal pair" << endl;

	    gen Y1 = faces[j].apexProjection;
	    Y1 = Inn(X0,Y1)*Y1;
	    K.red(Y1);
	    Y1 = Proj(Y1,X0);
	    gen Y2 = faces[k].apexProjection;
	    Y2 = Inn(X0,Y2)*Y2;
	    K.red(Y2);
	    Y2 = Proj(Y2,X0);
	    gen tee = Inn(Y1,faces[j].X0)*Inn(faces[j].X0,Y1)-Inn(Y1,faces[j].X1)*Inn(faces[j].X1,Y1);
	    K.red(tee);
	    tee = Inn(Y2,faces[k].X0)*Inn(faces[k].X0,Y2)-Inn(Y2,faces[k].X1)*Inn(faces[k].X1,Y2);
	    K.red(tee);
	    gen tt = (Inn(Y1,Y2)+Inn(Y2,Y1))/2;
	    gen te = SqNorm(Y1)*SqNorm(Y2) - tt*tt;
	    K.red(te);
	    te = K.convertToReal(te);
	    int sgn;
	    if (!K.checkSign(te,sgn)) {
	      cerr << "Trouble checking sign (is intersection is slice only)" << endl;
	      exit(1);
	    }
	    else {
	      if (sgn<0) {
		cerr << "Intersection is not just the common slice!?" << endl;
		//		exit(1);
		genparam = false; // THIS IS NOT WHAT WE WANT, ONLY OK IF WE DON'T NEED TO CHECK EXTRA COMPONENT...
	      }
	      else {
		cout << "intersection is slice only!" << endl;
		genparam = false;
	      }
	    }
	      
	    V0 = X0;
	    if (K.areDep(X0,faces[j].apex.coords)) {
	      cout << "Common slice is top face of face #" << j << endl;
	      W0 = Proj(faces[j].topface[0].coords,V0);
	    }
	    else {
	      if (K.areDep(X0,faces[j].mirror.coords)) {
		cout << "Common slice is mirror of face #" << j << endl;
		W0 = Proj(faces[j].bottomface[0].coords,V0);
	      }
	      else {
		cerr << "Unidentified common slice, this is odd.." << endl;
		exit(1);
	      }
	    }
	    Z0 = BoxProd(V0,W0); // not clear if we need this
	    if (K.areDep(X0,faces[k].apex.coords)) {
	      cout << "Common slice is top face of face #" << k << endl;
	    }
	    else {
	      if (K.areDep(X0,faces[k].mirror.coords)) {
		cout << "Common slice is mirror of face #" << k << endl;
	      }
	      else {
		cerr << "Unidentified common slice, this is odd.." << endl;
		exit(1);
	      }
	    }
	  }
	  else {
	    if (sx==0) {
	      cerr << "Strange, real spines intersect at infinity..." << endl;
	      exit(1);
	    }
	    else {
	      //	      cerr << "Real spines intersect inside, linear intersection (not implemented yet)" << endl;
	      //  This is actually OK, the Giraud parametrization still works!
	      
	      if (K.areDep(faces[j].cspine,faces[k].cspine)) {
		cout << "Same complex spine!" << endl;
	      }
	      gen te = Inn(faces[j].cspine,faces[k].cspine);
	      if (is_zero(te,&ct)) {
		cout << "Orthogonal complex spines" << endl;
	      }

	      genparam = true;
	      //	      exit(1);
	    }
	  }
	}
      }	
      else {
	cerr << "Real spines don't intersect" << endl;
	genparam = true;
      }
    }
    else {
      cerr << "Real spines don't intersect" << endl;
      genparam = true;
    }
  }

  if (genparam) {
    cout << "This is a generic ridge, picture not implemented yet" << endl;
  }
  else {
    ofstream file;
    string jc = static_cast<ostringstream*>( &(ostringstream() << j) )->str();
    string kc = static_cast<ostringstream*>( &(ostringstream() << k) )->str();
    string filename = "pics/pic-ridge"+K.tag+"_"+jc+"-"+kc+".asy";
    file.open(filename.c_str());
    file << "import graph;" << endl;
    file << "size(7.5cm,0);" << endl;
    file << "draw(circle((0,0),1),red);" << endl;

    vector<int> inds = findFaces(V0);
    gen V0n = K.evali(V0);
    gen W0n = K.evali(W0);
    gen Hn = K.evali(H);

    gen X1 = K.evali(vertices[ridges[i].verts[0]].coords);
    gen X2 = K.evali(vertices[ridges[i].verts[3]].coords);
    X1 = X1/sqrt(-re(conj(X1,&ct)*Hn*X1,&ct),&ct);
    X2 = X2/sqrt(-re(conj(X2,&ct)*Hn*X2,&ct),&ct);
    gen yo = conj(X2,&ct)*Hn*X1;
    X2 = (-yo/abs(yo,&ct))*X2;
    gen MP = X1+X2;
    gen mpz = (-conj(MP,&ct)*Hn*W0n)/(conj(MP,&ct)*Hn*V0n);

    W0n = (conj(mpz,&ct)/abs(mpz,&ct))*W0n;
    V0n = V0n/sqrt(-re(conj(V0n,&ct)*Hn*V0n,&ct),&ct);
    W0n = W0n/sqrt(re(conj(W0n,&ct)*Hn*W0n,&ct),&ct);

    gen mpz2 = (-conj(MP,&ct)*Hn*W0n)/(conj(MP,&ct)*Hn*V0n);

    for (int u=0; u<faces.size(); u++) {
      if (u!=j && u!=k) {
	cout << "Working on face #" << u << endl;
	gen X0i = K.evali(faces[u].X0);
	gen X1i = K.evali(faces[u].X1);
	
	gen a = conj(X0i,&ct)*Hn*W0n;
	gen b = conj(X0i,&ct)*Hn*V0n;
	gen c = conj(X1i,&ct)*Hn*W0n;
	gen d = conj(X1i,&ct)*Hn*V0n;
	gen den = a*conj(a,&ct)-c*conj(c,&ct);
	
	den = re(den,&ct);

	if (is_greater(K.zeronumber,_left(den,&ct),&ct) && is_greater(_right(den,&ct),K.zeronumber,&ct)) {
	  cerr << "Interval for denominator contains 0, maybe a line?" << endl;	  
	}
	else {
	  gen ctr = (conj(c,&ct)*d-conj(a,&ct)*b)/den;
	  cout << "ctr=" << ctr << endl;
	  gen radsq = re( ctr*conj(ctr,&ct) , &ct ) - re( b*conj(b,&ct)-d*conj(d,&ct) , &ct )/den;
	  if (is_greater(_left(radsq,&ct),K.zeronumber,&ct)) {
	    gen rad = sqrt(radsq,&ct);
	    cout << "rad = " << rad << endl;
	    if (is_greater(1000,rad,&ct)) {
	      file << "draw(circle(("<<_left(re(ctr,&ct),&ct)<<","<<_left(im(ctr,&ct),&ct)<<"),"<<_left(rad,&ct)<<"),black);" << endl;
	      gen tee = ctr*(1-rad/abs(ctr,&ct));
	      file << "label(\"$" << u << "$\",(" << re(tee,&ct) << "," << im(tee,&ct) << "),black+fontsize(8));" << endl;	      
	    }
	  }
	  else {
	    if (is_greater(K.zeronumber,_right(radsq,&ct),&ct)) {
	      cout << "No intersection, radius of circle is negative" << endl;
	    }
	    else {
	      cout << "Doubtful intersection, the interval for radius contains 0" << endl;
	      cout << "Will simply draw nothing..." << endl;
	    }
	  }
	}
      } 
    }
    // then draw the geodesic polygon through vertices...
    cout << "Drawing polygon" << endl;
    vector<gen> verts_num;
    gen ii = gen("exp(pi*i/2)",&ct);
    gen fa = gen("180/pi",&ct);
    for (int u=0; u<ridges[i].verts.size(); u++) {
      gen X = K.evali(vertices[ridges[i].verts[u]].coords);
      gen rat = -(conj(X,&ct)*Hn*W0n)/(conj(X,&ct)*Hn*V0n);
      verts_num.push_back(_left(re(rat,&ct),&ct)+ii*_left(im(rat,&ct),&ct));
    }
    int si = verts_num.size();

    file << "path pol = ";
    for (int u=0; u<si; u++) {
      gen alp = verts_num[u];
      gen bet = verts_num[(u+1)%si];

      gen den = conj(alp,&ct)*bet - alp*conj(bet,&ct);
      gen ctr = (alp*(alp*conj(alp,&ct)-bet*conj(bet,&ct)) + (bet-alp)*(1+alp*conj(alp,&ct)))/den;

      gen rad = sqrt(ctr*conj(ctr,&ct)-1,&ct);
      gen u1 = alp-ctr;
      gen u3 = bet-ctr;
      gen th1 = arg(u1,&ct)*fa;
      gen th2 = arg(u3,&ct)*fa;
      if (is_greater(th2,th1,&ct)) {
	if (is_greater(th2-th1,180,&ct)) {
	  th2 = th2-360;
	}
      }
      else {
	if (is_greater(th1-th2,180,&ct)) {
	  th1 = th1-360;
	}
      }
      file << "arc(("<<re(ctr,&ct)<<","<<im(ctr,&ct)<<"),"<<rad<<","<<th1<<","<<th2<<")--" << endl;      
    } 
    file << "cycle;" << endl;
    file << "filldraw(pol,gray);" << endl;
    file << "limits((-1.1,-1.1),(1.1,1.1),Crop);";
    file.close();
  }
  
}

bool Polytope::checkt1val(gen rur, gen par, gen t1v, int &res) {
  gen par_refined;
  if (checkt1valAttempt(rur,par,t1v,res)) {
    return true;
  }
  else {
    if (checkEqualt1val(rur,par,t1v)) {
      res = 0;
      return true;
    }
    else {
      int savend = K.nd;
      bool done = false;
      while (K.nd<K.ndmax && !done) {
	// compute par_refined?
	gen sols =_realroot(makesequence(rur[2],K.prec),&ct);
	int count = 0;
	int ksave;
	for (int k=0; k<(*sols._VECTptr).size(); k++) {
	  gen rt = convert_interval(sols[k][0],K.nd,&ct);    
	  /*  gen rt;
	  if (sols[k][0].type==8) {
	    rt = convert_interval(sols[k][0],K.nd,&ct);      
	  }
	  else {
	    rt = sols[k][0];
	    }*/
	  //	  cout << "par=" << par << endl;
	  gen Jt = _intersect(makesequence(rt,par),&ct);
	  //	  cout << "Jt.type==3? " << (Jt.type==3) << endl;
	  //	  gen Jt = _intersect(makesequence(sols[k][0],par),&ct);
	  // changed nov 21, 2019 because for some trivial equations, realroots are not intervals...
	  if (Jt.type==3) {
	    par_refined = rt;
	    count++;
	    ksave = k;
	  }
	}
	if (count!=1) {
	  cout << "Could not identify par from rur (count=" << count << ")" << endl;
	  return false;
	}
	//	else {
	//	  par_refined = sols[ksave][0];
	//	}
	//	cout << "par_refined=" << par_refined << endl;
	done = checkt1valAttempt(rur, par_refined, t1v, res);
	if (!done) {
	  K.increasePrecision();
	}
      }
      K.nd = savend;
      K.lowerPrecision();
      return done;
    }
  }
}

bool Polytope::checkt1valAttempt(gen rur, gen par, gen t1v, int &res) {
  gen t1vi = K.evali(t1v);
  gen t1_from_rur = _horner(makesequence(rur[5],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);

  t1_from_rur = convert_interval(t1_from_rur,K.nd,&ct);      

  // added next line to fix m6-1%3...
  //  t1_from_rur = convert_interval(t1_from_rur,K.nd,&ct);

  //  cout << "t1vi=" << t1vi << endl;
  //  cout << "t1_from_rur=" << t1_from_rur << endl;
  // in some case the solutions are integers, then the result is not an interval...
  if (operator_equal(t1_from_rur,K.zeronumber,&ct)) {
      t1_from_rur = convert_interval(K.onenumber,K.nd,&ct)-K.onenumber;
  }
  if (is_greater(_left(t1vi,&ct),_right(t1_from_rur,&ct),&ct)) {
    res = -1;
    return true;
  }
  else {
    if (is_greater(_left(t1_from_rur,&ct),_right(t1vi,&ct),&ct)) {
      res = 1;
      return true;
    }
    else {
      return false;
    }
  }
}

bool Polytope::checkEqualt1val(gen rur, gen par, gen t1v) {
  //  cout << "checkEqualt1val" << endl;
  gen te = _subst(makesequence(t1v,s__IDNT_e,rur[4]/rur[3]),&ct);
  te = _simplify(te - rur[5]/rur[3],&ct);
  te = _numer(te,&ct);
  //  cout << "te=" << te << endl;
  //  cout << "rur[2]=" << rur[2] << endl;
  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);
  //  cout << "rem:" << te << endl;
  if (operator_equal(te,K.zeronumber,&ct)) {
    return true;
  }
  else {
    // need to check if par is a root of te!
    gen g = _gcd(makesequence(te,rur[2]),&ct);
    //   cout << "gcd=" << g << endl;
    gen sols = _realroot(makesequence(g,K.prec),&ct);
    //   cout << "sols=" << sols << endl;
    //  gen sols = _realroot(makesequence(te,K.prec),&ct);
    //  int count = 0;
    bool fdd = false;
    int ku = 0;
    while (!fdd && ku<(*sols._VECTptr).size()) {
      //   for (int k=0; k<(*sols._VECTptr).size(); k++) {
      gen rt = convert_interval(sols[ku][0],K.nd,&ct); 
      /*     gen rt;
      if (sols[ku][0].type==8) {
	rt = convert_interval(sols[ku][0],K.nd,&ct);      
      }
      else {
	rt = sols[ku][0];
	}*/
      gen Jt = _intersect(makesequence(rt,par),&ct);
      //      if (Jt.type==3) {
      //	count++;
      //      }
      fdd = Jt.type==3;
      ku++;
    }
    /*    if (count==0) {
      return false;
    }
    else {
      if (count==1) {
	return true;
      }
      else {
	cout << "Intervals are not isolating, will need to work harder" << endl;
	exit(1);
      }
      }*/
    return fdd;
  }
}

bool Polytope::checkt2val(gen rur, gen par, gen t2v, int &res) {
  //  cout << "checkt2val" << endl;
  gen par_refined;
  if (checkt2valAttempt(rur,par,t2v,res)) {
    return true;
  }
  else {
    if (checkEqualt2val(rur,par,t2v)) {
      res = 0;
      return true;
    }
    else {
      int savend = K.nd;
      bool done = false;
      while (K.nd<K.ndmax && !done) {
	// compute par_refined?
	gen sols =_realroot(makesequence(rur[2],K.prec),&ct);
	int count = 0;
	int ksave;
	for (int k=0; k<(*sols._VECTptr).size(); k++) {
	  gen rt = convert_interval(sols[k][0],K.nd,&ct);
	  /*	  gen rt;
	  if (sols[k][0].type==8) {
	    rt = convert_interval(sols[k][0],K.nd,&ct);      
	  }
	  else {
	    rt = sols[k][0];
	    }*/
	  gen Jt = _intersect(makesequence(rt,par),&ct);
	  if (Jt.type==3) {
	    count++;
	    ksave = k;
	  }
	}
	if (count!=1) {
	  cout << "Could not identify par from rur (count=" << count << ")" << endl;
	  return false;
	}
	else {
	  par_refined = sols[ksave][0];
	}
	done = checkt2valAttempt(rur, par_refined, t2v, res);
	if (!done) {
	  K.increasePrecision();
	}
      }
      K.nd = savend;
      K.lowerPrecision();
      return done;
    }
  }
}

bool Polytope::checkt2valAttempt(gen rur, gen par, gen t2v, int &res) {
  //  cout << "checkt2valAttempt" << endl;
  gen t2vi = K.evali(t2v);
  gen t2_from_rur = _horner(makesequence(rur[6],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);

  t2_from_rur = convert_interval(t2_from_rur,K.nd,&ct);      
    
    // in some case the solutions are integers, then the result is not an interval...
  //  cout << "t2_from_rur before trick: " << t2_from_rur << endl;
  if (operator_equal(t2_from_rur,K.zeronumber,&ct)) {
      t2_from_rur = convert_interval(K.onenumber,K.nd,&ct)-K.onenumber;
  }
  //  cout << "t2vi=" << t2vi << endl;
  //  cout << "t2_from_rur=" << t2_from_rur << endl;

  if (is_greater(_left(t2vi,&ct),_right(t2_from_rur,&ct),&ct)) {
    res = -1;
    return true;
  }
  else {
    if (is_greater(_left(t2_from_rur,&ct),_right(t2vi,&ct),&ct)) {
      res = 1;
      return true;
    }
    else {
      return false;
    }
  }
}

bool Polytope::checkEqualt2val(gen rur, gen par, gen t2v) {
  //  cout << "checkEqualt2val" << endl;
  gen te = _subst(makesequence(t2v,s__IDNT_e,rur[4]/rur[3]),&ct);
  te = _simplify(te - rur[6]/rur[3],&ct);
  te = _numer(te,&ct);
  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);
  if (operator_equal(te,K.zeronumber,&ct)) {
    return true;
  }
  else {
    // need to check if par is a root of te!
    gen g = _gcd(makesequence(te,rur[2]),&ct);
    gen sols = _realroot(makesequence(g,K.prec),&ct);
    //    gen sols = _realroot(makesequence(te,K.prec),&ct);
    //   int count = 0;
    bool fd = false;
    int k = 0;
    while (!fd && k<(*sols._VECTptr).size()) {
      //  for (int k=0; k<(*sols._VECTptr).size(); k++) {
      //      cout << "sols[" << k << "][0]=" << sols[k][0] << endl;
      gen rt = convert_interval(sols[k][0],K.nd,&ct);
      /*  gen rt;
      if (sols[k][0].type==8) {
	rt = convert_interval(sols[k][0],K.nd,&ct);      
      }
      else {
	rt = sols[k][0];
	}*/
      gen Jt = _intersect(makesequence(rt,par),&ct);
      /*      if (Jt.type==3) {
	count++;
	}*/
      fd = Jt.type==3;
      k++;
    }
    /*    if (count==0) {
      return false;
    }
    else {
      if (count==1) {
	return true;
      }
      else {
	cout << "Intervals are not isolating, will need to work harder" << endl;
	exit(1);
      }
      }*/
    return fd;
  }
}

bool Polytope::checkt3val(gen rur, gen par, gen t3v, gen vl, gen wl, gen vt, int &res) {
  /*  cout << "checkt3val" << endl;
  cout << "par=" << par << endl;
  cout << "t3v=" << t3v << endl;
  cout << "vl=" << vl << endl;
  cout << "wl=" << wl << endl;
  cout << "vt=" << vt << endl;*/
  gen par_refined;
  if (checkt3valAttempt(rur,par,t3v,vl,wl,vt,res)) {
    return true;
  }
  else {
    if (checkEqualt3val(rur,par,t3v,vl,wl,vt)) {
      res = 0;
      return true;
    }
    else {
      int savend = K.nd;
      bool done = false;
      while (K.nd<K.ndmax && !done) {
	gen sols =_realroot(makesequence(rur[2],K.prec),&ct);
	int count = 0;
	int ksave;
	for (int k=0; k<(*sols._VECTptr).size(); k++) {
	  //  gen rt = convert_interval(sols[k][0],K.nd,&ct);      
	  // not sure what type==8 means, and why the next conditional statement...
	  gen rt = convert_interval(sols[k][0],K.nd,&ct);      
	  /*	  gen rt;
	  if (sols[k][0].type==8) {
	    rt = convert_interval(sols[k][0],K.nd,&ct);      
	  }
	  else {
	    rt = sols[k][0];
	    }*/
	  gen Jt = _intersect(makesequence(rt,par),&ct);
	  if (Jt.type==3) {
	    count++;
	    ksave = k;
	  }
	}
	if (count!=1) {
	  cout << "Could not identify par from rur (count=" << count << ")" << endl;
	  return false;
	}
	else {
	  par_refined = convert_interval(sols[ksave][0],K.nd,&ct);
	}
	done = checkt3valAttempt(rur, par_refined, t3v,vl,wl,vt,res);
	if (!done) {
	  K.increasePrecision();
	}
      }
      K.nd = savend;
      K.lowerPrecision();
      return done;
    }
  }
}

bool Polytope::checkt3valAttempt(gen rur, gen par, gen t3v, gen vl, gen wl, gen vt, int &res) {

  //  cout << "checkt3valattempt" << endl;
  //  cout << "par=" << par << endl;
  
  // Changed this line on Nov 28, 2019, looks like a very silly typo
  gen svvi = _horner(makesequence(rur[4],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);
  //  gen svvi = _horner(makesequence(rur[5],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);
  gen t1vi = _horner(makesequence(rur[5],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);
  gen t2vi = _horner(makesequence(rur[6],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);

  // in some case the solutions are integers, then the result is not an interval...
  if (operator_equal(t1vi,K.zeronumber,&ct)) {
      t1vi = convert_interval(K.onenumber,K.nd,&ct)-K.onenumber;
  }
  if (operator_equal(t2vi,K.zeronumber,&ct)) {
      t2vi = convert_interval(K.onenumber,K.nd,&ct)-K.onenumber;
  }
  
  gen alp = -Inn(vt,vl);
  gen bet = Inn(vt,wl);
  alp = (alp*K.myconj(bet)+K.myconj(alp)*bet)/2;
  bet = bet*K.myconj(bet);
  K.red(alp);
  K.red(bet);
  alp = K.convertToReal(alp);
  bet = K.convertToReal(bet);

  //  cout << "alp=" << alp << endl;
  //  cout << "bet=" << bet << endl;
  //  cout << "svvi=" << svvi << endl;
  
  alp = _subst(makesequence(alp,s__IDNT_e,svvi),&ct);
  alp = _subst(makesequence(alp,K.t1,t1vi),&ct);
  alp = _subst(makesequence(alp,K.t2,t2vi),&ct);

  //  cout << "alp=" << alp << endl;

  bet = _subst(makesequence(bet,s__IDNT_e,svvi),&ct);
  bet = _subst(makesequence(bet,K.t1,t1vi),&ct);
  bet = _subst(makesequence(bet,K.t2,t2vi),&ct);

  //  cout << "bet=" << bet << endl;

  gen t3_from_rur = alp/bet;

  gen t3vi = K.evali(t3v);

  if (is_greater(_left(t3vi,&ct),_right(t3_from_rur,&ct),&ct)) {
    res = -1;
    return true;
  }
  else {
    if (is_greater(_left(t3_from_rur,&ct),_right(t3vi,&ct),&ct)) {
      res = 1;
      return true;
    }
    else {
      //      cout << "t3_from_rur=" << t3_from_rur << endl;
      //      cout << "t3vi=" << t3vi << endl;
      return false;
    }
  }
}

bool Polytope::checkEqualt3val(gen rur, gen par, gen t3v, gen vl, gen wl, gen vt) {

  //  cout << "checkEqualt3val" << endl;
  //  cout << "par=" << par << endl;
  
  gen alp = -Inn(vt,vl);
  gen bet = Inn(vt,wl);
  alp = (alp*K.myconj(bet)+K.myconj(alp)*bet)/2;
  bet = bet*K.myconj(bet);
  K.red(alp);
  K.red(bet);
  alp = K.convertToReal(alp);
  bet = K.convertToReal(bet);

  //  cout << "alp=" << alp << endl;
  //  cout << "bet=" << bet << endl;

  alp = _simplify(_subst(makesequence(alp,s__IDNT_e,rur[4]/rur[3]),&ct),&ct);
  alp = _simplify(_subst(makesequence(alp,K.t1,rur[5]/rur[3]),&ct),&ct);
  alp = _simplify(_subst(makesequence(alp,K.t2,rur[6]/rur[3]),&ct),&ct);
  
  bet = _simplify(_subst(makesequence(bet,s__IDNT_e,rur[4]/rur[3]),&ct),&ct);
  bet = _simplify(_subst(makesequence(bet,K.t1,rur[5]/rur[3]),&ct),&ct);
  bet = _simplify(_subst(makesequence(bet,K.t2,rur[6]/rur[3]),&ct),&ct);

  //  cout << "after subst" << endl;
  //  cout << "alp=" << alp << endl;
  //  cout << "bet=" << bet << endl;

  //  cout << "t3v=" << t3v << endl;
  gen t3v_rur = _simplify(_subst(makesequence(t3v,s__IDNT_e,rur[4]/rur[3]),&ct),&ct);
  gen t3v_num = _numer(t3v_rur,&ct);
  gen t3v_den = _denom(t3v_rur,&ct);
  t3v_num = _rem(makesequence(t3v_num,rur[2],s__IDNT_e),&ct);
  t3v_den = _rem(makesequence(t3v_den,rur[2],s__IDNT_e),&ct);

  //  cout << "t3v_rur: " << t3v_rur << endl;
  //  cout << "t3v_num: " << t3v_num << endl;
  //  cout << "t3v_den: " << t3v_den << endl;
  
  gen te = alp*t3v_den - bet*t3v_num;
  gen te_num = _numer(te,&ct);
  gen te_den = _denom(te,&ct);
  
  //  cout << "te_num=" << te_num << endl;
  //  cout << "te_den=" << te_den << endl;

  te_num = _rem(makesequence(te_num,rur[2],s__IDNT_e),&ct);
  te_den = _rem(makesequence(te_den,rur[2],s__IDNT_e),&ct);

  /*  cout << "after rem:" << endl;
  cout << "te_num=" << te_num << endl;
  cout << "te_den=" << te_den << endl;*/

  te = te_num;
  
  //  cout << "te before rem: " << te << endl;
  //  gen te =alp/bet - _subst(makesequence(t3v,s__IDNT_e,rur[4]/rur[3]),&ct);
  //  cout << "te before factor" << te << endl;
  //  te = _factor(te,&ct);
  //  cout << "te after factor: " << te << endl;
  //  te = _numer(te,&ct);
  //  cout << "te after numer: " << te << endl;
  //  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);
  //  cout << "te after rem: " << te << endl;
  
  if (operator_equal(te,K.zeronumber,&ct)) {
    return true;
  }
  else {
    // need to check if par is a root of te!
    gen g = _gcd(makesequence(te,rur[2]),&ct);
    //    cout << "te=" << te << endl;
    //   cout << "rur[2]=" << rur[2] << endl;
    //  cout << "gcd=" << g << endl;
    gen sols = _realroot(makesequence(g,K.prec),&ct);
    //    gen sols = _realroot(makesequence(te,K.prec),&ct);
    //    int count = 0;
    bool fd = false;
    int k = 0;
    while (!fd && k<(*sols._VECTptr).size()) {
      //    for (int k = 0; k<(*sols._VECTptr).size(); k++) { 
      gen rt = convert_interval(sols[k][0],K.nd,&ct); 
      //      cout << "rt=" << rt << endl;
      /*   gen rt;
      if (sols[k][0].type==8) {
	rt = convert_interval(sols[k][0],K.nd,&ct);      
      }
      else {
	rt = sols[k][0];
	}*/
      gen Jt = _intersect(makesequence(rt,par),&ct);
      fd = Jt.type==3;
      /*      if (Jt.type==3) {
	count++;
	}*/
      k++;
    }
    /*    if (count==0) {
      return false;
    }
    else {
      if (count==1) {
	return true;
      }
      else {
	cout << "Intervals are not isolating, will need to work harder" << endl;
	exit(1);
      }
      }*/
    return fd;
  }
}


bool Polytope::checkEqSign(gen rur, gen par, gen eq, int &res) {
  //  cout << "checkEqSign" << endl;
  gen par_refined;
  if (checkEqSignAttempt(rur,par,eq,res)) {
    return true;
  }
  else {
    if (checkEqZero(rur,par,eq)) {
      res = 0;
      return true;
    }
    else {
      //   cout << "Eq is not zero" << endl;
      int savend = K.nd;
      bool done = false;
      while (K.nd<K.ndmax && !done) {
	//	cout << "Will use realroot... " << endl;
	gen sols =_realroot(makesequence(rur[2],K.prec),&ct);
	//	cout << "  ... done realroot" << endl;
	int count = 0;
	int ksave;
	for (int k=0; k<(*sols._VECTptr).size(); k++) {
	  //	  cout << "using root #" << k << endl;
	  gen rt = convert_interval(sols[k][0],K.nd,&ct);
	  /*	  gen rt;
	  if (sols[k][0].type==8) {
	    rt =  convert_interval(sols[k][0],K.nd,&ct);     
	  }
	  else {
	    rt = sols[k][0];
	    }*/
	  gen Jt = _intersect(makesequence(rt,par),&ct);
	  if (Jt.type==3) {
	    count++;
	    ksave = k;
	  }
	}
	if (count!=1) {
	  cout << "Could not identify par from rur (count=" << count << ")" << endl;
	  return false;
	}
	else {
	  par_refined = sols[ksave][0];
	}
	done = checkEqSignAttempt(rur,par_refined,eq,res);
	if (!done) {
	  //	  cout << "Increasing precision" << endl;
	  K.increasePrecision();
	}
      }
      K.nd = savend;
      K.lowerPrecision();
      //      cout << "done with checkeqsign" << endl;
      return done;
    }
  }
}

bool Polytope::checkEqSignAttempt(gen rur, gen par, gen eq, int &res) {
  gen svvi = _horner(makesequence(rur[5],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);
  gen t1vi = _horner(makesequence(rur[5],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);
  gen t2vi = _horner(makesequence(rur[6],par,s__IDNT_e),&ct)/_horner(makesequence(rur[3],par,s__IDNT_e),&ct);

  gen eq_eval = _subst(makesequence(eq,s__IDNT_e,svvi),&ct);
  eq_eval = _subst(makesequence(eq_eval,K.t1,t1vi),&ct);
  eq_eval = _subst(makesequence(eq_eval,K.t2,t2vi),&ct);

  if (is_greater(K.zeronumber,_right(eq_eval,&ct),&ct)) {
    res = -1;
    return true;
  }
  else {
    if (is_greater(_left(eq_eval,&ct),K.zeronumber,&ct)) {
      res = 1;
      return true;
    }
    else {
      return false;
    }
  }
}

bool Polytope::checkEqZero(gen rur, gen par, gen eq) {

  if (verbose) {
    cout << "checkEqZero" << endl;
    cout << "eq=" << eq << endl;
    cout << "rur=" << rur << endl;
  }
  
  gen te = _subst(makesequence(eq,s__IDNT_e,rur[4]/rur[3]),&ct);
  te = _numer(te,&ct);
  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);

  if (verbose) {
    cout << "te after subs s: " << te << endl;
  }
  
  te = _subst(makesequence(te,K.t1,rur[5]/rur[3]),&ct);
  te = _numer(te,&ct);
  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);

  if (verbose) {
    cout << "te after subs t1: " << te << endl;
  }
  
  te = _subst(makesequence(te,K.t2,rur[6]/rur[3]),&ct);
  te = _numer(te,&ct);
  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);

  if (verbose) {
    cout << "te after subs t2: " << te << endl;
  }
  
  //  te = giac::expand(te,&ct);
  //  te = _numer(te,&ct);
  //  te = _rem(makesequence(te,rur[2],s__IDNT_e),&ct);

  if (operator_equal(te,K.zeronumber,&ct)) {
    return true;
  }
  else {
    // need to check if par is a root of te!

    //    cout << "te=" << te << endl;
    //    cout << "rur[2]=" << rur[2] << endl;

    gen g = _gcd(makesequence(te,rur[2]),&ct);
    //   [the gcd is useless??]

    //    cout << "g=" << g << endl;
    
    gen sols = _realroot(makesequence(g,K.prec),&ct);
    //    gen sols = _realroot(makesequence(te,K.prec),&ct);

    //    cout << "roots: " << sols << endl;
    //    int count = 0;
    bool fd = false;
    int k = 0;
    //    for (int k=0; k<(*sols._VECTptr).size(); k++) {
    while (!fd && k<(*sols._VECTptr).size()) {
      gen rt = convert_interval(sols[k][0],K.nd,&ct); 
      /*   gen rt;
      if (sols[k][0].type==8) {
	rt = convert_interval(sols[k][0],K.nd,&ct);      
      }
      else {
	rt = sols[k][0];
	}*/
      gen Jt = _intersect(makesequence(rt,par),&ct);
      /*      if (Jt.type==3) {
	count++;
	}*/
            fd = Jt.type==3;
            k++;
    }
    //    cout << "result: " << fd << endl; 
    /*    if (count==0) {
      return false;
    }
    else {
      if (count==1) {
	return true;
      }
      else {
	cout << "Intervals are not isolating, will need to work harder" << endl;
	exit(1);
      }
      }*/
    return fd;
  }
}

void Polytope::importFaceData() {

  string filename = "faceData/faceData_"+K.tag+".chg";
  ifstream infile(filename.c_str());

  while (infile)
    {
      int n;
      infile >> n;
      cout << "[" << n << "]-gon" << endl;

      if (infile) {

	vector<int> wm;
	string line;
	getline(infile,line);
	istringstream issm(line);
	copy(istream_iterator<int>(issm),istream_iterator<int>(),back_inserter(wm));
	cout << "Mirror: " << convertword(wm) << endl;
      }

      for (int k=0; k<n; k++) {
	if (infile) {
	  string s;
	  infile >> s;
	  cout << s << endl;
	} 
      }
    }

}

void Polytope::exportFaceData() {

  ofstream file;
  string filename = "faceData/faceData_"+K.tag+".chg";
  file.open(filename.c_str());
  for (int k=0; k<faces.size(); k++) {
    file << faces[k].sides.size() << endl;
    file << faces[k].mirror.word << endl;
    for (int l=0; l<faces[k].sides.size(); l++) {
      file << faces[k].sides[l].word << endl;
    }
  }
  file.close();

  cout << "Done exporting face data" << endl;

}

void Polytope::checkLinks() {

  for (int k=0; k<vertexPOrbits.size(); k++) {
    int kk = vertexPOrbits[k][0];
    cout << "Studying link for vertex #" << kk << endl;

    WVector V = vertices[kk];
    int e0 = 0;
    int e1 = 0;
    int e2 = 0;

    vector<int> incident_edges;
    for (int l=0; l<edges.size(); l++) {
      if (edges[l].origin==kk || edges[l].end==kk) {
	incident_edges.push_back(l);
      }
    }
    e0 = incident_edges.size();
    if (verbose) {
      cout << "e0=" << e0 << endl;
    }
    
    for (int l=0; l<ridges.size(); l++) {
      int m = -1;
      bool isvert = false;
      while (!isvert && m!=ridges[l].verts.size()-1) {
	m++;
	isvert = ridges[l].verts[m]==kk;
      }
      if (isvert) {
	e1++;
      }
    }
    if (verbose) {
      cout << "e1=" << e1 << endl;
    }
    
    vector<vector<int> > twofaces_in_link; // Note these could be DIGONS!
                                           // I think this is not a problem, digons do not affect orientation?
    for (int u=0; u<vertices[kk].faceindices.size(); u++) {
      if (vertices[kk].faceindices[u]!=-1) {
	vector<int> twoface, ordered_twoface;
	for (int v=0; v<incident_edges.size(); v++) {
	  vector<int> fi = edges[incident_edges[v]].faceindices;
	  if (find(fi.begin(),fi.end(),vertices[kk].faceindices[u])!=fi.end()) {
	    twoface.push_back(incident_edges[v]);
	  }
	}
	if (verbose) {
	  cout << "Unordered 2-face: " << convertword(twoface) << endl;
	}
	
	ordered_twoface.push_back(twoface[0]);
	twoface.erase(twoface.begin());
	bool foundneighb = false;
	int st1 = -1;
	while (!foundneighb && st1!=twoface.size()-1) {
	  st1++;
	  foundneighb = intersect(edges[twoface[0]].faceindices,edges[twoface[st1]].faceindices).size()!=0;
	}
	if (!foundneighb) {
	  cerr << "Trouble finding neighbor in 2-face of link" << endl;
	  exit(1);
	}
	else {
	  ordered_twoface.push_back(twoface[st1]);
	  twoface.erase(twoface.begin()+st1);
	  while (twoface.size()>0) {
	    foundneighb = false;
	    int t = -1;
	    while (!foundneighb && t!=twoface.size()-1) {
	      t++;
	      foundneighb = ( intersect(edges[ordered_twoface[ordered_twoface.size()-1]].faceindices,edges[twoface[t]].faceindices).size()!=0 );
	    }
	    if (!foundneighb) {
	      cerr << "Trouble finding neighbor in 2-face of link" << endl;
	      exit(1);
	    }
	    else {
	      ordered_twoface.push_back(twoface[t]);
	      twoface.erase(twoface.begin()+t);
	    }
	  }
	}
	if (ordered_twoface.size()>2) {
	  twofaces_in_link.push_back(ordered_twoface);
	}
	else {
	  cout << "Ignoring digon, irrelevant for orientation check" << endl;
	}
	if (verbose) {
	  cout << "Ordered 2-face: " << convertword(ordered_twoface) << endl;
	}
      }
    }
    if (verbose) {
      cout << "Ordered 2-faces:"  << endl;
      for (int uu=0; uu<twofaces_in_link.size(); uu++) {
	cout << convertword(twofaces_in_link[uu]) << endl;
      }
    }
    // then need to check if we can orient them coherently!
    
    vector<int> list;
    for (int uu=0; uu<twofaces_in_link.size(); uu++) {
      list.push_back(uu);
    }
    vector<vector<int> > well_oriented_twofaces;
    
    bool is_orientable = true;
    while (is_orientable && list.size()>0) {
      
      if (verbose) {
	cout << " trying to find a 2-face that neighbors a well oriented one" << endl;
      }
      bool isneighbor = false;
      int zz=-1;
      vector<int> common;
      while (!isneighbor && zz!=well_oriented_twofaces.size()-1) {
	zz++;
	vector<int> common = intersect(twofaces_in_link[0],well_oriented_twofaces[zz]); 
	isneighbor = common.size()==2;
      }
      if (!isneighbor) {
	list.erase(list.begin());
	if (verbose) {
	  cout << "list=" << list << endl;
	}
      }
      else {
	cout << " found a neighbor, will orient it well with that neighbor, then check compatibility" << endl;

	bool or1, or2;
	int uuu=-1;
	bool fdcommon = false;
	while (!fdcommon && uuu!=twofaces_in_link[0].size()-1) {
	  uuu++;
	  fdcommon = common[0]==twofaces_in_link[0][uuu];
	}
	if (!fdcommon) {
	  cerr << "Trouble finding common" << endl;
	  exit(1);
	}
	else {
	  or1 = (common[1]==twofaces_in_link[0][(uuu+1)%twofaces_in_link[0].size()]);
	}
	int uuuu=-1;
	fdcommon = false;
	while (!fdcommon && uuuu!=well_oriented_twofaces[zz].size()-1) {
	    uuuu++;
	    fdcommon = common[0]==well_oriented_twofaces[zz][uuuu];
	}
	if (!fdcommon) {
	  cerr << "Trouble finding common" << endl;
	  exit(1);
	}
	else {
	  or2 = (common[1]==well_oriented_twofaces[zz][(uuuu+1)%well_oriented_twofaces[zz].size()]);
	}
	if (or1==or2) {
	  cout << " switch orientation" << endl;
	  // switch orientation!
	  twofaces_in_link[0] = flipword(twofaces_in_link[0]);
	}
       
	bool is_coherent;
	for (int y=0; y<well_oriented_twofaces.size(); y++) {	  
	  vector<int> common = intersect(twofaces_in_link[0],well_oriented_twofaces[y]); 
	  if (common.size()==2) {
	    // locate common in both 2faces?
	    bool or1, or2;
	    int uuu=-1;
	    bool fdcommon = false;
	    while (!fdcommon && uuu!=twofaces_in_link[0].size()-1) {
	      uuu++;
	      fdcommon = common[0]==twofaces_in_link[0][uuu];
	    }
	    if (!fdcommon) {
	      cerr << "Trouble finding common" << endl;
	      exit(1);
	    }
	    else {
	      or1 = (common[1]==twofaces_in_link[0][(uuu+1)%twofaces_in_link[0].size()]);
	    }
	    int uuuu=-1;
	    fdcommon = false;
	    while (!fdcommon && uuuu!=well_oriented_twofaces[y].size()-1) {
	      uuuu++;
	      fdcommon = common[0]==well_oriented_twofaces[y][uuuu];
	    }
	    if (!fdcommon) {
	      cerr << "Trouble finding common" << endl;
	      exit(1);
	    }
	    else {
	      or2 = (common[1]==well_oriented_twofaces[y][(uuuu+1)%well_oriented_twofaces[y].size()]);
	    }
	    if (or1==or2) {
	      cerr << "This vertex link is not orientable!!" << endl;
	      exit(1);
	    }
	  }
	}
	twofaces_in_link.erase(twofaces_in_link.begin());
      }
    }

    if (vertices[kk].faceindices[0]==-1) {
      e2 = vertices[kk].faceindices.size()-1;
    }
    else {
      e2 = vertices[kk].faceindices.size();
    }
    if (verbose) {
      cout << "e2=" << e2 << endl;
    }
    
    gen ee = (e0-e1+e2);
    if (!operator_equal(ee,K.twonumber,&ct)) {
      cerr << "Euler characteristic of vertex: " << ee << ", this is not a sphere!" << endl;
      exit(1);
    }
  }
  
  cout << endl;
  cout << "Great! Vertex links are spheres!" << endl;
  cout << "The boundary of the polytope is a manifold." << endl;
  cout << endl;

}

void Polytope::checkSphere() {
  
  vector<vector<int> > graph_edges;
  for (int uu=0; uu<edges.size(); uu++) {
    vector<int> e;
    e.push_back(edges[uu].origin);
    e.push_back(edges[uu].end);
    graph_edges.push_back(e);
  }
  sgraph G(graph_edges);
      
  vector<vector<int> > stabgens;
  vector<vector<bool> > orientations;
  G.fundamental_group(0,stabgens,orientations);

  cout << "1-skeleton has " << vertices.size() << " vertices, " << edges.size() << " edges" << endl;
  cout << "Found " << G.generators.size() << " gens for pi_1 of 1-skeleton" << endl;

  vector<vector<int> > ridgerels;
  vector<vector<bool> > ridgeorients;
  for (int u=0; u<ridges.size(); u++) {
    vector<int> rel;
    vector<bool> orient;
    int nv = ridges[u].verts.size();
    for (int v=0; v<nv; v++) {
      int jo = ridges[u].verts[v%nv];
      int ko = ridges[u].verts[(v+1)%nv];
      bool fd = false;
      bool flipped;
      int w = -1;
      while (!fd && w!=edges.size()-1) {
	w++;
	fd = (jo==edges[w].origin && ko==edges[w].end);
	if (fd) {
	  flipped = false;
	}
	else {
	  fd = (jo==edges[w].end && ko == edges[w].origin);
	  if (fd) {
	    flipped = true;
	  }
	}
      }
      if (fd) {
	rel.push_back(w);
	orient.push_back(flipped);
      }
      else {
	cerr << "Problem identifying edge in ridge" << endl;
	exit(1);
      }
    }
    ridgerels.push_back(rel);
    ridgeorients.push_back(orient);
  }

  G.export_fg_with_relations(0,K.tag,ridgerels,ridgeorients);
  cout << "  (you should check that that group is trivial by simplifying it with GAP)" << endl;
  cout << endl;

}

string Polytope::checkSignature(gen A) {
  // this only makes sense provided A is a Hermitian matrix!
 
  gen de = K.det(A);
  K.red(de);
  de = K.convertToReal(de);
  int sgn_de;
  if (!K.checkSign(de,sgn_de)) {
    cerr << "Trouble checking signature of the Hermitian form" << endl;
    exit(1);
  }
  else {
    if (sgn_de<0) {
      return "-++";
      // this only works for sporadic/triangle group Hermitian forms!
      //   (because by construction there are some positive vectors, so signature cannot be ---)
    }
    else {
      gen sn = A[0][0]*A[1][1] - A[0][1]*A[1][0];
      K.red(sn);
      sn = K.convertToReal(sn);
      int sgn_sn;
      if (!K.checkSign(sn,sgn_sn)) {
	cerr << "Trouble checking signature of the Hermitian form (doing Gramm-Schmidt)" << endl;
	exit(1);
      }
      else {
	if (sgn_sn>0) {
	  return "+++";
	}
	else {
	  if (sgn_sn<0) {
	    return "--+";
	  }
	  else {
	    cerr << "Trouble computing signature" << endl;
	    exit(1);
	  }
	}
      }
    }
  }
}

bool Polytope::checkTraceField(gen &res) {

  // so far this only works for Mostow and Sporadic groups!!

  gen sigma;
  cout << endl;

  if (is_symmetric) {
    // don't want to take the cube for mostow groups
    if (K.tag[0]=='m') {
      //      cout << "tag=" << K.tag << endl;
      //   cout << "K.taugen: " << K.taugen << endl;
      //     cout << "taugen: " << taugen << endl;
      sigma = taugen;
    }
    else {
      sigma = normal(taugen,&ct);
      //      cout << "taugen=" << sigma << endl;
      gen s2 = normal(sigma*sigma,&ct);
      //      cout << "taugen^2=" << s2 << endl;
      sigma = normal(s2*sigma,&ct);
      //     cout << "taugen^3=" << sigma << endl;
    }
  }
  else {
    sigma = normal(K.T[0]*K.T[1]*K.T[2],&ct);    
  }

  sigma = eval(sigma,&ct);
  cout << "sigma=" << sigma << endl;
  cout << "  (" << _evalf(sigma,&ct) << ")" << endl;
  
  cout << "Will now compute an upper bound for the degree of adjoint trace field.." << endl;
  cout << endl;
  cout << "tau^3=" << sigma << endl;
  /*  cout << "tau^3, eval=" << eval(sigma,&ct) << endl;
  cout << "tau^3, simplify eval=" << _simplify(eval(sigma,1,&ct),&ct) << endl;
  cout << "evalf(tau^3)=" << _evalf(sigma,&ct) << endl;*/
  
  gen pm = _pmin(eval(sigma,1,&ct),&ct);
  // changes nov 22 2019, playing with m3-1%6
  //gen pm = _pmin(normal(eval(sigma,1,&ct),&ct),&ct);
  //  gen pm = _pmin(normal(_eval(sigma,&ct),&ct),&ct);
  cout << "pm=" << pm << endl;

  gen sigminpol = giac::expand(_poly2symb(makesequence(pm,x__IDNT_e),&ct),&ct);
  if (verbose) {
    cout << "Minimal polynomial of tau^3: " << sigminpol << endl;  
  }
  
  int dt3 = _degree(makesequence(sigminpol,x__IDNT_e),&ct).val; 
  if (dt3==1) {
    
    gen zeta = eval(gen("exp(2*pi*i/"+print_INT_(pval)+")",&ct),1,&ct);
    gen zeta_mp = giac::expand(_poly2symb(makesequence(_pmin(zeta,&ct),x__IDNT_e),&ct),&ct);
    if (verbose) {
      cout << "Minimal polynomial of exp(2*pi*i/p): " << zeta_mp << endl;
    }
    sigminpol = zeta_mp;
 
  }
  else {

    if (operator_equal(_coeff(makesequence(sigminpol,x__IDNT_e,dt3),&ct),-K.onenumber,&ct)) {
      sigminpol = - sigminpol;
    }
    
    //    if (verbose) {
      cout << "sigminpol " << sigminpol << endl;
      // }
    
    gen compol = simplify(eval(gen("x^2+1",&ct),&ct)-sigminpol,&ct);
    
    if (!operator_equal(compol,K.zeronumber,&ct)) {

      gen st("k",&ct);
      gen ro = rootof(sigminpol,&ct); // this will often not be the same as tau^3
      sto(st,ro,&ct);
      
      gen zeta = _simplify(gen("exp(2*pi*i/"+print_INT_(pval)+")",&ct),&ct);
      gen zeta_mp = giac::expand(_poly2symb(makesequence(_pmin(zeta,&ct),z__IDNT_e),&ct),&ct);

      cout << "Minimal polynomial of exp(2*pi*i/p): " << zeta_mp << endl;
      gen fa = _factors(makesequence(zeta_mp,ro),&ct);
      
      for (int l=0; 2*l<(*fa._VECTptr).size(); l++) {
	
	int deg = _degree(makesequence(fa[2*l],z__IDNT_e),&ct).val;
	if (deg>1) {
	
	  gen te = _simplify(eval(_subst(makesequence(fa[2*l],z__IDNT_e,zeta),&ct),1,&ct),&ct);
	
	  if (operator_equal(te,K.zeronumber,&ct)) {
	    // then split this factor!
	    if (verbose) {
	      cout << "Split this factor: " << fa[2*l] << endl;
	    }
	    vecteur coe;
	    for (int uu=deg; uu>=0; uu--) {
	      gen teuu = _coeff(makesequence(fa[2*l],z__IDNT_e,uu),&ct);
	      if (verbose) {
		cout << "teuu=" << teuu << endl;
	      }
	      if (contains(teuu,st)) {
		if (verbose) {
		  cout << "Contains k!" << endl;
		}
		vecteur coe_u;
		int subdeg = _degree(makesequence(teuu,st),&ct).val;
		for (int vv=subdeg; vv>=0; vv--) {
		  gen teuu_vv = _coeff(makesequence(teuu,st,vv),&ct);
		  coe_u.push_back(teuu_vv);
		}
		if (verbose) {
		  cout << "coe_u=" << coe_u << endl;
		}
		coe.push_back(giac::expand(_poly2symb(makesequence(coe_u,x__IDNT_e),&ct),&ct));
	      }
	      else {
		if (teuu.type==8) {
		  if (teuu.is_symb_of_sommet(at_prod)) {
		    gen yoo = _poly2symb(makesequence(_simplify(teuu[1][1]*teuu[2],&ct),x__IDNT_e),&ct);
		    coe.push_back(yoo);
		  }
		  else {
		    if (teuu.is_symb_of_sommet(at_rootof)) {
		      gen yoo = _poly2symb(makesequence(_simplify(teuu[1],&ct),x__IDNT_e),&ct);
		      coe.push_back(yoo);
		    }
		    else {
		      cout << "teuu=" << teuu << endl;
		      cout << "Problem reading factors!" << endl;
		    }
		  }	 
		}
		else {
		  coe.push_back(teuu);
		}
	      }
	    }
	    if (verbose) {
	      cout << "coe=" << coe << endl;
	    }
	    vecteur eqs;
	    eqs.push_back(sigminpol);
	    eqs.push_back(giac::expand(_poly2symb(makesequence(coe,a__IDNT_e),&ct),&ct));
	    if (verbose) {
	      cout << "eqs: " << eqs << endl;
	    }
	    vecteur vars;
	    vars.push_back(x__IDNT_e);
	    vars.push_back(a__IDNT_e);
	    if (verbose) {
	      cout << "vars: " << vars << endl;
	    }
	    gen gb = _gbasis(makesequence(eqs,vars,change_subtype(_RUR_REVLEX,_INT_GROEBNER)),&ct);
	    if (gb[0]==-4)  {
	      gen varprov = lidnt(gb[2])[0];
	      sigminpol = _subst(makesequence(gb[2],varprov,x__IDNT_e),&ct);
	      int ds = _degree(makesequence(sigminpol,x__IDNT_e),&ct).val;
	      if (operator_equal( _coeff(makesequence(sigminpol,x__IDNT_e,ds),&ct), -K.onenumber, &ct)) {
		sigminpol = _simplify(-sigminpol,&ct);		
	      }
	    }
	    else {
	      cerr << "Trouble computing primitive element" << endl;
	      exit(1);
	    }
	  }
	}
      }
    }
    else {
      cout << "Need alternate method, sto(i) is not allowed" << endl;
      
      gen l = _lcm(makesequence(4*K.onenumber,pval),&ct);
      cout << "LCM=" << l << endl;
      
      sigminpol = giac::expand(_poly2symb(makesequence(_cyclotomic(l,&ct),x__IDNT_e),&ct),&ct);

      if (verbose) {
	cout << "sigminpol: " << sigminpol << endl;
      }
    }
  }    
   
  if (verbose) {
    cout << "sigminpol=" << sigminpol << endl;
  }
  int maxdeg = _degree(makesequence(sigminpol,x__IDNT_e),&ct).val;
  if (maxdeg%2!=0) {
    cerr << "Odd degree for complex field?! This should not happen..." << endl;
    exit(1);
  }

  maxdeg = maxdeg/2;
  cout << endl;
  cout << "Degree of adjoint trace field is at most " << maxdeg << endl;

  cout << "K.minpol: " << K.minpol << endl;
  cout << "K.realminpol: " << K.realminpol << endl;
  cout << "K.realgen: " << K.realgen << endl;
  cout << "K.realgenasrootof: " << K.realgenasrootof << endl;
  
  vecteur li, traces;
  vector<vector<int> > wo, woall;

  li.push_back(K.Id);
  vector<int> wo0;
  wo.push_back(wo0);
  woall.push_back(wo0);

  traces.push_back(K.trace(K.Id)*K.trace(K.Id));

  bool done = false;
  // this could be improved, we should stop whenever we have found a generator!
  while (li.size()<100) {
    int n = li.size();
    for (int l=0; l<n; l++) {
     
      vector<int> wop1;
      wop1.push_back(1);
      wop1 = concat(wop1,woall[l]);
      woall.push_back(wop1);
      gen M = R1*li[l];
      K.matred(M);
      li.push_back(M);
      gen tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wop1);
      }

      vector<int> wom1;
      wom1.push_back(-1);
      wom1 = concat(wom1,woall[l]);
      woall.push_back(wom1);
      M = R1i*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wom1);
      }

      vector<int> wop2;
      wop2.push_back(2);
      wop2 = concat(wop2,woall[l]);
      woall.push_back(wop2);
      M = R2*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wop2);
      }

      vector<int> wom2;
      wom2.push_back(-2);
      wom2 = concat(wom2,woall[l]);
      woall.push_back(wom2);
      M = R2i*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wom2);
      }

      vector<int> wop3;
      wop3.push_back(3);
      wop3 = concat(wop3,woall[l]);
      woall.push_back(wop3);
      M = R3*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wop3);
      }

      vector<int> wom3;
      wom3.push_back(-3);
      wom3 = concat(wom3,woall[l]);
      woall.push_back(wom3);
      M = R3i*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wom3);
      }

      vector<int> wop4;
      wop4.push_back(4);
      wop4 = concat(wop4,woall[l]);
      woall.push_back(wop4);
      M = J*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wop4);
      }

      vector<int> wom4;
      wom4.push_back(-4);
      wom4 = concat(wom4,woall[l]);
      woall.push_back(wom4);
      M = Ji*li[l];
      K.matred(M);
      li.push_back(M);
      tr = K.trace(M);
      tr = tr*K.myconj(tr);
      K.red(tr);
      tr = K.convertToReal(tr);
      if (_degree(makesequence(tr,s__IDNT_e),&ct).val>0 && !K.isInList(tr,traces)) {
	traces.push_back(tr);
	wo.push_back(wom4);
      }

    }
  }
  cout << "Values of |tr|^2 found " << endl;
  for (int uu=1; uu<wo.size(); uu++) {
    cout << convertword(wo[uu]) << ": " << traces[uu] << endl;
  }

  bool found = false;
  vecteur goodtraces;
  for (int k=0; k<traces.size(); k++) {
    gen yo = _subst(makesequence(traces[k],s__IDNT_e,K.realgenasrootof),&ct);
    yo = _simplify(yo,&ct);
    if (contains(yo,gen("j",&ct))) {
      yo = _subst(makesequence(yo,gen("j",&ct),K.genasrootof),&ct);
    }

    gen pmt = _pmin(yo,&ct);

    int de = _degree(pmt,&ct).val;
    if (de>maxdeg) {
      cerr << "Found an element with degree higher than upper bound?!" << endl;
      exit(1);
    }
    else {
      if (de==maxdeg) {
	goodtraces.push_back(traces[k]);
      }
    }
  }

  if (maxdeg>1 && goodtraces.size()==0) {
    cerr << "I found no trace that generates the adjoint trace field" << endl;
    return false;
  }
  else {
    cout << "good traces: " << goodtraces << endl;

    cout << "The adjoint trace field is equal to the upper bound, it has degree " << maxdeg << endl;
    if (maxdeg==1) {
      cout << "It is generated by 1" << endl;
      res = K.onenumber;
      return true;
    }
    else {
      bool found;
      int k=-1;
      while (!found && k!=goodtraces.size()-1) {
	k++;
	found = operator_equal(goodtraces[k],s__IDNT_e,&ct);
      }
      if (found) {
	cout << "It is generated by s" << endl;
	res = s__IDNT_e;
      }
      else {
	int dmin = K.dr.val;
	for (int uu=0; uu<goodtraces.size(); uu++) {
	  int de = _degree(makesequence(goodtraces[uu],s__IDNT_e),&ct).val;
	  if (de<dmin) {
	    dmin = de;
	    res = goodtraces[uu];
	  }
	}
	cout << "It is generated by " << res << endl;
	cout << "   (" << K.evali(res) << ")" << endl;
	cout << _simplify(_subst(makesequence(res,s__IDNT_e,K.realgenasrootof),&ct),&ct) << endl;
	//	cout << "It is generated by " << goodtraces[0] << endl;
	//	cout << _simplify(_subst(makesequence(goodtraces[0],s__IDNT_e,K.realgenasrootof),&ct),&ct) << endl;
      }
      return true;
    }
    cout << endl;
  }
  

}

bool Polytope::isArithmeticAlt() {

  gen gentf;
  if (!checkTraceField(gentf)){
    cerr << "I could not compute the adjoint trace field, cannot determine arithmeticity" << endl;
    exit(1);
  }

  cout << "gentf=" << gentf << endl;

  vecteur other_roots;

  gen fa = _factors(makesequence(_subst(makesequence(K.minpol,x__IDNT_e,a__IDNT_e),&ct),K.genasrootof),&ct);

  int nf = (*fa._VECTptr).size()/2;

  int k=0;
  bool found = false;
  while (k<nf && !found) {
    gen df = _degree(makesequence(fa[2*k],a__IDNT_e),&ct);
    if (df==1) {

      gen te = _simplify(-_coeff(makesequence(fa[2*k],a__IDNT_e,0),&ct)/_coeff(makesequence(fa[2*k],a__IDNT_e,1),&ct),&ct);

      if (contains(te,gen("j",&ct))) {
	int dete = _degree(makesequence(te,j__IDNT_e),&ct).val;
	vecteur coe;
	for (int uu=dete; uu>=0; uu--){
	  coe.push_back(_coeff(makesequence(te,j__IDNT_e,uu),&ct));
	}
	gen yoo = giac::expand(_poly2symb(makesequence(coe,x__IDNT_e),&ct),&ct);
	other_roots.push_back(yoo);
      }
      else {
	if (te.is_symb_of_sommet(at_prod)) {
	  gen yoo = giac::expand(_poly2symb(makesequence(_simplify(te[1][1]*te[2],&ct),x__IDNT_e),&ct),&ct);
	  other_roots.push_back(yoo);
	}
	else {
	  if (te.is_symb_of_sommet(at_rootof)) {
	    gen yoo = giac::expand(_poly2symb(makesequence(_simplify(te[1],&ct),x__IDNT_e),&ct),&ct);
	    other_roots.push_back(yoo);
	  }
	  else {
	    cout << "te=" << te << endl;
	    cout << "Problem reading factors!" << endl;
	  }
	}
      }
    }
    k++;
  }

  cout << "Done computing Galois conjugates of field generator" << endl;
  
  vecteur x_images;
  x_images.push_back(x__IDNT_e);
  vecteur gentf_images;
  gentf_images.push_back(gentf);
  for (int k=0; k<other_roots.size(); k++) {
    gen te = _subst(makesequence(gentf,s__IDNT_e,_subst(makesequence(K.realgen,x__IDNT_e,other_roots[k]),&ct)),&ct);
    cout << "te before reduction: " << te << endl;
    K.red(te);
    //    cout << "te=" << te << endl;
    gen ste = K.convertToReal(te);
    //   cout << "ste=" << ste << endl;

    bool isnew = true;
    int l=0;
    while (isnew && l<gentf_images.size()) {
      isnew = !operator_equal(gentf_images[l],ste,&ct);
      l++;
    }
    if (isnew) {
      gentf_images.push_back(ste);
      x_images.push_back(other_roots[k]);
    }
  }
  cout << endl;
  cout << "Done computing images" << endl;
  if (verbose) {
    cout << "Images of complex generator: " << x_images << endl;
    cout << "Corresponding images of adjoint trace field generator: " << gentf_images << endl;
  }

  gentf_images.erase(gentf_images.begin());
  x_images.erase(x_images.begin());

  cout << "Signature of (non trivial) Galois conjugates?" << endl;

  bool is_arithmetic = true;
  for (int u=0; u<x_images.size(); u++) {
    gen Hu = _subst(makesequence(H,x__IDNT_e,x_images[u]),&ct);
    K.matred(Hu);

    string si = checkSignature(Hu);
    cout << si << endl;
    if (si=="-++" || si=="--+") {
      is_arithmetic=false;
    }
  }

  if (is_arithmetic) {
    cout << "Sorry, this group is arithmetic." << endl;
  }
  else {
    cout << "The group is NOT arithmetic!" << endl;
  }
  
  isarithmeticitychecked = true;
  isarithmetic = is_arithmetic;
  return is_arithmetic;

}

bool Polytope::identify_in_field(gen x0, gen pol, gen &res) {

  gen den = _denom(x0,&ct);
  
  gen x = normal(x0*den,&ct);

  gen mpx = giac::expand(_poly2symb(makesequence(_pmin(eval(x,&ct),&ct),a__IDNT_e),&ct),&ct);
  if (verbose) {
    cout << "min pol (identify_in_field): " << mpx << endl;
  }
  
  if (_degree(makesequence(mpx,a__IDNT_e),&ct).val==1) {
    res = x0;
    return true;
  }
  else {
    gen gg = rootof(pol,&ct);
     gen sta = gen("b",&ct);
    sto(gg,sta,&ct);

    gen fad = _factors(makesequence(mpx,gg),&ct);

    bool found = false;
    int k = 0;
    int nk = (*fad._VECTptr).size()/2; 
    while (!found && k<nk) {

      if (verbose) {
	cout << "factor: " << fad[2*k] << endl;
      }
      int dfd = _degree(makesequence(fad[2*k],a__IDNT_e),&ct).val;
      if (verbose) {
	cout << "dfd=" << dfd << endl;
      }
      if (dfd!=1) {
	cerr << "Trouble computing determinant as an element of the trace field" << endl;
	//	cout << "k=" << k << endl;
	return false;
      }
      gen te = normal(-_subst(makesequence(fad[2*k],a__IDNT_e,K.zeronumber),&ct)/_diff(makesequence(fad[2*k],a__IDNT_e),&ct),&ct);
      gen tec = normal(eval(te-x,&ct),&ct);      
      found = operator_equal(tec,K.zeronumber,&ct);
      if (found) {
	if (verbose) {
	  cout << "Found: " << te << endl;
	}
	// Found as a rootof, need to express it as a polynomial b...

	if (contains(te,gen("b",&ct))) {
	  if (verbose) {
	    cout << "using b in computation of coeffs (det)" << endl;
	  }
	  int dete = _degree(makesequence(te,b__IDNT_e),&ct).val;
	  vecteur coe;
	  for (int uu=dete; uu>=0; uu--){
	    coe.push_back(_coeff(makesequence(te,b__IDNT_e,uu),&ct));
	  }
	  res = giac::expand(_poly2symb(makesequence(coe,b__IDNT_e),&ct),&ct)/den;
	  return true;
	}
	else {
	  if (te.is_symb_of_sommet(at_prod)) {
	    if (verbose) {
	      cout << "at_prod" << endl;
	    }
	    res = giac::expand(_poly2symb(makesequence(_simplify(te[1][1]*te[2],&ct),b__IDNT_e),&ct),&ct)/den;
	    return true;
	  }
	  else {
	    if (te.is_symb_of_sommet(at_rootof)) {
	      if (verbose) {
		cout << "at_rootof" << endl;
	      }
	      res = giac::expand(_poly2symb(makesequence(_simplify(te[1],&ct),b__IDNT_e),&ct),&ct)/den;
	      return true;
	    }
	    else {
	      cout << "te=" << te << endl;
	      cout << "Problem reading factors!" << endl;
	      return false;;
	    }
	  }
	}
      }
      k++;
    }
    if (!found) {
      cout << "Trouble identifying in trace field" << endl;
      return false;
    }
  }

  
}


bool Polytope::isArithmetic() {

  gen gentf;
  if (!checkTraceField(gentf)){
    cerr << "I could not compute the adjoint trace field, cannot determine arithmeticity" << endl;
    exit(1);
  }

  if (verbose) {
    cout << "gentf=" << gentf << endl;
    cout << "  (" << K.evali(gentf) << ")" << endl;
  }
  
  gen gentf_asrootof = normal(_subst(makesequence(gentf,s__IDNT_e,K.realgenasrootof),&ct),&ct);
  cout << "gentf as rootof: " << gentf_asrootof << endl;
  //  gen gentf_asrootof = normal(_subst(makesequence(gentf,s__IDNT_e,K.realgenasrootof),&ct),&ct);
  
  //  if (verbose) {
  //  cout << "gentf as rootof=" << gentf_asrootof << endl;
    //  }
  gen p = giac::expand(_poly2symb(makesequence(_pmin(eval(gentf_asrootof,&ct),&ct),a__IDNT_e),&ct),&ct);

  //  if (verbose) {
  cout << "min pol of gentf: " << p << endl;
    //  }
  
  //  gen p_roots = _realroot(makesequence(p,K.nd),&ct);

  if (_degree(makesequence(p,a__IDNT_e),&ct).val==1) {
    // The group is arithmetic.
    //  just for sanity check, will determine the signature of the form
    cout << "Trace field is Q" << endl;

    gen dH = K.det(H); // should be rational, almost nothing to do!
    K.red(dH);
    gen dHs = H[0][0]*H[1][1]-H[0][1]*H[1][0];
    K.red(dHs);
    
    cout << "det=" << dH << endl;
    cout << "subdet=" << dHs << endl;
  
    if (is_greater(-K.prec,dH,&ct)) {
      cout << "Original signature ++-" << endl;
    }
    else {
      if (is_greater(dH,K.prec,&ct)) {
	if (is_greater(-K.prec,dHs,&ct)) {
	  cout << "Original signature +--" << endl;
	  cout << " (this should not happen)" << endl;
	  exit(1);
	}
	else {
	  if (is_greater(dHs,K.prec,&ct)) {
	    cout << "Original signature +++" << endl;	
	    cout << " (this should not happen)" << endl;
	    exit(1);
	  }
	  else {
	    cerr << "Could not determine signature from intervals" << endl;
	    exit(1);
	  }
	}
      }
    }
    isarithmeticitychecked = true;
    isarithmetic = true;
    naindex = 0;
    return true;
  }
  // done with the case where trace field is Q
  

  // only works if p has degree>1
  gen gg = rootof(p,&ct);
  gen sta = gen("b",&ct);
  sto(sta,gg,&ct);
  
  gen gentf_pol;
  if (!identify_in_field(gentf_asrootof,p,gentf_pol)) {
    cerr << "Trouble identifying generator as poly in rootof" << endl;
    exit(1);    
  }

  vecteur other_roots;
  
  gen fa = _factors(makesequence(p,gg),&ct);
  int nf = (*fa._VECTptr).size()/2;
  for (int k=0; k<nf; k++) {
    //    cout << "k=" << k << endl;
    gen df = _degree(makesequence(fa[2*k],a__IDNT_e),&ct).val;
    if (verbose) {
      cout << "Factor: " << fa[2*k] << endl;
      cout << "df=" << df << endl;
    }
    if (df==1) {
      gen te = normal(-_coeff(makesequence(fa[2*k],a__IDNT_e,0),&ct)/_coeff(makesequence(fa[2*k],a__IDNT_e,1),&ct),&ct);
      gen tec = normal(eval(te-gentf_asrootof,&ct),&ct);
      gen te_pol;
      if (!operator_equal(tec,K.zeronumber,&ct)) {
	if (!identify_in_field(te,p,te_pol)) {
	  cerr << "Trouble identifying generator as poly in rootof" << endl;
	  exit(1);    
	}
	else {
	  gen tecc = _simplify(te_pol-b__IDNT_e,&ct);
	  if (!operator_equal(tecc,K.zeronumber,&ct)) {
	    other_roots.push_back(te_pol);
	  }
	}
      }
    }
  }

  cout << "Done computing " << other_roots.size() << " Galois conjugates of trace field generator" << endl;
  cout << "Other roots: " << other_roots << endl;
  
  gen dH = K.det(H);
  K.red(dH);
  dH = K.convertToReal(dH);
  if (verbose) {
    cout << "dH=" << dH << endl;
  }
  gen dH_asrootof = normal(_subst(makesequence(dH,s__IDNT_e,K.realgenasrootof),&ct),&ct);
  if (verbose) {
    cout << "det(H) as rootof=" << dH_asrootof << endl;
  }
  gen dHpol;
  if (!identify_in_field(dH_asrootof,p,dHpol)) {
    cerr << "Trouble identifying determinant" << endl;
    exit(1);    
  }

  gen dHs = H[0][0]*H[1][1]-H[0][1]*H[1][0];
  K.red(dHs);
  dHs = K.convertToReal(dHs);
  if (verbose) {
    cout << "dHs=" << dHs << endl;
  }
  gen dHs_asrootof = normal(_subst(makesequence(dHs,s__IDNT_e,K.realgenasrootof),&ct),&ct);
  if (verbose) {
    cout << "sub det as rootof=" << dHs_asrootof << endl;
  }
  gen dHspol;
  if (!identify_in_field(dHs_asrootof,p,dHspol)) {
    cerr << "Trouble identifying sub-determinant" << endl;
    exit(1);    
  }

  if (verbose) {
    cout << "dHpol=" << dHpol << endl;
    cout << "dHspol=" << dHspol << endl;
  }

  gen det0 = convert_interval(_subst(makesequence(dHpol,b__IDNT_e,gg),&ct),K.nd,&ct);
  gen subdet0 = convert_interval(_subst(makesequence(dHspol,b__IDNT_e,gg),&ct),K.nd,&ct);

  if (verbose) {
    cout << "det(H): " << det0 << endl;
    cout << "subdet: " << subdet0 << endl;
  }
  
  if (is_greater(-K.prec,det0,&ct)) {
    cout << "Original signature ++-" << endl;
  }
  else {
    if (is_greater(det0,K.prec,&ct)) {
      if (is_greater(-K.prec,subdet0,&ct)) {
	cout << "Original signature +--" << endl;
	cout << " (this should not happen)" << endl;
	cout << " det(H) num " << K.evali(dH) << endl;
	cout << " subdet num " << K.evali(dHs) << endl;
	exit(1);
      }
      else {
	if (is_greater(subdet0,K.prec,&ct)) {
	  cout << "Original signature +++" << endl;	
	  cout << " (this should not happen)" << endl;
	  cout << " det(H) num " << K.evali(dH) << endl;
	  cout << " subdet num " << K.evali(dHs) << endl;
	  exit(1);
	}
	else {
	  cerr << "Could not determine signature from intervals" << endl;
	  exit(1);
	}
      }
    }
  }

  int NA_index = 0;
  for (int j=0; j<other_roots.size(); j++) {
    if (verbose) {
      cout << "Galois conjugate #" << (j+1) << ":" << endl;
    }

    gen detj = _subst(makesequence(_subst(makesequence(dHpol,b__IDNT_e,other_roots[j]),&ct),b__IDNT_e,a__IDNT_e),&ct);
    gen subdetj = _subst(makesequence(_subst(makesequence(dHspol,b__IDNT_e,other_roots[j]),&ct),b__IDNT_e,a__IDNT_e),&ct);

    detj = _simplify(giac::expand(detj,&ct),&ct);
    subdetj = _simplify(giac::expand(subdetj,&ct),&ct);
    
    detj = _rem(makesequence(detj,p,a__IDNT_e),&ct);
    subdetj = _rem(makesequence(subdetj,p,a__IDNT_e),&ct);
    
    if (verbose) {
      cout << "det(H) in a: " << detj << endl;
      cout << "subdet in a: " << subdetj << endl;
    }

    detj = convert_interval(_subst(makesequence(detj,a__IDNT_e,gg),&ct),K.nd,&ct);
    subdetj = convert_interval(_subst(makesequence(subdetj,a__IDNT_e,gg),&ct),K.nd,&ct);

    if (verbose) {
      cout << "det(H): " << detj << endl;
      cout << "subdet: " << subdetj << endl;
    }
    
    if (is_greater(-K.prec,detj,&ct)) {
      cout << "   signature ++-" << endl;
      NA_index++;
    }
    else {
      if (is_greater(detj,K.prec,&ct)) {
	if (is_greater(-K.prec,subdetj,&ct)) {
	  cout << "   signature +--" << endl;
	  NA_index++; 
	}
	else {
	  if (is_greater(subdetj,K.prec,&ct)) {
	    cout << "   signature +++" << endl;	
	  }
	  else {
	    cerr << "Could not determine signature from intervals" << endl;
	    exit(1);
	  }
	}
      }
    }
  }
  cout << "Non-arithmeticity index: " << NA_index << endl;
  
  if (NA_index==0) {
    cout << "Sorry, this group is arithmetic." << endl;
  }
  else {
    cout << "The group is NOT arithmetic, NA index " << NA_index << endl;
  }
  
  isarithmeticitychecked = true;
  isarithmetic = (NA_index==0);
  naindex = NA_index;
  return (NA_index==0);


}

gen Polytope::lowerDegree(gen g){

  gen res;
  gen p = giac::expand(_poly2symb(makesequence(_pmin(g,&ct),z__IDNT_e),&ct),&ct);

  gen co = _coeff(makesequence(p,z__IDNT_e),&ct);
  gen de =_lcm(_denom(co,&ct),&ct);
  p = _simplify(de*p,&ct);
  if (_degree(makesequence(p,z__IDNT_e),&ct).val>1) {
    gen j = rootof(p,&ct);
    gen fa = _factors(makesequence(p,j),&ct);
    bool fd = false;
    for (int l=0; !fd && 2*l<(*fa._VECTptr).size(); l++) {
      int d = _degree(makesequence(fa[2*l],z__IDNT_e),&ct).val; 
      if (d>1){
	return g;
      }
      if (d==1) {
	gen te = normal(-_coeff(makesequence(fa[2*l],z__IDNT_e,0),&ct)/_coeff(makesequence(fa[2*l],z__IDNT_e,1),&ct),&ct);

	gen tet = normal(te-g,&ct);
	fd = is_zero(tet,&ct);
	if (fd) {
	  return te; 
	}
      }
    }
  }
  else {
    return g;
  }    
  return g;
}

bool Polytope::isPPower(gen M) {
  bool fd = false;
  int k=orderofp-1;
  while (!fd && k>=0) {
    gen N = M*Ppowers[k];
    K.matred(N);
    fd = K.isScalar(N);
    k--;
  }
  return fd;
}

int Polytope::testPoint(gen V) {
  // returns -1 if point is inside polytope, 0 if it is on boundary, +1 if outside.
  //   (for now, I will not check the boundary of the ball)
  //
  int k = 0;
  bool foundzero = false;
  while (k<faces.size()) {
    gen te = Inn(V,faces[k].X0)*Inn(faces[k].X0,V) - Inn(V,faces[k].X1)*Inn(faces[k].X1,V);
    K.red(te);
    te = K.convertToReal(te);
    int sg;

    if (!K.checkSign(te,sg)){
      cerr << "Trouble checking sign of equation (testing point inside polytope)" << endl;
      exit(1);
    }
    else {
      if (sg==0) {
	foundzero = true;
      }
      else {
	if (sg>0) {
	  return 1;
	}
      }
    }
    k++;
  }
  // if we get here, all equations are <=0, we are in polytope!
  if (foundzero) {
    // one equation is 0, the point may still not be in the boundary?
    return 0;
  }
  else {
    return -1;
  }
}

bool Polytope::findMirror(gen M, gen &res) {

  //  cout << "Début findMirror" << endl;
  if (verbose) {
    cout << "Trying to compute mirror for: " << endl;
    cout << "M=" << M << endl;
    cout << "K.genasrootof=" << K.genasrootof << endl;
  }
  gen cpm0 = _charpoly(M,&ct);

  vecteur cpm1;
  for (int j=0; j<4; j++) {
    gen tej = cpm0[j];
    K.red(tej);
    cpm1.push_back(_subst(makesequence(tej,x__IDNT_e,eval(K.genasrootof,&ct)),&ct));
  }

  gen var = u__IDNT_e;
  // Used to be l__IDNT_e, I changed this to make t3S2 work :S

  
  gen cpm = normal(_poly2symb(makesequence(cpm1,var),&ct),&ct);
  
  /*
  //  gen Mn = normal(_subst(makesequence(M,x__IDNT_e,K.genasrootof),&ct),&ct);
  //  cout << "compute det and eval" << endl;
  gen pon = _eval(_det(Mn - l__IDNT_e*K.Id,&ct),&ct);
  //  gen pon = ;
  cout << "char pol: " << pon << endl;
  cout << _charpoly(Mn,&ct);
  */

  if (verbose) {
    cout << "cpm=" << cpm << endl;
    cout << "K.genasrootof: " << K.genasrootof << endl;
  }
  
    gen fan = _factors(makesequence(eval(cpm,&ct),K.genasrootof),&ct);
  //  gen fan = _factors(makesequence(cpm,l__IDNT_e,K.genasrootof),&ct);

  //  cout << _factor(makesequence(cpm,l__IDNT_e,K.genasrootof),&ct) << endl;
  
    if (verbose) {
      cout << "factors=" << fan << endl; 
      cout << "fan size: " << (*fan._VECTptr).size() << endl;
    }
  
  //  for (int jj = 0; 2*jj<6; jj++) {
  for (int jj = 0; 2*jj<(*fan._VECTptr).size(); jj++) {

    //    cout << "if" << endl;
    if (  (_degree(makesequence(fan[2*jj],var),&ct).val==1 && (fan[2*jj+1]).val==1)  ||  ( (fan[2*jj+1]).val==3)) {

      if (verbose) {
	cout << "Studying factor #" << jj << endl;
      }
      //      cout << "huh?" << endl;
      gen ev = _eval(normal(-_subst(makesequence(fan[2*jj],var,K.zeronumber),&ct)/_coeff(makesequence(fan[2*jj],var,1),&ct),&ct),&ct);
      // changed to make Mostow G(4,0) work, hopefully this doesn't break anything...

      if (verbose) {
	cout << "ev=" << ev << endl;
      }
      
      //      cout << "convert interval" << endl;
      gen evi =  convert_interval(ev,K.nd,&ct);
      if (verbose) {
	cout << "evi=" << evi << endl;
      }
            
      //      cout << "pmin" << endl;
      gen mpev = giac::expand(_poly2symb(makesequence(_pmin(ev,&ct),z__IDNT_e),&ct),&ct);
      if (verbose) {
	cout << "min pol of ev: " << mpev << endl;
      }

      gen evx;
      if (! K.findInField(mpev,evi,evx)) {
	cerr << "Trouble identifying eigenvalue in field" << endl;
	exit(1);
      }

      if (verbose) {
	cout << "eigenvalue in terms of x: " << evx << endl;
      }
      
      gen A = _simplify(M - evx*K.Id,&ct);
      if (verbose) {
	cout << "A before red: " << A << endl;
      }
      K.matred(A);
      if (verbose) {
	cout << "A after red: " << A << endl;
      }
      vector<int> inds;
      vector<gen> lir;
      for (int kk=0; kk<3; kk++) {
	gen v0 = _row(makesequence(A,kk),&ct);
	if (!is_zero(v0,&ct)) {
	  if (inds.size()==0) {
	    inds.push_back(kk);
	    lir.push_back(v0);
	    if (verbose) {
	      cout << " non-zero row (" << kk << "): " << v0 << endl;
	    }
	  }
	  else {
	    //	    if (!is_zero(v0,&ct) && inds.size()==1) {
	    bool tes = K.areDep(v0,lir[0]);
	    if (!tes) {
	      if (verbose){
		cout << " indep non-zero row (" << kk << "): " << v0 << endl;
	      }
	      inds.push_back(kk);
	    }
	    else {
	      if (verbose){
		cout << " following row (" << kk << ") is dependent on first one: " << v0 << endl;
	      }
	    }
	  }
	}
      }
      if (inds.size()<2) {
	cerr << "Trouble finding mirror, wrong rank" << endl;
	exit(1);
      }
      gen mirr = _cross( makesequence( _row(makesequence(A,inds[0]),&ct), _row(makesequence(A,inds[1]),&ct)),&ct);
      K.vectred(mirr);
      res = mirr;
      //      cout << "mirr=" << mirr << endl;
      //     cout << "Fin findMirror" << endl;
      return true;
    }
  }
  //  cout << "Fin findMirror" << endl;
  return false;
}

void Polytope::reduce_degree_vect(gen &x0) {
  for (int j=0; j<(*x0._VECTptr).size(); j++) {
    reduce_degree((*x0._VECTptr)[j]);
  }
}

void Polytope::reduce_degree_mat(gen &x0) {
  for (int j=0; j<(*x0._VECTptr).size(); j++) {
    reduce_degree_vect((*x0._VECTptr)[j]);
  }
}

void Polytope::reduce_degree(gen &x0) {


  //  cout << "reduce_degree" << endl;

  //  cout << "evalf: " << _evalf(x0,&ct) << endl;
  //  cout << "convert interval: " << convert_interval(x0,K.nd,&ct) << endl;
  
  // Removed the denom on Dec 1 2019, to fix s4c groups, but this was useful when rational numbers occurred...
  gen den = _denom(x0,&ct);
  //  gen den = K.onenumber;

  //  cout << "den=" << den << endl;
  
  gen x = normal(x0*den,&ct);

  //  cout << "x=" << x << endl;
  
  gen mpx = giac::expand(_poly2symb(makesequence(_pmin(eval(x,&ct),&ct),a__IDNT_e),&ct),&ct);

  //  cout << "min pol: " << mpx << endl;

  //  cout << "complex roots: " << _complexroot(makesequence(mpx,K.prec),&ct) << endl;
  
  //  cout << " (degree " << _degree(makesequence(mpx,a__IDNT_e),&ct).val << ")" << endl;
    
  if (_degree(makesequence(mpx,a__IDNT_e),&ct).val>1) {

    //mpx need not be irreducible, not sure if this could ever be a problem...

    gen gg = rootof(mpx,&ct);

    //    cout << "gg=" << gg << endl;
    
    gen fad = _factors(makesequence(mpx,gg),&ct);

    //    cout << "fad=" << fad << endl;

    bool found = false;
    int k = 0;
    int nk = (*fad._VECTptr).size()/2; 
    while (!found && k<nk) {
      //    cout << "factor " << fad[2*k] << endl;
      int dfd = _degree(makesequence(fad[2*k],a__IDNT_e),&ct).val;
      //   cout << "dfd=" << dfd << endl;
      if (dfd==1) {	
	//	cout << "using factor " << fad[2*k] << endl;
	gen te = normal(-_subst(makesequence(fad[2*k],a__IDNT_e,K.zeronumber),&ct)/_diff(makesequence(fad[2*k],a__IDNT_e),&ct),&ct);
	//	cout << "te=" << te << endl;
	gen tec = normal(te-x,&ct);
	//	cout << "tec=" << tec << endl;
	//	cout << "evalf(tec): " << _evalf(tec,&ct) << endl;
	//	cout << "convert_interval(tec): " << convert_interval(tec,K.nd,&ct) << endl;
	found = operator_equal(tec,K.zeronumber,&ct);
	if (found) {
	  x0 = eval(te/den,&ct);
	  //	  cout << "done" << endl;
	  //	  cout << "evalf: " << _evalf(x0,&ct) << endl;
	}
	else {
	  //	  cout << tec << endl;
	  //	  cout << "non-zero (" << _evalf(tec,&ct) << ")" << endl;
	  cout << "non-zero (" << convert_interval(tec,K.nd,&ct) << ")" << endl;
	}
      }
      k++;
    }
    if (!found) {
      cout << "FAIL" << endl;
    }
  }
}

void Polytope::normalize_vector(gen &v) {
  // will be used with vectors whose entries are complex intervals
  bool fd = false;
  int j = -1;
  while (!fd && j<2) {
    j++;
    gen ns = re(v[j],&ct)*re(v[j],&ct) + im(v[j],&ct)*im(v[j],&ct);
    fd = is_greater(_left(ns,&ct),K.prec,&ct);
  }
  if (fd) {
    v = v/v[j];
  }
}

bool Polytope::my_jordan_knowing_linear_order_alt(gen M, int o, vecteur &bas, vecteur &evs) {

  bool found = false;

  bool problem = false;
  
  int count = 0;

  int savend = K.nd;

  gen Mi = convert_interval(_subst(makesequence(M,x__IDNT_e,K.xvi),&ct),K.nd,&ct);
  gen Hi = convert_interval(_subst(makesequence(H,x__IDNT_e,K.xvi),&ct),K.nd,&ct);
  
  gen u = rootof(_cyclotomic(o,&ct),&ct);
  gen uj = K.onenumber;
  
  vector<int> neg_powers;
  vector<int> pos_powers;
  vecteur bas_neg;
  vecteur bas_pos;
  
  for (int j=0; j<o; j++) {
    gen uji = convert_interval(uj,K.nd,&ct);
    //  cout << "uji=" << uji << endl;
    gen A = Mi-uji*convert_interval(K.Id,K.nd,&ct);
    gen di = A[0][0] * A[1][1] * A[2][2] + A[0][1] * A[1][2] * A[2][0] + A[1][0] * A[2][1] * A[0][2]
      - A[2][0] * A[1][1] * A[0][2] - A[1][0] * A[0][1] * A[2][2] - A[2][1] * A[1][2] * A[0][0];
    //  cout << "di=" << di << endl;
    gen dins = re(di,&ct)*re(di,&ct) + im(di,&ct)*im(di,&ct);
    //   cout << "|di|^2=" << dins << endl;
    
    /*    if (is_greater(_left(dins,&ct),K.zeronumber,&ct)) {
	  cout << "Power " << j << " is definitely not candidate eigenvalue" << endl;
	  }*/
    if (is_greater(K.zeronumber,_left(dins,&ct),&ct)) {
      if (verbose) {
	cout << "Power " << j << " may well be an eigenvalue" << endl;
      }
      
      gen r1 = _row(makesequence(A,0),&ct);
      gen r2 = _row(makesequence(A,1),&ct);
      gen r3 = _row(makesequence(A,2),&ct);
      
      gen v12 = _cross(makesequence(r1,r2),&ct);
      gen v23 = _cross(makesequence(r2,r3),&ct);
      gen v31 = _cross(makesequence(r3,r1),&ct);
      
      gen nz12 = convert_interval(K.zeronumber,K.nd,&ct);
      for (int l=0; l<3; l++) {
	nz12 = nz12 + re(v12[l],&ct)*re(v12[l],&ct) + im(v12[l],&ct)*im(v12[l],&ct); 
      }
      if (verbose) {
	cout << "nz12=" << nz12 << endl;
      }
      if (is_greater(_left(nz12,&ct),K.prec,&ct)) {
	// v12 is eigenvector
	normalize_vector(v12);
	gen v12c = conj(v12,&ct);
	gen ns12 = re(v12c*Hi*v12,&ct);
	if (verbose) {
	  cout << "square norm of eigenvector: " << ns12 << endl;
	}
	if (is_greater(_left(ns12,&ct),K.prec,&ct)) {
	  pos_powers.push_back(j);
	  bas_pos.push_back(v12);
	}
	else {
	  if (is_greater(-K.prec,_right(ns12,&ct),&ct)) {
	    neg_powers.push_back(j);
	    bas_neg.push_back(v12);
	  }
	  else {
	    cout << "Trouble determining sign of eigenvector" << endl;
	    cout << "  (maybe higher precision would help)" << endl;
	    problem = true;
	  }
	}
      }
      else {
	gen nz23 = convert_interval(K.zeronumber,K.nd,&ct);
	for (int l=0; l<3; l++) {
	  nz23 = nz23 + re(v23[l],&ct)*re(v23[l],&ct) + im(v23[l],&ct)*im(v23[l],&ct); 
	}
	if (verbose) {
	  cout << "nz23=" << nz23 << endl;
	}
	if (is_greater(_left(nz23,&ct),K.prec,&ct)) {
	  // v23 is eigenvector
	  normalize_vector(v23);
	  gen v23c = conj(v23,&ct);
	  gen ns23 = re(v23c*Hi*v23,&ct);
	  if (verbose) {
	    cout << "square norm of eigenvector: " << ns23 << endl;
	  }
	  if (is_greater(_left(ns23,&ct),K.prec,&ct)) {
	    pos_powers.push_back(j);
	    bas_pos.push_back(v23);
	  }
	  else {
	    if (is_greater(-K.prec,_right(ns23,&ct),&ct)) {
	      neg_powers.push_back(j);
	      bas_neg.push_back(v23);
	    }
	    else {
	      cout << "Trouble determining sign of eigenvector" << endl;
	      cout << "  (maybe higher precision would help)" << endl;
	      return false;
	    }
	  }
	}
	else {
	  gen nz31 = convert_interval(K.zeronumber,K.nd,&ct);
	  for (int l=0; l<3; l++) {
	    nz31 = nz31 + re(v31[l],&ct)*re(v31[l],&ct) + im(v31[l],&ct)*im(v31[l],&ct); 
	  }
	  if (verbose) {
	    cout << "nz31=" << nz31 << endl;
	  }
	  if (is_greater(_left(nz31,&ct),K.prec,&ct)) {
	    // v31 is eigenvector
	    normalize_vector(v31);
	    gen v31c = conj(v31,&ct);
	    gen ns31 = re(v31c*Hi*v31,&ct);
	    if (verbose) {
	      cout << "square norm of eigenvector: " << ns31 << endl;
	    }
	    if (is_greater(_left(ns31,&ct),K.prec,&ct)) {
	      pos_powers.push_back(j);
	      bas_pos.push_back(v31);
	    }
	    else {
	      if (is_greater(-K.prec,_right(ns31,&ct),&ct)) {
		neg_powers.push_back(j);
		bas_neg.push_back(v31);
	      }
	      else {
		cout << "Trouble determining sign of eigenvector" << endl;
		cout << "  (maybe higher precision would help)" << endl;
		return false;
	      }
	    }
	  }
	}
      }
    }
    uj = normal(u*uj,&ct);
  }

  if (verbose) {
    cout << "powers for negative vectors: " << neg_powers << endl;
    cout << "powers for positive vectors: " << pos_powers << endl;
  }
  if (pos_powers.size()!=2 || neg_powers.size()!=1) {
    cout << "Wrong signature" << endl;
    return false;
  }
  
  // will return rational numbers encoding roots of unity
  evs.push_back(neg_powers[0]*K.onenumber/o);
  evs.push_back(pos_powers[0]*K.onenumber/o);
  evs.push_back(pos_powers[1]*K.onenumber/o);
  
      // and intervals for corresponding eigenvectors (negative vector first)
  bas.push_back(bas_neg[0]);
  bas.push_back(bas_pos[0]);
  bas.push_back(bas_pos[1]);
  
  int an1, an2;
  an1 = (pos_powers[0]-neg_powers[0]);
  if (2*an1<-o) {
    an1 = an1+o;
  }
  an2 = (pos_powers[1]-neg_powers[0]);
  if (2*an2<-o) {
    an2 = an2+o;
      }
  cout << "Angles: " << an1 << "/" << o << ", " << an2 << "/" << o << endl;
  
  return true;

  
}

bool Polytope::my_jordan_knowing_linear_order(gen M, int o, vecteur &bas, vecteur &evs) {

  //  gen Mro = evalWordAsRootOf(w);
  gen Mro = normal(eval(_subst(makesequence(M,x__IDNT_e,K.genasrootof),&ct),&ct),&ct);
  //  gen Mro = eval(_subst(makesequence(M,x__IDNT_e,K.genasrootof),&ct),&ct);

  gen cham = _charpoly(M,&ct);
  //  gen cham = K.det(M-l__IDNT_e*K.Id);
  if (verbose) {
    cout << "cham=" << cham << endl;
  }
  
  gen co0 = cham[3];
  //  gen co0 = _subst(makesequence(cham,l__IDNT_e,0),&ct);
  K.red(co0);
  if (verbose) {
    cout << "co0 in x: " << co0 << endl;
    cout << "will substitute x to K.genasrootof=" << K.genasrootof << endl;
    cout << "  eval(K.genasrootof)=" << eval(K.genasrootof,&ct) << endl;
  }
  co0 = normal(_subst(makesequence(co0,x__IDNT_e,eval(K.genasrootof,&ct)),&ct),&ct);
  if (verbose) {
    cout << "co0=" << co0 << endl;
    cout << "  (" << _evalf(co0,&ct) << ")" << endl;
  }
  /*  reduce_degree(co0);
  if (verbose) {
    cout << "co0 after reduce degree=" << co0 << endl;
    cout << "  (" << _evalf(co0,&ct) << ")" << endl;
    }*/
  
  gen co1 = cham[2];
  //  gen co1 = _coeff(makesequence(cham,l__IDNT_e,1),&ct);
  K.red(co1);
  if (verbose) {
    cout << "co1 in x: " << co1 << endl;
  }
  co1 = normal(_subst(makesequence(co1,x__IDNT_e,eval(K.genasrootof,&ct)),&ct),&ct);
  if (verbose) {
    cout << "co1=" << co1 << endl;
    cout << "  (" << _evalf(co1,&ct) << ")" << endl;
  }
  /*  reduce_degree(co1);
  if (verbose) {
    cout << "co1 after reduce degree=" << co1 << endl;
    cout << "  (" << _evalf(co1,&ct) << ")" << endl;
    }*/
  
  gen co2 = cham[1];
  //  gen co2 = _coeff(makesequence(cham,l__IDNT_e,2),&ct);
  K.red(co2);
  if (verbose) {
    cout << "co2 in x: " << co2 << endl;
  }
  co2 = normal(_subst(makesequence(co2,x__IDNT_e,eval(K.genasrootof,&ct)),&ct),&ct);
  if (verbose) {
    cout << "co2=" << co2 << endl;
    cout << "  (" << _evalf(co2,&ct) << ")" << endl;
  }
  /*  reduce_degree(co2);
  if (verbose) {
    cout << "co2 after reduce degree=" << co2 << endl;
    cout << "  (" << _evalf(co2,&ct) << ")" << endl;
    }*/
  
  gen co0_num = _evalf(co0,&ct);
  gen co1_num = _evalf(co1,&ct);
  gen co2_num = _evalf(co2,&ct);
  if (verbose) {
    cout << "co0_num=" << co0_num << endl;
    cout << "co1_num=" << co1_num << endl;
    cout << "co2_num=" << co2_num << endl;
  }
  
  gen rough_eps = 1e-4;
  
  vecteur bas_rough, evs_rough;
  
  gen u = rootof(_cyclotomic(o,&ct),&ct);
  if (verbose) {
    cout << "o=" << o << endl;
    cout << "u=" << u << endl;
  }
  gen uj = K.onenumber;
  int j = 0;
  while (j<o && evs_rough.size()<3){
    //  for (int j=0; j<o; j++) {
    //    cout << "j=" << j << endl;
    //  cout << "num val..." << endl;
    gen ujn = _evalf(uj,&ct);
    //   cout << "uj=" << uj << endl;
    //    cout << "ujn=" << ujn << endl;
    //   cout << "... done num val" << endl;
    gen d1n = ujn+co2_num;
    //    cout << "d1n=" << d1n << endl;
    gen d2n = d1n*ujn+co1_num;
    //    cout << "d2n=" << d2n << endl;
    gen d3n = d2n*ujn+co0_num;
    //   cout << "d3n=" << d3n << endl;
    gen djn = abs(d3n,&ct);
    if (verbose) {
      cout << "computed num val for dj: " << djn << endl;
    }
    if (is_greater(rough_eps,djn,&ct)) {
      if (verbose) {
	cout << "doing exact comps" << endl;
	cout << "j=" << j << endl;
	cout << "uj=" << uj << endl;
      }
      gen d1 = normal(eval(uj+co2,&ct),&ct);
      /*      gen d1red;
      if (!reduce_degree(d1,d1red)) {
	cout << "Trouble reducing degree" << endl;
      }
      else {
	d1 = d1red;
	}*/
      //     cout << "d1=" << d1 << endl;
      //   cout << "d1n=" << d1n << endl;
      gen d2 = normal(eval(d1*uj+co1,&ct),&ct);
      /*      gen d2red;
      if (!reduce_degree(d2,d2red)) {
	cout << "Trouble reducing degree" << endl;
      }
      else {
	d2 = d2red;
	}*/
      //   cout << "d2=" << d2 << endl;
      //  cout << "d2n=" << d2n << endl;
      gen d = normal(eval(d2*uj+co0,&ct),&ct);
      //      reduce_degree(d);
      cout << "d=" << d << endl;
      cout << "eval(d)=" << eval(d,&ct) << endl;
      cout << "evalf(d)=" << _evalf(d,&ct) << endl;
      if (verbose) {
	cout << "d contains j?" << contains(d,j__IDNT_e) << endl;
	cout << "subst j gar: " <<_subst(makesequence(d,j__IDNT_e,K.genasrootof),&ct) << endl;
	cout << "normal subst j gar: " << normal(_subst(makesequence(d,j__IDNT_e,K.genasrootof),&ct),&ct) << endl;
	//      d = _subst(makesequence(d,j__IDNT_e,K.genasrootof),&ct);
	//     d = normal(d,&ct);
	cout << "d=" << d << endl;
	cout << " (" << _evalf(d,&ct) << ")" << endl;
      }
      //      cout << "d3n=" << d3n << endl;
      if (operator_equal(d,K.zeronumber,&ct)) {
	cout << "j=" << j << ",  gives eigenvalue" << endl;

	cout << "uj=" << uj << endl;

	gen A = recursive_normal(eval(Mro-uj*K.Id,&ct),&ct);

	if (verbose) {
	  cout << "Will now try and reduce degree for matrix before computing kernel" << endl;
	}
	
	//	reduce_degree_mat(A);
	
	if (verbose) {
	  cout << "A=" << A << endl;
	  //	  cout << "det(A)=" << _det(A,&ct) << endl;
	}

	/*	gen ke;
	if (!my_giac_kernel(A,ke)) {
	  cout << "Trouble computing kernel (with my_giac_kernel)" << endl;
	  cout << "   (maybe the kernel was not 1-dimensional)" << endl;
	  return false;
	}
	else {
	  cout << "Should be close to 0 vector: " << convert_interval(A,K.nd,&ct)*convert_interval(ke,K.nd,&ct) << endl;
	}
	evs_rough.push_back(uj);
	bas_rough.push_back(eval(ke,&ct));*/
	
	
	gen ke = _ker(A,&ct);
       	if (verbose) {
	  cout << "ke=" << ke << endl;
	}
	int kes = (*ke._VECTptr).size(); 
	if (verbose) {
	  cout << "kes=" << kes << endl;
	}
	if (kes==1) {
	  cout << "Should be close to 0 vector: " << convert_interval(A,K.nd,&ct)*convert_interval(ke[0],K.nd,&ct) << endl;
	  evs_rough.push_back(uj);
	  bas_rough.push_back(eval(ke[0],&ct));
	  //  cout << "bas_rough.size(): " << bas_rough.size() << endl;
	}
	else {
	  if (kes>1) {
	    cout << "Matrix has a repeated eigenvalue" << endl;
	    exit(1);
	  }
	}
	
      }
      //      else {
      //	cout << "power " << j << " does not give an eigenvalue" << endl;
      //      }
    }
    //    else {
    //     cout << "Did not even try exact computation, this seemed nonzero:" << endl;
    //    cout << "djn=" << djn << endl;
    uj = normal(eval(uj*u,&ct),&ct);
    //    cout << "uj=" << uj << endl;
    j++;
  }
  if (evs_rough.size()==3) {

    if (verbose) {
      cout << "Found 3 eigenvectors, will now order them so the first one has negative square-norm.." << endl;
    }
    
    vector<int> inds_neg, inds_pos;
    for (int j=0; j<3; j++) {
      if (verbose) {
	cout << "Computing square norm of " << bas_rough[j] << endl;
      }
      gen cc = normal(eval(conj(bas_rough[j],&ct),&ct),&ct);
      if (verbose) {
	cout << "cc=" << cc << endl;
      }
      gen te = normal(eval(cc*H_rootof*bas_rough[j],&ct),&ct);
      if (verbose) {
	cout << "te=" << te << endl;
      }
      gen ter0 = convert_interval(te,K.nd,&ct);
      if (verbose) {
	cout << "ter0=" << ter0 << endl;
      }
      gen ter = re(ter0,&ct);
      if (verbose) {
	  cout << "ter=" << ter << endl;
      }
      if (is_greater(_left(ter,&ct),K.zeronumber,&ct)){
	if (verbose) {
	  cout << " (positive)" << endl;
	}
	inds_pos.push_back(j);
      }
      else {
	if (is_greater(K.zeronumber,_right(ter,&ct),&ct)){
	  if (verbose) {
	    cout << " (negative)" << endl;
	  }
	  inds_neg.push_back(j);
	}
	else {
	  // interval contains 0
	  cout << "Interval contains 0, should be able to overcome this difficulty" << endl;
	  cout << "  (not implemented yet)" << endl;
	  return false;
	}
      }
    }
    if (inds_pos.size()!=2 || inds_neg.size()!=1){
      cout << "Wrong signature" << endl;
      return false;
    }
    else {
      if (verbose) {
	cout << " ... done reordering" << endl;
      }
      evs.push_back(evs_rough[inds_neg[0]]);
      evs.push_back(evs_rough[inds_pos[0]]);
      evs.push_back(evs_rough[inds_pos[1]]);
      bas.push_back(bas_rough[inds_neg[0]]);
      bas.push_back(bas_rough[inds_pos[0]]);
      bas.push_back(bas_rough[inds_pos[1]]);
      return true;
    }
  }
  else {
    cout << "Trouble diagonalizing matrix" << endl;
    return false;
  }
}

bool Polytope::my_giac_are_dep(gen v, gen w) {

  gen d12 = normal(eval(v[0]*w[1]-v[1]*w[0],&ct),&ct);
  if (!operator_equal(d12,K.zeronumber,&ct)) {
    return false;
  }
  else {
    gen d23 = normal(eval(v[1]*w[2]-v[2]*w[1],&ct),&ct);
    if (!operator_equal(d23,K.zeronumber,&ct)) {
      return false;
    }
    else {
      gen d13 = normal(eval(v[0]*w[2]-v[2]*w[0],&ct),&ct);
      if (!operator_equal(d13,K.zeronumber,&ct)) {
	return false;
      }
      else {
	return true;
      }
    }
  }
   
}

bool Polytope::my_giac_kernel(gen M, gen &res) {
  // method to avoid giac's kernel, only valid when M is a matrix of rootofs, no x allowed!
  //
  // returns true only if kernel is 1-dimensional
  //
  // include reducing the degree (this may not be a good idea either...)
  
  vecteur nonzerorows;

  /*  gen m11 = M[0][0];
  gen mred;
  if (reduce_degree(m11,mred)) {
    m11 = mred;
  }
  gen m12 = M[0][1];
  if (reduce_degree(m12,mred)) {
    m12 = mred;
    }
  gen m13 = M[0][2];
  if (reduce_degree(m13,mred)) {
    m13 = mred;
    }
  vecteur row1v;
  row1v.push_back(m11);
  row1v.push_back(m12);
  row1v.push_back(m13);
  gen row1 = gen(row1v);*/
  gen row1 = _row(makesequence(M,0),&ct);
  if (!operator_equal(row1,K.zerovector,&ct)) {
    nonzerorows.push_back(row1);
  }
  
  /*  gen m21 = M[1][0];
  if (reduce_degree(m21,mred)) {
    m21 = mred;
  }
  gen m22 = M[1][1];
  if (reduce_degree(m22,mred)) {
    m22 = mred;
  }
  gen m23 = M[1][2];
  if (reduce_degree(m23,mred)) {
    m23 = mred;
  }
  vecteur row2v;
  row2v.push_back(m21);
  row2v.push_back(m22);
  row2v.push_back(m23);
  gen row2 = gen(row2v);*/

  gen row2 = _row(makesequence(M,1),&ct);
     
  if (!operator_equal(row2,K.zerovector,&ct)) {
    // still want to test if it is independent from first one
    if (nonzerorows.size()==0) {
      nonzerorows.push_back(row2);
    }
    else {
      if (!my_giac_are_dep(row2,nonzerorows[0])) {
	nonzerorows.push_back(row2);
      }
    }
  }

  /*  gen m31 = M[2][0];
  if (reduce_degree(m31,mred)) {
    m31 = mred;
  }
  gen m32 = M[2][1];
  if (reduce_degree(m32,mred)) {
    m32 = mred;
  }
  gen m33 = M[2][2];
  if (reduce_degree(m33,mred)) {
    m33 = mred;
  }
  vecteur row3v;
  row3v.push_back(m31);
  row3v.push_back(m32);
  row3v.push_back(m33);
  gen row3 = gen(row3v);*/

  gen row3 = _row(makesequence(M,2),&ct);
    
  if (!operator_equal(row3,K.zerovector,&ct)) {
    if (nonzerorows.size()==0) {
      nonzerorows.push_back(row3);
    }
    else {
      if (nonzerorows.size()==1) {
	if (!my_giac_are_dep(row3,nonzerorows[0])) {
	  nonzerorows.push_back(row3);
	}
      }
      // otherwise, two independent vectors, can use cross-product
    }
  }

  cout << "nonzerorows.size(): " << nonzerorows.size() << endl;
  if (nonzerorows.size()==2) {
    gen v = nonzerorows[0];
    gen w = nonzerorows[1];
    
    vecteur u;
    u.push_back(normal(v[1]*w[2]-v[2]*w[1],&ct));
    u.push_back(normal(v[2]*w[0]-v[0]*w[2],&ct));
    u.push_back(normal(v[0]*w[1]-v[1]*w[0],&ct));

    gen ke0 = gen(u);
    
    cout << "ke0=" << ke0 << endl;

    bool fd = false;
    int k=-1;
    while (!fd && k<2) {
      k++;
      fd = !operator_equal(ke0[k],K.zeronumber,&ct);
    }
    if (!fd) {
      cout << "Trouble, cross-product should not be zero" << endl;
      return false;
    }
    else {
      res = normal(ke0/ke0[k],&ct);
      /*      cout << "Will now try and reduce degree" << endl;
      vecteur alt;
      for (int j=0; j<3; j++) {
	if (j==k) {
	  alt.push_back(res[j]);
	}
	else {
	  gen te;
	  if (reduce_degree(res[j],te)) {
	    alt.push_back(te);
	  }
	  else {
	    alt.push_back(res[j]);
	  }
	}
      }
      res = gen(alt);
      cout << "kernel gen-d by: " << res << endl;*/
      return true;
    }
  }
  else {
    return false;
  }
}

bool Polytope::my_jordan(gen M, vecteur &bas, vecteur &evs) {

  cout << "start my_jordan" << endl;

  gen poP = K.det(M-l__IDNT_e*K.Id);

  //  cout << "Characteristic polynomial of poP: " << poP << endl;
  //  cout << "genasrootof=" << K.genasrootof << endl;

  vecteur eqs_pop;
  eqs_pop.push_back(K.minpol);
  eqs_pop.push_back(poP);
  vecteur var_pop;
  var_pop.push_back(x__IDNT_e);
  var_pop.push_back(l__IDNT_e);

  //  cout << "eqs_pop=" << eqs_pop << endl;
  //  cout << "var_pop=" << var_pop << endl;

  cout << "calcul rur" << endl;
  gen gb = _gbasis(makesequence(eqs_pop,var_pop,change_subtype(_RUR_REVLEX,_INT_GROEBNER)),&ct);
  cout << "done with rur" << endl;
  
  if (gb[0]==-4)  {

    gen varpar = lidnt(gb[2])[0];
    gen fa0 = _factors(gb[2],&ct);
    cout << "computed factors of parameter polynomial" << endl;
    int ng = (*fa0._VECTptr).size()/2;

    vecteur pars_restricted;
    for (int k=0; k<ng; k++) {
      if (_degree(fa0[2*k],&ct)>0) {
	//	if (verbose) {
	  cout << "fa0[2*k]=" << fa0[2*k] << endl;
	  //	}
	gen te = rootof(fa0[2*k],&ct);
	cout << "splitting factor" << endl;
	gen fa = _factors(makesequence(fa0[2*k],te),&ct);
	cout << "done splitting" << endl;
	vecteur pars;
	for (int j = 0; 2*j<(*fa._VECTptr).size(); j++) {
	  gen dj = _degree(fa[2*j],&ct);
	  //	  cout << "computing parameter " << j << endl; 
	  if (dj.val==1) {
	    gen num = eval(_subst(makesequence(fa[2*j],varpar,K.zeronumber),&ct),&ct);
	    //	    cout << "num=" << num << endl;
	    gen den = _diff(fa[2*j],&ct);
	    //	    cout << "den=" << den << endl;
	    gen sol = normal(-num/den,&ct);
	    //	    cout << "sol=" << sol << endl;
	    pars.push_back(sol);
	    /*	    gen sol_red;
	    if (reduce_degree(sol,sol_red)) {
	      pars.push_back(sol_red);	      
	    }
	    else {
	      pars.push_back(sol);
	      }*/
	  }
	  //	  cout << " ... done" << endl;
	}
	//	cout << "Found " << pars.size() << " params" << endl;
	for (int j=0; j<pars.size(); j++) {
	  //	  cout << "using parameter " << j << endl;
	  gen num = normal( _subst(makesequence(gb[4],varpar,pars[j]),&ct), &ct);
	  //	  cout << "num=" << num << endl;
	  gen den = normal( _subst(makesequence(gb[3],varpar,pars[j]),&ct), &ct);
	  //	  cout << "den=" << den << endl;
	  te = normal( (num/den) - K.genasrootof, &ct);
	  //	  cout << "comparing with genas rootof... what counts is whether this is zero:" << te << endl;
	  if (operator_equal(te,K.zeronumber,&ct)) {
	    cout << "pars[" << j << "]=" << pars[j] << endl;
	    cout << _evalf(pars[j],&ct) << endl;
	    pars_restricted.push_back(pars[j]);
	  }
	}
      }
    }

    if (pars_restricted.size()==3) {
      vecteur evals;
      cout << "Computing eigenvalues" << endl;
      for (int j=0; j<pars_restricted.size(); j++) {
	gen num = normal(_subst(makesequence(gb[5],varpar,pars_restricted[j]),&ct),&ct);
	cout << "num=" << num << endl;
	gen den = normal(_subst(makesequence(gb[3],varpar,pars_restricted[j]),&ct),&ct); 
	cout << "den=" << den << endl;
	
	gen evj = normal(num/den,&ct);
	reduce_degree(evj);
	cout << "e-value " << j << ": " << evj << endl;
	evals.push_back( evj );
      }
      vecteur cols;
      for (int j=0; j<evals.size(); j++) {
	cout << "Computing kernel for eigenvalue #" << j << endl;
	gen A = recursive_normal(eval(_subst(makesequence(M,x__IDNT_e,K.genasrootof),&ct) - evals[j]*K.Id,&ct),&ct);
	if (verbose) {
	  cout << "A:=" << A << endl;
	}
	gen ke = _ker(A,&ct);
	//	if (verbose) {
	  cout << "ke:=" << ke << endl;
	  //	}
	if ((*ke._VECTptr).size()!=1) {
	  cout << "Matrix has a repeated eigenvalue, this is not handled by my_jordan (yet)" << endl;
	  return false;
	}
	else {
	  gen ke2 = ke[0];
	  if (operator_equal(ke2[0],K.zeronumber,&ct)) {
	    if (operator_equal(ke2[1],K.zeronumber,&ct)) {
	      if (operator_equal(ke2[2],K.zeronumber,&ct)) {
		cerr << "Trouble, kernel basis vector should not be zero!" << endl;
		exit(1);
	      }
	      else {
		ke2 = normal(ke2/ke2[2],&ct);
	      }
	    }
	    else {
	      ke2 = normal(ke2/ke2[1],&ct);
	    }
	  }
	  else {
	    ke2 = normal(ke2/ke2[0],&ct);
	  }
	  cout << "ke2=" << ke2 << endl;
	  cols.push_back(ke2);
	}
      }

      vector<int> inds_neg, inds_pos;
      for (int j=0; j<3; j++) {
	//	if (verbose) {
	  cout << "Computing square norm of " << cols[j] << endl;
	  //	}
	gen cc = normal(conj(cols[j],&ct),&ct);
	//	if (verbose) {
	  cout << "cc=" << cc << endl;
	  //	}
	  gen te = simplify(normal(cc*H_rootof*cols[j],&ct),&ct);
	  if (verbose) {
	    cout << "te=" << te << endl;
	  }
	  gen ter0 = convert_interval(te,K.nd,&ct);
	  cout << "ter0=" << ter0 << endl;
	  gen ter = re(ter0,&ct);
	//	if (verbose) {
	  cout << "ter=" << ter << endl;
	  //	}
 	if (is_greater(_left(ter,&ct),K.zeronumber,&ct)){
	  //	  if (verbose) {
	    cout << " (positive)" << endl;
	    //	  }
	  inds_pos.push_back(j);
	}
	else {
	  if (is_greater(K.zeronumber,_right(ter,&ct),&ct)){
	    //	    if (verbose) {
	      cout << " (negative)" << endl;
	      //  }
	    inds_neg.push_back(j);
	  }
	  else {
	    // interval contains 0
	    cout << "Interval contains 0, should be able to overcome this difficulty" << endl;
	    cout << "  (not implemented yet)" << endl;
	    return false;
	  }
	}
      }
      if (inds_pos.size()!=2 || inds_neg.size()!=1){
	cout << "Wrong signature" << endl;
	return false;
      }
      
      vecteur cols2;
      cols2.push_back(cols[inds_neg[0]]);
      cols2.push_back(cols[inds_pos[0]]);
      cols2.push_back(cols[inds_pos[1]]);      

      gen keke = gen(cols2);
      gen U = K.transpose(keke);
      // First column of U is the fixed point in the ball
      for (int j=0; j<(*U._VECTptr).size(); j++) {
	bas.push_back(U[j]);
      }
      //      bas = U;

      vecteur evv;
      evv.push_back(evals[inds_neg[0]]);
      evv.push_back(evals[inds_pos[0]]);
      evv.push_back(evals[inds_pos[1]]);
      gen kekeke = gen(evv);
      evs = evv;
      
      return true;
    }
    else {
      cout << "Found " << pars_restricted.size() << " e-values" << endl;
      cout << "Not obvious how to express eigenvalues as rootof" << endl;
      return false;
    }
  }
  else {
    cout << "Trouble splitting characteristic polynomial (in my_jordan)" << endl;
    return false;
  }

}

string Polytope::convert_time(long double x) {
  int hours = floor(x/(60*60));
  int mins = floor((x-60*60*hours)/60.0);
  int secs = floor((x-60*60*hours-60*mins));
  string res;
  stringstream ss;
  if (hours>0) {
    ss << hours << "h";
  }
  if (mins>0) {
    ss << mins << "m";
  }
  ss << secs << "s" << endl;
  return ss.str();
}
back to top