import javax.swing.JButton;
import java.awt.Dimension;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener; 
import java.awt.event.ActionEvent;
import javax.swing.*;
//import javax.swing.JFrame;	// applet 
		
public class HyperbolicApplet
//    extends JFrame /* java.applet.Applet*/ non
   extends java.applet.Applet
    implements java.awt.event.MouseListener,
               java.awt.event.MouseMotionListener,
               java.awt.event.KeyListener
          
               
{



 private String getParameterString(String paramName,
                  String defaultValue)
 {
     String paramValueString = getParameter(paramName);
     if (paramValueString != null)
  return paramValueString;
     else
  return defaultValue;
 }
 private int getParameterInt(String paramName,
               int defaultValue)
 {
     String paramValueString = getParameter(paramName);
     if (paramValueString != null)
            {
                try
                {
                    return Integer.decode(paramValueString).intValue();
                }
                catch (NumberFormatException e)
                {
                    return 0;
                }
            }
     else
  return defaultValue;
 }
 private double getParameterDouble(String paramName,
                     double defaultValue)
 {
     String paramValueString = getParameter(paramName);
     if (paramValueString != null)
            {
                try
                {
                    return Double.parseDouble(paramValueString);
                }
                catch (NumberFormatException e)
                {
                    return 0;
                }
            }
     else
  return defaultValue;
 }
 private boolean getParameterBoolean(String paramName,
         boolean defaultValue)
 {
     String paramValueString = getParameter(paramName);
     if (paramValueString != null)
            {



                try
                {
                    int n = Integer.decode(paramValueString).intValue();
                    return n != 0;
                }
                catch (NumberFormatException e)
                {
                    if (paramValueString.equalsIgnoreCase("true")
                     || paramValueString.equalsIgnoreCase("yes")
                     || paramValueString.equalsIgnoreCase("t")
                     || paramValueString.equalsIgnoreCase("y"))
                        return true;
                    return false;
                }
            }
     else
  return defaultValue;
 }

public void ana_schlafli(String schlafliString)
{

               schlafliString = schlafliString.trim();

                String orbifoldSymbol = null;



                {
                    int len = schlafliString.length();
                    int i = 0;
                    if (i < len && schlafliString.charAt(i) == '(')
                    {


                        while (i < len && schlafliString.charAt(i++) != ')')
                            ;
                    }
                    else
                    {



                        while (i < len && " ;:_^+".indexOf(schlafliString.charAt(i)) != -1)
                            i++;
                    }

                    for (; i < len; ++i)
                    {
                        char c = schlafliString.charAt(i);
                        if ("[(<.*".indexOf(c) != -1)
                        {
                            orbifoldSymbol = schlafliString.substring(i);
                            schlafliString = schlafliString.substring(0,i);
                            break;
                        }
                    }
                }

  String[] schlafliTokens;
  {
      java.util.StringTokenizer st =
   new java.util.StringTokenizer(schlafliString," ;:_^+");
      schlafliTokens = new String[st.countTokens()];
      for (int i = 0; st.hasMoreTokens(); ++i)
   schlafliTokens[i] = st.nextToken();
  }
  dim = schlafliTokens.length;
  if (dim >= 1)
  {



      {

                        if (schlafliTokens[0].startsWith("(")
                         && schlafliTokens[0].endsWith(")"))
                            schlafliTokens[0] = schlafliTokens[0].substring(1, schlafliTokens[0].length()-1);

   java.util.StringTokenizer st =
       new java.util.StringTokenizer(schlafliTokens[0], ",");
   int nps = st.countTokens();
   schlafli_pp = new int[nps];
   backEdgeInds = new int[nps];
   for (int i = 0; st.hasMoreTokens(); ++i)
   {
       String pToken = st.nextToken();




                            if (pToken.startsWith("<")
                             && pToken.endsWith(">"))
                            {
                                pToken = pToken.substring(1,pToken.length()-1);
                            }

       {
    schlafli_pp[i] = Integer.decode(pToken).intValue();
    backEdgeInds[i] = i;
       }
   }
      }
  }
  if (dim >= 2)
  {
  		schlafli_q = Integer.decode(schlafliTokens[1]).intValue();
  }
  if (dim >= 3)
  {
      schlafli_r = Integer.decode(schlafliTokens[2]).intValue();
  }


                if (dim == 1
                 && schlafli_pp.length >= 3)
                {
                    dim = 2;
                    schlafli_q = 1;
                }
  if (dim != 2)
  {

      HyperbolicUtils.set_exception("HyperbolicApplet.java","ana_schlafli","Schlafli symbol \""+schlafliString+"\" has "+schlafliTokens.length+" space-or-semicolon separated tokens implying dim="+dim+", but only supported dim is 2");
  }

                if (orbifoldSymbol != null)
                {




                    int len = orbifoldSymbol.length();
                    for (int i = 0; (i) < (len); ++i)
                    {
                        char c = orbifoldSymbol.charAt(i);
                        char endChar =
                            c == '[' ? ']' :
                            c == '(' ? ')' :
                            c == '<' ? '>' :
                            0;
                        if (endChar != 0)
                        {
                            int j;
                            for (j = i+1; j <= len; ++j)
                            {
                                c = (j == len ? '\0' : orbifoldSymbol.charAt(j));
                                if (c == endChar
                                 || "[]()<>.*".indexOf(c) != -1
                                 || j == len)
                                {
                                    String bracketContents = orbifoldSymbol.substring(i+1, j);
                                    java.util.StringTokenizer st =
                                        new java.util.StringTokenizer(bracketContents, " ;_,");
                                    int n = st.countTokens();
                                    if (n == 1)
                                    {
                                        int ind = Integer.decode(st.nextToken()).intValue();
                                        if (!((0 <=(ind))&&((ind)< schlafli_pp.length)))
                                            HyperbolicUtils.set_exception("HyperbolicApplet.java","ana_schlafli","Orbifold symbol refers to index "+ind+" which is out of bounds [0.."+(schlafli_pp.length-1)+"]!");

                                        backEdgeInds[ind] = (endChar==')' ? ~ind : ind);
                                    }
                                    else if (n == 2)
                                    {
                                        int ind0 = Integer.decode(st.nextToken()).intValue();
                                        int ind1 = Integer.decode(st.nextToken()).intValue();
                                        if (!((0 <=(ind0))&&((ind0)< schlafli_pp.length)))
                                            HyperbolicUtils.set_exception("HyperbolicApplet.java","ana_schlafli","Orbifold symbol refers to index "+ind0+" which is out of bounds [0.."+(schlafli_pp.length-1)+"]!");
                                        if (!((0 <=(ind1))&&((ind1)< schlafli_pp.length)))
                                            HyperbolicUtils.set_exception("HyperbolicApplet.java","ana_schlafli","Orbifold symbol refers to index "+ind1+" which is out of bounds [0.."+(schlafli_pp.length-1)+"]!");
                                        backEdgeInds[ind0] = (endChar==')' ? ~ind1 : ind1);
                                        backEdgeInds[ind1] = (endChar==')' ? ~ind0 : ind0);
                                    }
                                    else
                                        HyperbolicUtils.set_exception("HyperbolicApplet.java","ana_schlafli","Orbifold symbol \""+orbifoldSymbol+"\" is malformed! (should be 1 or 2 items per cycle)");
                                    if (c == endChar)
                                        i = j;
                                    else
                                        i = j-1;
                                    break;
                                }
                            }
                        }
                        else if ("[]()<>.*".indexOf(c) != -1)
                        {
                            {System.out.print("    ");System.out.println("i" + " = " + (i));};
                            {System.out.print("    ");System.out.println("c" + " = " + (c));};
                            HyperbolicUtils.set_exception("HyperbolicApplet.java","ana_schlafli","Orbifold symbol \""+orbifoldSymbol+"\" is malformed!");
                        }

                    }
                }

  if (nonEventVerbose >= 1)
  {
      System.out.println("    dim = "+dim);
      System.out.print("    p = ");
      int i;
      for (i = 0; (i) < (schlafli_pp.length); ++i)
      {
   System.out.print(schlafli_pp[i]);
   if (i+1 < schlafli_pp.length)
       System.out.print(",");
      }
      System.out.println();
      System.out.println("    q = "+schlafli_q);
  }
     }



 public void init()
 {
/* bouton+ */
bantialiasing.addActionListener(new fantialiasing());
bdoquality.addActionListener(new fdoquality());
bepaissir_trait.addActionListener(new fepaissir_trait());
bdesepaissir_trait.addActionListener(new fdesepaissir_trait());
brandom_jitter.addActionListener(new frandom_jitter());
bisometry_label.addActionListener(new fisometry_label());
baugmente_nombre_cote.addActionListener(new faugmente_nombre_cote());
bdiminue_nombre_cote.addActionListener(new fdiminue_nombre_cote());
baugmente_nombre_polygone.addActionListener(new faugmente_nombre_polygone());
bdiminue_nombre_polygone.addActionListener(new fdiminue_nombre_polygone());
baugmente_nombre_cote.addActionListener(new faugmente_nombre_cote());
bgraphe.addActionListener(new fgraphe());
baugmente_nombre_cote.addActionListener(new faugmente_nombre_cote());
bdiminue_nombre_cote.addActionListener(new fdiminue_nombre_cote());
bprimal.addActionListener(new fprimal());
bdual.addActionListener(new fdual());
bdraw_snub.addActionListener(new fdraw_snub());
bsnub_parity.addActionListener(new fsnub_parity());
baugmente_nombre_cote.addActionListener(new faugmente_nombre_cote());
buniformise_snub.addActionListener(new funiformise_snub());
bmodele.addActionListener(new fmodele());
bchoix_troncation.addActionListener(new fchoix_troncation());
baugmente_profondeur.addActionListener(new faugmente_profondeur());
bdiminue_profondeur.addActionListener(new fdiminue_profondeur());
bpavage.addActionListener(new fpavage());
bpavage_regulier.addActionListener(new fpavage_regulier());
bpavage_mixte.addActionListener(new fpavage_mixte());
bhelp.addActionListener(new fhelp());
/* bouton- */

     eventVerbose = getParameterInt("EventVerbose", eventVerbose);
     if (eventVerbose >= 1) System.out.println("in init");

            nonEventVerbose = getParameterInt("nonEventVerbose", nonEventVerbose);
            maxLevels = getParameterInt("maxLevels", maxLevels);
            maxIsometries = getParameterInt("maxIsometries", maxIsometries);
            doRandomJitter = getParameterBoolean("doRandomJitter", doRandomJitter);
            showIsometryLabels = getParameterInt("showIsometryLabels", showIsometryLabels);
            lineThicknessInPixels = getParameterDouble("lineThicknessInPixels", lineThicknessInPixels);
            drawPrimal = getParameterBoolean("drawPrimal", drawPrimal);
            drawDual = getParameterBoolean("drawDual", drawDual);
            drawSnub = getParameterBoolean("drawSnub", drawSnub);

     {
  String schlafliString = getParameterString("symbol","7 3"); /* "(3,6,6,6)^1 [0][1][2][3]"); */
             ana_schlafli(schlafliString);

 
}
            if (schlafli_pp.length == 1)
            {
                currentOperation = getParameterInt("currentOperation", PRIMAL);
                currentOperation = (((currentOperation)+(wythoffChoices.length))%(wythoffChoices.length));
                currentWythoff = wythoffChoices[currentOperation];
            }
            else
            {
                currentOperation = OMNITRUNCATED;
                currentWythoff = null;
            }

            String currentIsometryParam = getParameter("currentIsometry");
            if (currentIsometryParam != null)
            {
                try
                {
                    double foo[] = (double[])Arrays.fromString(currentIsometryParam);
                    System.out.println("    " + "foo" + " = " + Arrays.toStringCompact(foo));
                    currentIsometry = new Isometry2(new Complex(foo[0],foo[1]),
                                                    new Complex(foo[2],foo[3]),
                                                    foo.length>4 ? (int)foo[4] : 1);
                }
                catch (java.text.ParseException e)
                {
                    System.out.println("couldn't parse currentIsometry \""+currentIsometryParam+"\"");
                }
            }

            if (drawSnub &&
               !(schlafli_pp.length == 1 || is_all_even(schlafli_pp)))
            {
                HyperbolicUtils.message("Sorry, can't draw snub unless tiling is regular or all faces are even");
                drawSnub = false;
            }




            this.addMouseListener(this);
            this.addMouseMotionListener(this);
            this.addKeyListener(this);  /* bouton+- */

            graphicsAntiAliasingSetter = new GraphicsAntiAliasingSetter();

     if (eventVerbose >= 1) System.out.println("out init");
 }

        public void start()
        {
            if (eventVerbose >= 1) System.out.println("in start");
            if (eventVerbose >= 1) System.out.println("out start");
        }
        public void stop()
        {
            if (eventVerbose >= 1) System.out.println("in stop");
            if (eventVerbose >= 1) System.out.println("out stop");
        }
        public void destroy()
        {
            if (eventVerbose >= 1) System.out.println("in destroy");
            if (eventVerbose >= 1) System.out.println("out destroy");
        }







        private static int getButton(java.awt.event.MouseEvent e)
        {




            int mods = e.getModifiers();

            if ((mods & java.awt.event.InputEvent.ALT_MASK) != 0)
                return 2;
            if ((mods & java.awt.event.InputEvent.META_MASK) != 0)
                return 3;
            return 1;
        }




        public void mousePressed(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in mousePressed: "+e);
            if (eventVerbose >= 1)
            {
                if (getButton(e) == 1)
                    System.out.println("    (left mouse)");
                if (getButton(e) == 2)
                    System.out.println("    (middle mouse)");
                if (getButton(e) == 3)
                    System.out.println("    (right mouse)");
            }

            prevX = e.getX();
            prevY = e.getY();

            int whichButton = getButton(e);
            if (whichButton == 1)
            {
                button1IsDown = true;
            }
            else if (whichButton == 2)
            {
                button2IsDown = true;
            }
            else if (whichButton == 3)
            {
                button3IsDown = true;

                java.awt.Dimension size = getSize();
                Complex p = new Complex(
                                ((-1.) + ((double)prevX/(size.width-1))*((1.)-(-1.))),
                                ((-1.) + (1.-(double)prevY/(size.height-1))*((1.)-(-1.))));
                if (hyperbolicModel == HyperbolicUtils.KLEIN_DISK)
                    Complex.k2p(p);

                this.changeCurrentWythoffSoCanonicalVertIsAt(p);
                repaint();

            }
            if (doQuality == 1)
                repaint();

            if (eventVerbose >= 1) System.out.println("out mousePressed: "+e);
        }
        public void mouseReleased(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in mouseReleased: "+e);

            int whichButton = getButton(e);
            if (whichButton == 1)
            {
                button1IsDown = false;
            }
            else if (whichButton == 2)
            {
                button2IsDown = false;
            }
            else if (whichButton == 3)
            {
                button3IsDown = false;
            }
            if (doQuality == 1)
                repaint();

            if (eventVerbose >= 1) System.out.println("out mouseReleased: "+e);
        }
        public void mouseEntered(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in mouseEntered: "+e);
            if (eventVerbose >= 1) System.out.println("out mouseEntered: "+e);
        }
        public void mouseExited(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in mouseExited: "+e);
            if (eventVerbose >= 1) System.out.println("out mouseExited: "+e);
        }
        public void mouseClicked(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in mouseClicked: "+e);
            if (eventVerbose >= 1) System.out.println("out mouseClicked: "+e);
        }
        public void mouseDragged(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 2) System.out.println("  in mouseDragged: "+e);

            if (button3IsDown)
            {
                int thisX = e.getX();
                int thisY = e.getY();
                java.awt.Dimension size = getSize();
                Complex p = new Complex(
                                ((-1.) + ((double)thisX/(size.width-1))*((1.)-(-1.))),
                                ((-1.) + (1.-(double)thisY/(size.height-1))*((1.)-(-1.))));
                if (hyperbolicModel == HyperbolicUtils.KLEIN_DISK)
                    Complex.k2p(p);
                this.changeCurrentWythoffSoCanonicalVertIsAt(p);
                repaint();
            }


            if (button1IsDown)
            {
                int thisX = e.getX();
                int thisY = e.getY();

                java.awt.Dimension size = getSize();
                Complex pp = new Complex(
                                    ((-1.) + ((double)prevX/(size.width-1))*((1.)-(-1.))),
                                    ((-1.) + (1.-(double)prevY/(size.height-1))*((1.)-(-1.))));
                Complex p = new Complex(
                                    ((-1.) + ((double)thisX/(size.width-1))*((1.)-(-1.))),
                                    ((-1.) + (1.-(double)thisY/(size.height-1))*((1.)-(-1.))));
                if (hyperbolicModel == HyperbolicUtils.KLEIN_DISK)
                {
                    Complex.k2p(p);
                    Complex.k2p(pp);
                }

                if (eventVerbose >= 1)
                {
                    {System.out.print("    ");System.out.println("pp" + " = " + (pp));};
                    {System.out.print("    ");System.out.println("p" + " = " + (p));};
                }

                Isometry2 increment;
                switch (motionModel)
                {
                    case TRACKBALL:

                        increment = new Isometry2(Complex.one,
                                                  Complex.sub(p,pp),
                                                  1);
                        break;
                    case DRAG_GEODESIC:


                        increment =
                            Isometry2.mul(Isometry2.mul(
                                new Isometry2(Complex.one,pp,1),
                                new Isometry2(Complex.one,
                                              new Isometry2(Complex.one,
                                                            Complex.neg(pp),
                                                            1).apply(p),
                                              1)),
                                new Isometry2(Complex.one,
                                              Complex.neg(pp),
                                              1));
                        break;
                    case DRAG_NONPRECESSING:
                        increment =
                            Isometry2.mul(new Isometry2(Complex.one,p,1),
                                          new Isometry2(Complex.one,
                                                        Complex.neg(pp),
                                                        1));
                        break;
                    case DRAG_PURETRANSLATE_CLAMPED:
                        {
                            double max = .99;
                            if ((((pp.x)*(pp.x))+((pp.y)*(pp.y))) > max*max)
                            {
                                double scale = max / MyMath.hypot(pp.x,pp.y);
                                pp.x *= scale;
                                pp.y *= scale;
                            }
                            if ((((p.x)*(p.x))+((p.y)*(p.y))) > max*max)
                            {
                                double scale = max / MyMath.hypot(p.x,p.y);
                                p.x *= scale;
                                p.y *= scale;
                            }
                        }

                    case DRAG_PURETRANSLATE:



                        increment = Isometry2.pureTranslation(pp,p);
                        break;
                    default:
                        { if (!(false)) HyperbolicUtils.set_exception("HyperbolicApplet.java","mouseDragged" , "case motionModel default"); 
                        increment = Isometry2.pureTranslation(pp,p); /* juste pour l'initialiser pour un test du compilateur*/};
                }
                currentIsometry = Isometry2.mul(increment, currentIsometry);

                prevX = thisX;
                prevY = thisY;

                repaint();
            }

            if (eventVerbose >= 2) System.out.println("  out mouseDragged: "+e);
        }
        public void mouseMoved(java.awt.event.MouseEvent e)
        {
            if (eventVerbose >= 3) System.out.println("    in mouseMoved: "+e);
            if (eventVerbose >= 3) System.out.println("    out mouseMoved: "+e);
        }

   

/* bouton+ */
        public void keyPressed(java.awt.event.KeyEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in keyPressed: "+e);
            if (eventVerbose >= 1) System.out.println("out keyPressed: "+e);
        }
        public void keyReleased(java.awt.event.KeyEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in keyReleased: "+e);
            if (eventVerbose >= 1) System.out.println("out keyReleased: "+e);
        }
        public void keyTyped(java.awt.event.KeyEvent e)
        {
            if (eventVerbose >= 1) System.out.println("in keyTyped: "+e);
            switch (e.getKeyChar())
            {
                case 'V':
                    System.out.print("eventVerbose "+eventVerbose);
                    eventVerbose = (eventVerbose+1) % 4;
                    System.out.println(" -> "+eventVerbose);
                    break;

                case ' ':
                    repaint();
                    break;
                case 'A':
                    doAntiAliasing ^= true;
                    System.out.println("doAntiAliasing -> "+doAntiAliasing+" (if this platform supports it)");
                    repaint();
                    break;
                case 'u':
                    System.out.print("doQuality "+doQuality);
                    doQuality = (doQuality+1) % 3;
                    System.out.println(" -> "+doQuality);
                    repaint();
                    break;

                case 't':
                    System.out.print("lineThicknessInPixels "+lineThicknessInPixels);
                    lineThicknessInPixels += 1.;
                    System.out.println(" -> "+lineThicknessInPixels);
                    repaint();
                    break;
                case 'T':
                    System.out.print("lineThicknessInPixels "+lineThicknessInPixels);
                    lineThicknessInPixels -= 1.;
                    System.out.println(" -> "+lineThicknessInPixels);
                    repaint();
                    break;

                case 'J':
                    doRandomJitter ^= true;
                    System.out.println("doRandomJitter -> "+doRandomJitter);
                    repaint();
                    break;
                case 'L':
                    System.out.print("showIsometryLabels "+showIsometryLabels);
                    showIsometryLabels = (showIsometryLabels+1) % 3;
                    System.out.println(" -> "+showIsometryLabels);
                    repaint();
                    break;

                case 'B':
                    doDoubleBuffer ^= true;
                    System.out.println("doDoubleBuffer -> "+doDoubleBuffer);
                    repaint();
                    break;
                case 'r':
                    currentIsometry = Isometry2.identity;
                    repaint();
                    break;

                case 'p':
                    for (int i = 0; (i) < (schlafli_pp.length); ++i)
                        ++schlafli_pp[i];
                    repaint();
                    break;
                case 'P':
                    for (int i = 0; (i) < (schlafli_pp.length); ++i)
                        --schlafli_pp[i];

                    if (HyperbolicUtils.calcUniformTilingHalfEdgeLength(schlafli_pp, schlafli_q, 1) == 0.)

                        for (int i = 0; (i) < (schlafli_pp.length); ++i)
                            ++schlafli_pp[i];
                    else
                        repaint();
                    break;
                case 'q':
                    ++schlafli_q;
                    repaint();
                    break;
                case 'Q':
                    --schlafli_q;

                    if (HyperbolicUtils.calcUniformTilingHalfEdgeLength(schlafli_pp, schlafli_q, 1) == 0.)

                        ++schlafli_q;
                    else
                        repaint();
                    break;



                case 'c':
                    doCompleteGraph ^= true;
                    System.out.println("doCompleteGraph -> "+doCompleteGraph);
                    repaint();
                    break;

                case 'x':
                    if (e.isShiftDown())
                        windowDumpRequested = true;
                    else
                        psDumpRequested = true;
                    repaint();
                    break;

                case 'h':
                    drawPrimal ^= true;
                    System.out.println("drawPrimal -> "+drawPrimal);
                    repaint();
                    break;
                case 'd':
                    drawDual ^= true;
                    System.out.println("drawDual -> "+drawDual);
                    repaint();
                    break;
                case 's':
                    if (e.isShiftDown())
                    {
                        snubParity ^= 1;
                        System.out.println("snubParity -> "+snubParity);
                        repaint();
                    }
                    else
                        if (schlafli_pp.length == 1 || is_all_even(schlafli_pp))
                        {
                            drawSnub ^= true;
                            System.out.println("drawSnub -> "+drawSnub);
                            repaint();
                        }
                        else
                            HyperbolicUtils.message("Sorry, can't draw snub unless it's regular or all faces are even");
                    break;
                case 'k':
                    if (schlafli_pp.length == 1 ||
                        is_all_even(schlafli_pp) && VecMath.isIdentityPerm(backEdgeInds))
                    {
                        double newWythoff[] = new double[currentPickableSchwarzPolygon.SchwarzPolygon.length];
                        if (HyperbolicUtils.findBaryCoordsThatMakeSnubUniform(
                                     newWythoff,
                                     currentPickableSchwarzPolygon.SchwarzPolygon,
                                     nonEventVerbose))
                        {
                            System.out.println("    " + "newWythoff" + " = " + Arrays.toStringCompact(newWythoff));
                            currentWythoff = newWythoff;
                            currentOperation = OMNITRUNCATED;
                            repaint();
                        }
                        else
                        {
                            HyperbolicUtils.message("Sorry, failed to make snub uniform for some reason");
                        }
                    }
                    else
                    {
                        HyperbolicUtils.message("Sorry, can't make snub uniform unless it's regular, or all faces are even and all generating isometries are mirror-edge reflections");
                    }
                    break;

                case 'm':
                    if (hyperbolicModel == HyperbolicUtils.POINCARE_DISK)
                        hyperbolicModel = HyperbolicUtils.KLEIN_DISK;
                    else
                        hyperbolicModel = HyperbolicUtils.POINCARE_DISK;
                    repaint();
                    break;

                case 'o':
                    if (schlafli_pp.length == 1)
                    {
                        if (currentWythoff == wythoffChoices[currentOperation])
                            currentOperation = (currentOperation+1) % wythoffChoices.length;
                        currentWythoff = wythoffChoices[currentOperation];
                    }
                    else
                    {

                        currentWythoff = null;
                    }
                    repaint();
                    break;
                case 'O':
                    if (schlafli_pp.length == 1)
                    {
                        if (currentWythoff == wythoffChoices[currentOperation])
                            currentOperation = (((currentOperation-1)+(wythoffChoices.length))%(wythoffChoices.length));
                        currentWythoff = wythoffChoices[currentOperation];
                    }
                    else
                    {

                        currentWythoff = null;
                    }
                    repaint();
                    break;

                case 'i':
                case 'g':
                    System.out.print("maxIsometries "+maxIsometries);
                    maxIsometries += (e.isShiftDown() ? -1 : 1)
                                   * (e.isControlDown() ? 1 : 10);
                    if (maxIsometries < 0)
                        maxIsometries = 0;
                    System.out.println(" -> "+maxIsometries);
                    repaint();
                    break;

                case 'I':
                    System.out.print("maxIsometries "+maxIsometries);
                    maxIsometries -= (e.isControlDown() ? 1 : 10);
                    if (maxIsometries < 0)
                        maxIsometries = 0;
                    System.out.println(" -> "+maxIsometries);
                    repaint();
                    break;

                default:
                    System.out.println("Unknown key '"+e.getKeyChar()+"'("+(int)e.getKeyChar()+") typed");
                    break;
            }
            if (eventVerbose >= 1) System.out.println("out keyTyped: "+e);
        }
 /* bouton- */

        public boolean isFocusTraversable()
        {
            return true;
        }


        public void update(java.awt.Graphics g)
        {
            paint(g);
        }



 public void paint(java.awt.Graphics frontBufferGraphics)
 {
     if (eventVerbose >= 1) System.out.println("in paint");

            makeSureBackBufferIsRight();
            java.awt.Graphics g;
            if (doDoubleBuffer)
                g = backBufferImage.getGraphics();
            else
                g = frontBufferGraphics;


            java.awt.Dimension size = getSize();
            double minSize = ((size.width)<=(size.height)?(size.width):(size.height));
            MyGraphics mg = new MyGraphics(g, size,
                                    -size.width/minSize,size.width/minSize,
                                    -size.height/minSize,size.height/minSize);

            if (psDumpRequested)
            {
                psDumpRequested = false;
                String fileName = "PSDUMP.eps";

                try {
                    java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.BufferedWriter(new java.io.FileWriter(fileName)));
                    System.out.println("Dumping postscript to "+fileName+"...");
                    mg = new PSMyGraphics(writer, size,
                                        -size.width/minSize,size.width/minSize,
                                        -size.height/minSize,size.height/minSize);
                } catch (java.io.IOException e)
                {
                    System.out.println("Couldn't write to file "+fileName+": "+e);
                }
            }

            if (implementAntiAliasingInHardware)
            {
                graphicsAntiAliasingSetter.setAntiAliasing(g,
                                                           doAntiAliasing,

                                                           eventVerbose
                                                           );
            }




     mg.setColor(java.awt.Color.black);
            mg.fillWindow();

            Isometry2 smallestIsometry = new Isometry2(currentIsometry);
            boolean doCurvedLines = doQuality >= 2
                                 || doQuality >= 1 && !(button1IsDown||button3IsDown);

            if (doCompleteGraph)
            {
                int n = schlafli_pp[0];
                double verts[][] = new double[n][2];
                for (int i = 0; (i) < (n); ++i)
                {
                    double ang = 2*Math.PI*i/n;
                    verts[i][0] = Math.cos(ang);
                    verts[i][1] = Math.sin(ang);
                }
                VecMath.mxs(verts, verts, .9999);

                mg.setColor(java.awt.Color.white);
                for (int i = 0; (i) < (n); ++i)
                for (int j = 0; (j) < (i); ++j)
                {
                    double a[] = verts[i];
                    double b[] = verts[j];
                    HyperbolicDrawUtils.DrawLine2fPoincare(mg,
                        a[0],a[1],b[0],b[1],
                        0.,
                        hyperbolicModel,
                        doCurvedLines,
                        doAntiAliasing && !implementAntiAliasingInHardware,
                        doRandomJitter,
                        lineThicknessInPixels);
                }
            }
            else if (schlafli_pp.length == 1)
            {
                HyperbolicDrawUtils.DrawOmnitruncatedTiling(
                                        mg,
                                        hyperbolicModel,
                                        currentIsometry,
                                        schlafli_pp[0], schlafli_q,
                                        maxLevels,
                                        maxIsometries,
                                        currentWythoff,
                                        drawPrimal,
                                        1.f, 1.f, 1.f,
                                        drawDual,
                                        0.f, 0.f, 1.f,
                                        drawSnub,
                                        1.f, 0.f, 0.f,
                                        snubParity,
                                        doCurvedLines,
                                        doAntiAliasing && !implementAntiAliasingInHardware,
                                        doRemoveDups,
                                        doRandomJitter,
                                        lineThicknessInPixels,
                                        showIsometryLabels,
                                        nonEventVerbose,
                                        smallestIsometry,
                                        currentPickableSchwarzPolygon);
            }
            else
            {
                HyperbolicDrawUtils.DrawUniformTiling(
                                        mg,
                                        hyperbolicModel,
                                        currentIsometry,
                                        schlafli_pp, schlafli_q, backEdgeInds,
                                        maxLevels,
                                        maxIsometries,
                                        currentWythoff,
                                        drawPrimal,
                                        1.f, 1.f, 1.f,
                                        drawDual,
                                        0.f, 0.f, 1.f,
                                        drawSnub,
                                        1.f, 0.f, 0.f,
                                        snubParity,
                                        doCurvedLines,
                                        doAntiAliasing && !implementAntiAliasingInHardware,
                                        doRemoveDups,
                                        doRandomJitter,
                                        lineThicknessInPixels,
                                        showIsometryLabels,
                                        nonEventVerbose,
                                        smallestIsometry,
                                        currentPickableSchwarzPolygon);
            }
            currentIsometry = smallestIsometry;

            mg.flush();
            if (mg instanceof PSMyGraphics)
            {
                System.out.println("done.");
            }

            if (g != frontBufferGraphics)
            {
                frontBufferGraphics.drawImage(backBufferImage, 0, 0, this);
                if (windowDumpRequested)
                {
                    windowDumpRequested = false;
                    String lineThicknessInPixelsString = (int)lineThicknessInPixels == lineThicknessInPixels ? ""+(int)lineThicknessInPixels : ""+lineThicknessInPixels;
                    String fileName = "WINDOWDUMP."+size.width+"x"+size.height+"."+lineThicknessInPixelsString+".ppm";
                    try {
                        java.io.BufferedOutputStream ostream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(fileName));
                        System.out.println("    "+size.width+"x"+size.height+"");
                        System.out.println("    currentIsometry="+currentIsometry+"");
                        System.out.println("Dumping window to "+fileName+"...");


                        int w = size.width;
                        int h = size.height;
                        int pixels[] = new int[w*h];
                        int off = 0;
                        int stride = w;
                        java.awt.image.PixelGrabber grabber = new java.awt.image.PixelGrabber(backBufferImage, 0, 0, w, h, pixels, off, stride);

                        try {
                            System.out.print("    grabbing...");
                            System.out.flush();
                            grabber.grabPixels();
                            System.out.println("done.");

                            System.out.print("    fixing");
                            System.out.flush();
                            {




                                int white = 0xffffffff;
                                int black = 0xff000000;
                                int blue = 0xff0000ff;
                                int outerblack = 0x00000000;

                                int stack[][] = new int[w*h][2];


                                seedfill(pixels, w, h,
                                         0, 0, outerblack, stack);
                                System.out.print(".");
                                System.out.flush();
                                seedfill(pixels, w, h,
                                         w-1, 0, outerblack, stack);
                                System.out.print(".");
                                System.out.flush();
                                seedfill(pixels, w, h,
                                         0, h-1, outerblack, stack);
                                System.out.print(".");
                                System.out.flush();
                                seedfill(pixels, w, h,
                                         w-1, h-1, outerblack, stack);
                                System.out.print(".");
                                System.out.flush();




                                int q = (int)Math.ceil(lineThicknessInPixels)+1;
                                for (int y = 0; (y) < (h); ++y)
                                for (int x = 0; (x) < (w); ++x)
                                {
                                    if (pixels[y*w+x] == black
                                     && (y < q || y >= h-q
                                      || x < q || x >= w-q
                                      || pixels[(y-q)*w+x] == outerblack
                                      || pixels[(y+q)*w+x] == outerblack
                                      || pixels[y*w+(x-q)] == outerblack
                                      || pixels[y*w+(x+q)] == outerblack
                                      || pixels[(y-q/2)*w+(x-q/2)] == outerblack
                                      || pixels[(y-q/2)*w+(x+q/2)] == outerblack
                                      || pixels[(y+q/2)*w+(x-q/2)] == outerblack
                                      || pixels[(y+q/2)*w+(x+q/2)] == outerblack))
                                        seedfill(pixels, w, h, x, y, blue, stack);
                                }



                            }
                            System.out.println(" done.");

                            System.out.print("    writing...");
                            System.out.flush();



                            {
                                String header = "P6\n"+w+" "+h+"\n255\n";
                                ostream.write(header.getBytes());
                            }

                            for (int y = 0; (y) < (h); ++y)
                            for (int x = 0; (x) < (w); ++x)
                            {
                                int pix = pixels[y*w + x];
                                int alpha = (pix>>24) & 0xff;
                                int red = (pix>>16) & 0xff;
                                int green = (pix>>8) & 0xff;
                                int blue = (pix>>0) & 0xff;
                                ostream.write(red);
                                ostream.write(green);
                                ostream.write(blue);
                            }
                            ostream.flush();
                            System.out.println("done.");

                            System.out.println("done.");
                        }
                        catch (InterruptedException e)
                        {
                            System.err.println("interrupted waiting for pixels!");
                        }

                    } catch (java.io.IOException e)
                    {
                        System.out.println("Couldn't write to file "+fileName+": "+e);
                    }
                }
            }

     if (eventVerbose >= 1) System.out.println("out paint");
 }


        static void seedfill(int pixels[], int w, int h, int x0, int y0, int newColor, int stack[ ][ ])
        {
            final int deltas[][] = {{1,0}, {0,1}, {-1,0}, {0,-1}};
            if (false)
            {

                int oldColor = pixels[y0*w+x0];
                pixels[y0*w+x0] = newColor;
                for (int i = 0; (i) < (4); ++i)
                {
                    int dx = deltas[i][0];
                    int dy = deltas[i][1];
                    int x = x0+dx;
                    int y = y0+dy;
                    if (((0 <=(x))&&((x)< w))
                     && ((0 <=(y))&&((y)< h))
                     && pixels[y*w+x] == oldColor)
                        seedfill(pixels, w, h, x, y, newColor, stack);
                }
            }
            else
            {
                int oldColor = pixels[y0*w+x0];
                if (oldColor != newColor)
                {
                    int nStack = 0;


                    pixels[y0*w+x0] = newColor;
                    stack[nStack][0] = x0;
                    stack[nStack][1] = y0;
                    nStack++;
                    while (nStack > 0)
                    {

                        nStack--;
                        int x = stack[nStack][0];
                        int y = stack[nStack][1];

                        for (int i = 0; (i) < (4); ++i)
                        {
                            int dx = deltas[i][0];
                            int dy = deltas[i][1];
                            int x1 = x+dx;
                            int y1 = y+dy;
                            if (((0 <=(x1))&&((x1)< w))
                             && ((0 <=(y1))&&((y1)< h))
                             && pixels[y1*w+x1] == oldColor)
                            {
                                pixels[y1*w+x1] = newColor;
                                stack[nStack][0] = x1;
                                stack[nStack][1] = y1;
                                nStack++;
                            }
                        }
                    }
                }
            }
        }


        private void makeSureBackBufferIsRight()
        {
            if (doDoubleBuffer)
            {
                java.awt.Dimension size = getSize();
                if (backBufferImage == null
                 || backBufferImage.getWidth(this) != size.width
                 || backBufferImage.getHeight(this) != size.height)
                {
                    if (eventVerbose >= 1)
                    {
                        System.out.println("Creating back buffer " + size.width + "x" + size.height);
                    }
                    backBufferImage = createImage(size.width, size.height);
                }
            }
            else
                backBufferImage = null;
        }


        private static boolean
        is_all_even(int array[])
        {
            for (int i = 0; (i) < (array.length); ++i)
                if ((array[i] & 1) != 0)
                    return false;
            return true;
        }

        private void
        changeCurrentWythoffSoCanonicalVertIsAt(Complex p)
        {
            if (schlafli_pp.length != 1)
            {


                if (!VecMath.isIdentityPerm(backEdgeInds))
                    return;
                if (!is_all_even(schlafli_pp))
                    return;
            }

            this.currentOperation = OMNITRUNCATED;
            Isometry2 isometryTakingPToSchwarzPolygon = new Isometry2();
            int nFlips[] = new int[1];
            if (currentPickableSchwarzPolygon.pick(p,
                                               isometryTakingPToSchwarzPolygon,
                                               nonEventVerbose,
                                               nFlips))
            {
                Complex q = isometryTakingPToSchwarzPolygon.apply(p);
                this.currentWythoff = new double[3];
                if (HyperbolicUtils.unHBary2(currentWythoff.length,
                             currentWythoff,
                             q,
                             currentPickableSchwarzPolygon.SchwarzPolygon,
                             nonEventVerbose))
                {

                    if (nonEventVerbose >= 1)
                        System.out.println("    " + "currentWythoff" + " = " + Arrays.toStringCompact(currentWythoff));
                }
                else
                {
                    currentWythoff = wythoffChoices[currentOperation];
                    if (currentOperation == OMNITRUNCATED
                     && schlafli_pp.length * schlafli_q != 3)
                        currentWythoff = null;
                }
            }
        }





    private final double wythoffChoices[][] = {
                {1,0,0},
                {.75,.25,0},
                {.5,.5,0},
                {.25,.75,0},
                {0,1,0},
                {0,.75,.25},
                {0,.5,.5},
                {0,.25,.75},
                {0,0,1},
                {.25,0,.75},
                {.5,0,.5},
                {.75,0,.25},
                {1,1,1},
                {1,1,0.75},
                {1,1,0.5},
                {1,1,0.25},
                {1,1,0.001},
                {1,1,0},
                {1,0,1},
                {0,1,1},
    };
    public static final int PRIMAL = 0;
    public static final int DUAL = 8;
    public static final int RUNCINATED = 10;
    public static final int OMNITRUNCATED = 12;





    GraphicsAntiAliasingSetter graphicsAntiAliasingSetter;

    int dim, schlafli_pp[], schlafli_q, schlafli_r;
    int currentOperation;
    double currentWythoff[];
    int backEdgeInds[];
    int eventVerbose = 0;
    int nonEventVerbose = 0;
    int maxLevels = 100;
    int maxIsometries = 500;
    int doQuality = 1;

    boolean drawPrimal = true;
    boolean drawDual = true;
    boolean drawSnub = false;
    int snubParity = 0;

    boolean doAntiAliasing = false;
    boolean doRemoveDups = true;
    boolean doRandomJitter = false;
    double lineThicknessInPixels = 1.;
    int showIsometryLabels = 0;
    boolean implementAntiAliasingInHardware = true;
    boolean doDoubleBuffer = true;
    java.awt.Image backBufferImage = null;

    int hyperbolicModel = HyperbolicUtils.POINCARE_DISK;





    int motionModel = DRAG_PURETRANSLATE_CLAMPED;


    boolean doCompleteGraph = false;

    int prevX = 0;
    int prevY = 0;
    Isometry2 currentIsometry = Isometry2.identity;
    PickableSchwarzPolygon currentPickableSchwarzPolygon = new PickableSchwarzPolygon();

    boolean button1IsDown = false;
    boolean button2IsDown = false;
    boolean button3IsDown = false;

/* bouton+ */
private static JButton bantialiasing = new JButton("<html>Antialiasing</html>");
private static JButton bdoquality = new JButton("<html>Quality</html>");
private static JButton bepaissir_trait = new JButton("<html>Epaissir<br>le trait</html>");
private static JButton bdesepaissir_trait = new JButton("<html>Amincir<br>le trait</html>");
private static JButton brandom_jitter = new JButton("<html>Ajouter<br>jitter</html>");
private static JButton bisometry_label = new JButton("<html>Afficher<br>label des<br>isometries</html>");
private static JButton baugmente_nombre_cote = new JButton("<html>Augmente<br>nombre<br>de cotés</html>");
private static JButton bdiminue_nombre_cote = new JButton("<html>Diminue<br>nombre<br>de cotés</html>");
private static JButton baugmente_nombre_polygone = new JButton("<html>Augmente<br>nombre de<br>polygones</html>");
private static JButton bdiminue_nombre_polygone = new JButton("<html>Diminue<br>nombre de<br>polygones</html>");
private static JButton bgraphe = new JButton("<html>Afficher le graphe</html>");
private static JButton bprimal = new JButton("<html>Dessiner<br>le polygone</html>");
private static JButton bdual = new JButton("<html>Dessiner<br>le dual</html>");
private static JButton bdraw_snub = new JButton("<html>Afficher<br>snub</html>");
private static JButton bsnub_parity = new JButton("<html>Change<br>parité<br>snub</html>");
private static JButton buniformise_snub = new JButton("<html>Uniformiser<br>snub</html>");
private static JButton bmodele = new JButton("<html>Modèle de<br>Poincaré<br> ou de Klein</html>");
private static JButton bchoix_troncation = new JButton("<html>Choix<br>troncation</html>");
private static JButton baugmente_profondeur = new JButton("<html>Augmente<br>profondeur</html>");
private static JButton bdiminue_profondeur = new JButton("<html>Diminue<br>profondeur</html>");
private static JButton bhelp = new JButton("<html>Aide et<br>commentaires</html>");
private static JButton bpavage = new JButton("<html>Pavage<br>par le<br>symbole BCGS</html>");
private static JButton bpavage_regulier = new JButton("<html>Pavage<br>regulier</html>");
private static JButton bpavage_mixte = new JButton("<html>Pavage<br>mixte</html>");
private static JLabel linfos = new JLabel();
private static String str=new String();
/* bouton- */


    public static final int TRACKBALL = 0;
    public static final int DRAG_GEODESIC = 1;
    public static final int DRAG_NONPRECESSING = 2;
    public static final int DRAG_PURETRANSLATE = 3;
    public static final int DRAG_PURETRANSLATE_CLAMPED = 4;


    public static void main(final String[] args)
    {
        System.out.println("in main");

        final java.awt.Frame frame = new java.awt.Frame("Hyperbolic Applet") {
            public boolean handleEvent(java.awt.Event event)
            {
                switch(event.id)
                {
                    case java.awt.Event.WINDOW_DESTROY:
                        System.out.println("bye!");





                        dispose();
                        System.exit(0);
                        return true;
                }
                return super.handleEvent(event);
            }
        };



        frame.addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent we) {
                System.out.println("ciao!");
                frame.dispose();
                System.exit(0);
            }
        });

        final HyperbolicApplet applet = new HyperbolicApplet();


        applet.setStub(new java.applet.AppletStub() {
             public void appletResize(int width, int height)
                 { }
             public java.applet.AppletContext getAppletContext() { return null;}
             public java.net.URL getCodeBase() { return null; }
             public java.net.URL getDocumentBase() { return null; }
             public String getParameter(String name)
             {


                 String prefix = name.toLowerCase() + "=";
                 int i;
                 for (i = 0; (i) < (args.length); ++i)
                     if (args[i].toLowerCase().startsWith(prefix))
                         return args[i].substring(prefix.length());
                 return null;
             }
             public boolean isActive() { return true; }
        });

/* bouton+ */
Dimension dimb = new Dimension(100, 60);
JPanel menu = new JPanel(); 
JPanel menugauche = new JPanel(); 
JPanel menudroit = new JPanel(); 

menu.setPreferredSize(new Dimension(512, 70)); 
menugauche.setPreferredSize(new Dimension(100, 512)); 
menudroit.setPreferredSize(new Dimension(200, 512)); 
linfos.setPreferredSize(new Dimension(200, 150)); 

bantialiasing.setPreferredSize(dimb);
bdoquality.setPreferredSize(dimb);
bepaissir_trait.setPreferredSize(dimb);
bdesepaissir_trait.setPreferredSize(dimb);
brandom_jitter.setPreferredSize(dimb);
bisometry_label.setPreferredSize(dimb);
baugmente_nombre_cote.setPreferredSize(dimb);
bdiminue_nombre_cote.setPreferredSize(dimb);
baugmente_nombre_polygone.setPreferredSize(dimb);
bdiminue_nombre_polygone.setPreferredSize(dimb);
bgraphe.setPreferredSize(dimb);
bprimal.setPreferredSize(dimb);
bdual.setPreferredSize(dimb);
bdraw_snub.setPreferredSize(dimb);
bsnub_parity.setPreferredSize(dimb);
buniformise_snub.setPreferredSize(dimb);
bmodele.setPreferredSize(dimb);
bchoix_troncation.setPreferredSize(dimb);
baugmente_profondeur.setPreferredSize(dimb);
bdiminue_profondeur.setPreferredSize(dimb);
bhelp.setPreferredSize(dimb);
bpavage.setPreferredSize(dimb);
bpavage_regulier.setPreferredSize(dimb);
bpavage_mixte.setPreferredSize(dimb);

applet.setPreferredSize(new Dimension(512,512));

menu.add(bmodele);
menu.add(baugmente_nombre_cote);
menu.add(bdiminue_nombre_cote);
menu.add(baugmente_nombre_polygone);
menu.add(bdiminue_nombre_polygone);

menu.add(bgraphe);
menu.add(bprimal);
menu.add(bdual);
menu.add(baugmente_profondeur);
menu.add(bdiminue_profondeur);
menu.add(bhelp);

menugauche.add(bepaissir_trait);
menugauche.add(bdesepaissir_trait);
menugauche.add(bantialiasing);
menugauche.add(bdoquality);
menugauche.add(brandom_jitter);
menugauche.add(bisometry_label);
menugauche.add(bchoix_troncation);
menugauche.add(bdraw_snub);
menugauche.add(bsnub_parity);
menugauche.add(buniformise_snub);


menudroit.add(bpavage_regulier);
menudroit.add(bpavage_mixte);
menudroit.add(bpavage);
menudroit.add(linfos);


 				frame.setLayout(new BorderLayout()); 	
 				frame.add(menu,BorderLayout.NORTH);	
 				frame.add(menugauche,BorderLayout.WEST);		
 				frame.add(menudroit,BorderLayout.EAST);		
 				
        frame.add(applet,BorderLayout.CENTER);
/* bouton- */

        applet.init();
        applet.start();


        int size = applet.getParameterInt("size", 800);
        int width = applet.getParameterInt("width", size);
        int height = applet.getParameterInt("height", size);



        frame.move(400,20);
        frame.resize(width+500, height+100); /* bouton +- */
        frame.show();

        System.out.println("out main");
    }

    boolean psDumpRequested = false;
    boolean windowDumpRequested = false;

/* bouton+ */
public void maj()
	{
	str="<html>modèle "+hyperbolicModel+"<br>";
	for (int i=0;i<schlafli_pp.length;++i)
		{if (i!=0) {str=str+",";}
		str=str+schlafli_pp[i];}
	str=str+" cotés<br>";
	if (schlafli_pp.length != 1)
		{str=str+schlafli_pp.length+" polygones par sommet<br>";
		str=str+"q="+schlafli_q+"<br>";}
	else
		{str=str+schlafli_q+" polygones par sommet<br>";}
	str=str+"r="+schlafli_r+"<br>";
	str=str+maxIsometries+" isométries<br>";
	str=str+"Wythoff "+currentOperation+" "+wythoffChoices[currentOperation][0]+","+wythoffChoices[currentOperation][1]+","+wythoffChoices[currentOperation][2]+"<br>";
	str=str+"snub "+drawSnub+"<br>";
	str=str+"parité snub "+snubParity+"<br>";
	str=str+"</html>";
	linfos.setText(str);
	linfos.repaint();
	
	repaint();
	}
    
class fmodele implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{ 
                    if (hyperbolicModel == HyperbolicUtils.POINCARE_DISK)
                        hyperbolicModel = HyperbolicUtils.KLEIN_DISK;
                    else
                        hyperbolicModel = HyperbolicUtils.POINCARE_DISK;
                  maj();    
                	} 
              } 
     

class faugmente_nombre_cote implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	for (int i = 0; (i) < (schlafli_pp.length); ++i)
                        ++schlafli_pp[i];
                	maj();   
                	}
           }
class fdiminue_nombre_cote implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	for (int i = 0; (i) < (schlafli_pp.length); ++i)
                        --schlafli_pp[i];

                    if (HyperbolicUtils.calcUniformTilingHalfEdgeLength(schlafli_pp, schlafli_q, 1) == 0.)

                        for (int i = 0; (i) < (schlafli_pp.length); ++i)
                            ++schlafli_pp[i];
                    else
                        maj();
                	}
           }
class faugmente_nombre_polygone implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	++schlafli_q;
                	maj();   
                	}
           }
class fdiminue_nombre_polygone implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	--schlafli_q;

                    if (HyperbolicUtils.calcUniformTilingHalfEdgeLength(schlafli_pp, schlafli_q, 1) == 0.)

                        ++schlafli_q;
                    else
                        maj();  
                	}
           }
class fgraphe implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	doCompleteGraph ^= true;
                    System.out.println("doCompleteGraph -> "+doCompleteGraph);
                	maj();   
                	}
           }
class fprimal implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	drawPrimal ^= true;
                    System.out.println("drawPrimal -> "+drawPrimal);
                	maj();   
                	}
           }
class fdual implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	drawDual ^= true;
                    System.out.println("drawDual -> "+drawDual);
                	maj();   
                	}
           }
class fchoix_troncation implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	if (schlafli_pp.length == 1)
                    {
                        if (currentWythoff == wythoffChoices[currentOperation])
                            currentOperation = (currentOperation+1) % wythoffChoices.length;
                        currentWythoff = wythoffChoices[currentOperation];
                    }
                    else
                    {

                        currentWythoff = null;
                    }
                	maj();   
                	}
           }
class faugmente_profondeur implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	System.out.print("maxIsometries "+maxIsometries);
                    maxIsometries += 10;
                                 
                    if (maxIsometries < 0)
                        maxIsometries = 0;
                    System.out.println(" -> "+maxIsometries);
                	maj();   
                	}
           }
class fdiminue_profondeur implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	System.out.print("maxIsometries "+maxIsometries);
                    maxIsometries -= 10;
                    if (maxIsometries < 0)
                        maxIsometries = 0;
                    System.out.println(" -> "+maxIsometries);
                	maj();   
                	}
           }

class fepaissir_trait implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	System.out.print("lineThicknessInPixels "+lineThicknessInPixels);
                    lineThicknessInPixels += 1.;
                    System.out.println(" -> "+lineThicknessInPixels);
                	maj();   
                	}
           }
class fdesepaissir_trait implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	System.out.print("lineThicknessInPixels "+lineThicknessInPixels);
                    lineThicknessInPixels -= 1.;
                    System.out.println(" -> "+lineThicknessInPixels);
                	maj();   
                	}
           }
class fantialiasing implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	doAntiAliasing ^= true;
                    System.out.println("doAntiAliasing -> "+doAntiAliasing+" (if this platform supports it)");
                	maj();   
                	}
           }
class fdoquality implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	System.out.print("doQuality "+doQuality);
                    doQuality = (doQuality+1) % 3;
                    System.out.println(" -> "+doQuality);
                	maj();   
                	}
           }
class frandom_jitter implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	doRandomJitter ^= true;
                    System.out.println("doRandomJitter -> "+doRandomJitter);
                	maj();   
                	}
           }
class fisometry_label implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	System.out.print("showIsometryLabels "+showIsometryLabels);
                    showIsometryLabels = (showIsometryLabels+1) % 3;
                    System.out.println(" -> "+showIsometryLabels);
                	repaint();   
                	}
           }
class fsnub_parity implements  ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	snubParity ^= 1;
                  System.out.println("snubParity -> "+snubParity);
                  maj();
                  }
           }
class fdraw_snub implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	
                        if (schlafli_pp.length == 1 || is_all_even(schlafli_pp))
                        {
                            drawSnub ^= true;
                            System.out.println("drawSnub -> "+drawSnub);
                            maj();
                        }
                        else
                            HyperbolicUtils.message("Sorry, can't draw snub unless it's regular or all faces are even");   
                	}
           }
class funiformise_snub implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	if (schlafli_pp.length == 1 ||
                        is_all_even(schlafli_pp) && VecMath.isIdentityPerm(backEdgeInds))
                    {
                        double newWythoff[] = new double[currentPickableSchwarzPolygon.SchwarzPolygon.length];
                        if (HyperbolicUtils.findBaryCoordsThatMakeSnubUniform(
                                     newWythoff,
                                     currentPickableSchwarzPolygon.SchwarzPolygon,
                                     nonEventVerbose))
                        {
                            System.out.println("    " + "newWythoff" + " = " + Arrays.toStringCompact(newWythoff));
                            currentWythoff = newWythoff;
                            currentOperation = OMNITRUNCATED;
                            maj();
                        }
                        else
                        {
                            HyperbolicUtils.message("Sorry, failed to make snub uniform for some reason");
                        }
                    }
                    else
                    {
                        HyperbolicUtils.message("Sorry, can't make snub uniform unless it's regular, or all faces are even and all generating isometries are mirror-edge reflections");
                    }  
                	}
           }  
class fpavage implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	String schlafliString=JOptionPane.showInputDialog(null,"Quel est le symbol BCGS (par exemple : (3,6,6,6)^1 [0][1][2][3] ) ?");
                	if (schlafliString != null)
                		{ana_schlafli(schlafliString);
                		if (schlafli_pp.length == 1)
            					{
                			currentOperation = PRIMAL;
                			currentOperation = (((currentOperation)+(wythoffChoices.length))%(wythoffChoices.length));
                			currentWythoff = wythoffChoices[currentOperation];
            					}
            				else
            					{
                			currentOperation = OMNITRUNCATED;
                			currentWythoff = null;
            					}
            				}
                	maj();  
                	}
           }     
class fhelp implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
                	{
                	help();   
                	}
           }  
static int indexbcgsregulier=0;
class fpavage_regulier implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
									{
									String tabbcgsregulier[]={"3 7","4 5","5 4","6 4","7 3","8 3","9 3","10 3","20 3"};
	     						
									String reponse="bcgs";
/*while (reponse!= null) */
									
									reponse = (String) JOptionPane.showInputDialog
	    							(null,
	     							"Pur un pavage regulier, choisir un code de schlafi (nb cotés du polygone, nb de polygones à chaque sommet)",
	     							"Géometrie hyperbolique- http://www.pdbzro.com/gags", 
	     							JOptionPane.QUESTION_MESSAGE,
	     							null, 
	     							tabbcgsregulier,
	     							tabbcgsregulier[indexbcgsregulier]);
	 								for (int i=0;i<tabbcgsregulier.length;i++)	
	 									{ 
	 									if (reponse==tabbcgsregulier[i])
	 										{
                			indexbcgsregulier=i+1;
                			if (i==tabbcgsregulier.length-1)
                				{indexbcgsregulier=0;}
                			/*int sppos=reponse.indexOf(" ");
                			String p=reponse.substring(0,sppos);
                			int q=Integer.parseInt(reponse.substring(sppos+1,reponse.length()));
                			reponse="(";
                			for (i=1;i<=q;i++)
                				{if (i!=1) 
                					{reponse=reponse+",";};
                				reponse=reponse+p;
                				}
                			reponse=reponse+")^1";*/
                			ana_schlafli(reponse);
	 										if (schlafli_pp.length == 1)
            						{
                				currentOperation = PRIMAL;
                				currentOperation = (((currentOperation)+(wythoffChoices.length))%(wythoffChoices.length));
                				currentWythoff = wythoffChoices[currentOperation];
            						}
            					else
            						{
                				currentOperation = OMNITRUNCATED;
                				currentWythoff = null;
            						}
	 										maj();
	 										}
	 									}
	 							}
	 				}
	 				
static int indexbcgsmixte=0;
class fpavage_mixte implements ActionListener
        	{
              /*  @Override*/
                public void actionPerformed(ActionEvent e) 
									{
									String tabbcgsmixte[]={"(3,6,6,6)^1 [0][1][2][3]","(3,6,6,6)^1 [0][1](2)(3)","(3,6,6,6)^1 (0 1)[2](3)","(3,4,5,6)^1"};
	     							
									String reponse="bcgs";
/*while (reponse!= null) */
									
									reponse = (String) JOptionPane.showInputDialog
	    							(null,
	     							"Pour un pavage mixte, choisir un symbole bcgs (nb de cotés des polygones à chaque sommet",
	     							"Géometrie hyperbolique- http://www.pdbzro.com/gags", 
	     							JOptionPane.QUESTION_MESSAGE,
	     							null, 
	     							tabbcgsmixte,
	     							tabbcgsmixte[indexbcgsmixte]);
	 								for (int i=0;i<tabbcgsmixte.length;i++)	
	 									{ 
	 									if (reponse==tabbcgsmixte[i])
	 										{
	 										ana_schlafli(reponse);
	 										if (schlafli_pp.length == 1)
            						{
                				currentOperation = PRIMAL;
                				currentOperation = (((currentOperation)+(wythoffChoices.length))%(wythoffChoices.length));
                				currentWythoff = wythoffChoices[currentOperation];
            						}
            					else
            						{
                				currentOperation = OMNITRUNCATED;
                				currentWythoff = null;
            						}
                			indexbcgsmixte=i+1;
                			if (i==tabbcgsmixte.length-1)
                				{indexbcgsmixte=0;}
	 										maj();
	 										}
	 									}
	 								}
	 					}
           
public void affiche(String str,String str2)
{
JOptionPane.showMessageDialog
	    							(null, 
	     							str, 
	     							str2, 
	     							JOptionPane.INFORMATION_MESSAGE);
} 
public void help()
{
int indexhelp=0;
String tabhelp[]={"Geometrie non euclidienne","Disque de Poincaré","Sphère projective de Riemann","Inversion",
"Transformations de Moebius","Polygone régulier","symbole de Schläfli","polygone dual","symbole de Coxeter-Dynkin",
"Burgiel/Conway/Goodman-Strauss (BCGS) orbifold symbol","Triangle de Schwarz","Coefficients de Wythoff et troncation",
"Alternation et snub","souris","nombres complexes","pourquoi Z2+C"};
	     							
String reponse="help";
while (reponse!= null) 
	{
	reponse = (String) JOptionPane.showInputDialog
	    							(null,
	     							"Choisir une rubrique de l'aide géometrie hyperbolique",
	     							"Géometrie hyperbolique- http://www.pdbzro.com/gags", 
	     							JOptionPane.QUESTION_MESSAGE,
	     							null, 
	     							tabhelp,
	     							tabhelp[indexhelp]);
	 for (int i=0;i<tabhelp.length;i++)	
	 		{ 
	 		if (reponse==tabhelp[i])
	 				{
	 				switch (i)
	 					{
	 					case 0:{	// Geometrie non euclidienne
str="<html>En géometrie non euclidienne, on n'admet pas le 5ème axiomed'Euclide, et donc par un point il peut passer plusieurs parallèles à une droite</html>";break;}
	 					case 1:{	// Disque de Poincaré
str="<html>Le disque de Poincaré représente le plan<br>par deux points il passe un seul cercle-droite orthogonal au cercle limite</html>";break;}
	 					case 2:{	// Sphère projective de Riemann
str="<html>Le plan se projette sur une sphère de Riemann en tracant une droite partant du pole nord, et joignant un point du plan équatorial<br>";
str=str+"On apelle cela une projection stéréographique<br>";
str=str+"le point (x,y,z) de la sphère correspond au point x/(1-z)+iy/(1-z) du plan<br>";
str=str+"le point x+iy du plan correspond au point 2x/(x2+y2+1), 2y/(x2+y2+1), (x2+y2-1)/(x2+y2+1) de la sphère<br>";
str=str+"une droite du plan definit avec le pole nord un plan qui coupe la sphère selon un cercle passant par le pole nord, qui est la projection de la droite sur la sphère et dont la tangente au pole nord est parallèle à la droite<br>";
str=str+"les cercles de longitude(passant par le pole nord) se projetent selon des droites passant par O<br>";
str=str+"un cercle de la sphère ne passant pas par le pole nord se projete sur le plan selon un cercle<br>";
str=str+"les cercles de latitude (parallèles à l'équateur) se projettent sur le plan selon des cercles centrés en O<br>";
str=str+"la grandeur des angles est conservée, leur orientation est inversée<br>";
break;}
	 					case 3:{	// inversion
str="<html>L'inversion d'un point P par rapport à un cercle de centre O et de rayon R est le point Q aligné avec O et P tel que OP*OQ=R*R<br><br>";
str=str+"en notation complexe, cela se traduit par f(z)=1/z' pour le cercle unité et f(z)-c= r*r/(z'-c') pour un autre<br><br>";
str=str+"l'inversion d'un point du cercle est lui même<br>";
str=str+"l'inversion d'un point intérieur au cercle est à l'extérieur du cercle, et réciproquement<br>";
str=str+"l'inversion du point O est à l'infini, et réciproquement<br>";
str=str+"l'inversion d'une droite passant par O est elle même<br";
str=str+"l'inversion d'une droite ne passant pas par O est un cercle passant par O et dont la tangente en O est parallèle à la droite<br>";
str=str+"    si la droite coupe le cercle en 2 points, ces deux points et O definissent le cercle inverse";
str=str+"l'inversion d'un cercle passant par O est une droite parallèle a la tangente au cercle en O<br>";
str=str+"l'inversion d'un cercle ne passant pas par O est un cercle ( attention : leurs centres ne sont pas inverses l'un de l'autre)<br>";
str=str+"    si le cercle coupe le cercle d'inversion en deux points, le cercle inversé aussi<br>";
str=str+"    si le cercle est intérieur au cercle d'inversion, le cercle inversé est extérieur<br>";
str=str+"    si le cercle contient le point O, le cercle inversé aussi<br><br>";
str=str+"l'inversion d'un ensemble de droites parallèles est constitué d'un faisceau de cercles tangents en O à la parallèle à ces droites<br>";
str=str+"l'inversion d'une tangente en T au cercle est le cercle de diametre OT<br>";
	 				break;}
	 					case 4:{	// Transformations de Moebius
str="<html>Une transformation de Moebius est de la forme f(z)=(a*z+b)/c*z+d) <br><br>";
str=str+"une transformation inversive est soit directe, de la forme (a*z+b)/c*z+d) soit indirecte, de la forme a*z'+b)/(c*z'+d avec z' le conjugué de z)<br>";
str=str+"si g(z)=(p*z+q)/(r*z+t)<br>";
str=str+"f(g(z))=(a(pz+q)/(rz+t) +b)  /  (c(pz+q)/(rz+t) +d) = (a(pz+q) + b(rz+t)) / (c(pz+q) + d(rz+t)) = ((ap+br)z + aq+bt) / ((cp+dr)z + cq+dt)<br>";
str=str+"les transformations de Moebius constituent donc un groupe<br>";
str=str+"et on peut représenter, à une constante près, la transformation par la matrice :<br>";
str=str+"( a  b )<br>";
str=str+"( c  d )<br><br>";

str=str+"si c=0, la transformation est de la forme f(z)=a*z+b, c'est une transformation linéaire affine : homothétie, rotation, reflexion, translation (voir l'aide du programme sur les ifs)<br>";
str=str+"sinon, on peut la mettre sous la forme f(z)=a/c  *  (z + d/c - d/c + b/a)/(z+d/c)  = a/c * (1+ (bc-ad)/a*c*(z+d/c))<br>";
str=str+"donc, si ad-bc n'est pas nul (auquel cas f(z)=a/c) la transformation est le produit d'une transformation linéaire, d'une inversion et d'une transformation linéaire<br>";
str=str+"une transformation de Moebius conserve la grandeur et l'orientation des angles et transforme les droites-cercles en droite-cercle (il suffit de choisir trois points à inverser)<br><br>";
str=str+"au coefficient 1/(ad-bc) près, l'inverse de la matrice est<br>";
str=str+"(d   -b)<br>";
str=str+"(-c   a)<br>";
str=str+"donc t^-1(z)=(dz-b)/(-cz+a)<br>";
str=str+"étant donnés deux ensembles de 3 pointsz1, z2, z3 et w1, w2, w3, il y a une transformation de Moebius unique qui transforme les 3 premiers en les 3 autres<br>";
str=str+"on passe pas trois points intermédiaires 0,1 et infini (l'axe des x)<br>";
str=str+"f(z)=k *(z-z1)/(z-z2) et on calcule k pour que f(z3)=1<br>;
str=str+"si z1=infini, f(z)=k/(z-z3)<br>";
str=str+"si z2=infini, f(z)=(z-z1)/(z-z3)<br>";
str=str+"si z3=infini, f(z)=k(z-z1)<br>";
str=str+"deux cercles(définis par 3 points) sont également transformables par une transformation de Moebius unique<br>";

break;}
	 					case 5:{	// polygone régulier
str="<html>Tout polygone régulier pave le plan, et même de plusieurs façons :<br>";
str=str+"les p angles extérieurs font un tour complet, soit 2pi/p par angle extérieur<br>";
str=str+"l'angle intérieur est le complément soit (1-2/p)*pi<br>";
str=str+"la somme des angles intérieur d'un polygone régulier de p cotés est (p-2)*pi<br>";
str=str+"</html>";
break;}
	 					case 6:{	// symbole de Schläfli et polyèdres réguliers
str="<html>Le symbole de Schläfli pour un polygone régulier convexe à p côtés est {p}. un pentagone régulier est représenté par {5}<br>";
str=str+"Pour représenter des polygones étoilés, les fractions sont utilisées. Ainsi le pentagramme, qui est le pentagone étoilé, est représenté par {5/2}<br>";
str=str+"ce qui signifie que ce polygone possède 5 arêtes et que chacune de ces arêtes relie les sommets de numéro s et s + 2. Ainsi la première arête relie le premier et le troisième sommet, la deuxième le troisième et le cinquième…<br><br>";
str=str+"dans le plan chaque sommet est entouré de q angles de (1-2/p)pi dont la somme vaut 2pi, soit : 1/p+1/q=1/2 et (p-2)(q-2)=4<br>";
str=str+"d'où les solutions pour un pavage régulier : (3,6)  (4,4), (6,3)<br><br>";
str=str+"Dans l'espace, on considère une face à p cotés et q faces par sommet, soit {p,q} et 1/p+1/q>1/2<br> or (p-2)*(q-2) > 4<br>";
str=str+"on trouve donc les 5 solides de Platon : tetraèdre (3,3), octaèdre (3,4), cube (4,3), isocaèdre (3,5) et dodécaèdre (5,3)<br>";
str=str+"n0 le nombre de sommets, n1 le nombre de cotés et n2 le nombre de faces alors la formule d'Euler donne n0-n1+n2=2<br>";
str=str+"q*n0=2*n1=p*n2<br>";
str=str+"donc 1/p+1/q-1/2=1/n1<br><br>";
str=str+"la formule de Descartes montre que la somme des angles des faces à un sommet est inférieur à 2pi=2pi-delta<br>";
str=str+"et si on considère tous les sommets, somme(delta)=4pi<br>";
str=str+"un triangles ayant des angles pi/p, pi/q et pi/r, 1/p+1/q+1/r=1<br>";
str=str+"donc les solutions sont (3,3,3) , (2,4,4) et (2,3,6)<br>";
str=str+"un poligone sphérique fait (n-2)pi donc au moins un des angles est supérieur à (n-2)pi/n donc pour n>=4, au moins un angle est obtus<br>";
break;}



	 					case 7:{	// polygone dual
str="<html>polygones duaux : les cotés de l'un sont en correspondance avec les sommets de l'autre, en respectant les propriétés d'adjacence.<br>";
str=str+"on considère les polygones dont les cotés joignent les barycentres des polygones primaires d'un sommet<br>";
str=str+"si on considère le polyhèdre {p,q} ayant n0 sommets, n1 cotés et n2 faces, on remplace chaque coté par sa perpendiculaire au milieu, et on obtient un dual {q,p} de n1 cotés, n2 sommets et n0 faces<br>";
str=str+"le cercle circonscrit de l'un est le cercle inscrit de l'autre, et ils sont inverses par rapport à ce cercle<br>";
str=str+"leurs listes de symboles de schläfli sont en ordre inverse <br>";
str=str+"If the sequence is palindromic, i.e. the same forwards and backwards, the polytope is self-dual. Every regular polytope in 2 dimensions (polygon) is self-dual<br>";
str=str+"</html>";
break;}
						case 8:{	// symbole de Coxeter-Dynkin
str="<html>Start with any permutation of integers p0,p1,...,pn-1 (n>=3, pi>=2). the list p0,p1,...,pn-1 can also be written as p1,...,pn-1,p0 or any other permutation.)<br>";
str=str+"les nombres peuvent être tous égaux, c'est le cas des polygones réguliers<br>";
str=str+"The fundamental uniform tiling corresponding to p0,p1,...,pn-1 has vertex configuration (2p0,2p1,...,2pn-1) <br>";
str=str+"that is, each vertex is surrounded by n regular polygons whose numbers of sides are 2p0,2p1,...,2pn-1 in CCW or CW order),<br>";
str=str+"with symmetry group generated by reflections about the perpendicular bisectors of the edges of the tiling.<br><br>"; 
str=str+"The dual of the fundamental tiling is composed of \"Schwarz polygons\" denoted (p0 p1 ... pn-1), with interior angles pi/p0, pi/p1, ..., pi/pn-1, and reflected about their edges. <br><br>";

str=str+"We can color each vertex of the uniform tiling (or Schwarz polygon of the dual) \"even\" or \"odd\" depending on whether it is generated as an even or odd number of reflections of a fixed initial vertex (or Schwarz polygon of the dual, respectively),<br>";
str=str+"i.e. whether it is an even or odd distance (number of edges) from the initial vertex. <br<br>";

str=str+"The resulting tiling is spherical, planar, or hyperbolic, depending on whether the sum of (pi - pi/pi) is <, =, or > 2pi respectively.<br><br>";
str=str+"- Fundamental tiling is denoted by the Coxeter-Dynkin symbol (p0|p1|...|pn-1|). The vertical bar between pi and pi+1 represents the edge type between a 2pi-gon and a 2pi+1-gon in the tiling. <br>";
str=str+"- The fundamental tiling with one of its edge types (i.e. one edge and all its reflections) shrunk to a point, we remove the corresponding bar in the symbol. <br>";
str=str+"This has the effect of merging each \"even\" vertex with an adjacent \"odd\" vertex. <br>";
str=str+"The resulting merged vertex configuration is (p0,2p1,2p2,...,2pn-2,pn-1,2pn-2,...,2p2,2p1). Note that it is symmetric about p0 and pn-1. <br>";
str=str+"- The fundamental tiling with one of its face types pi (i.e. two consecutive edge types) shrunk to a point<br";
str=str+"This has the effect of merging together groups of 2p0 vertices.<br>";
str=str+"The resulting merged vertex configuration is (p1,2p2,2p3,...,2pn-2,pn-1,2pn-2,...,2p3,2p2)p0. Note that it is symmetric about each p1 and pn-1. <br>";
str=str+"- Starting with the fundamental tiling, add edges connecting each pair of \"even\" vertices that share an \"odd\" neighbor vertex, then remove all \"odd\" vertices along with all the original edges. <br>";
str=str+"The edge lengths and angles will then need to be adjusted so that the resulting tiles are regular polygons again.<br>";
str=str+"The resulting vertex configuration is (p0,n,p1,n,...,pn-1,n). We denote this by the symbol (p0||p1||...||pn-1||) (which is the same as (p1||...||pn-1||p0||), etc.)</html> ";break;}
/*
For (p-2)*(q-2) > 4:
	  (2|p|q|)     (4,2p,2q)			omnitruncated {p,q} or {q,p}
	  (2|p|q )     (2,2p,q,2p) = (2p,q,2p)		truncated {p,q}
	  (2|p q|)     (q,4,p,4)			runcinated {p,q} or {q,p}
	  (2 p|q|)     (p,2q,2,2q) = (p,2q,2q)		truncated {q,p}
	  (2|p q )     (2,p)^q = p^q			{p,q}
	  (2 p|q )     (p,q)^2				bitruncated {p,q} or {q,p}
	  (2 p q|)     (q,2)^p = q^p			{q,p}
	  (2||p||q||)  (2,3,p,3,q,3) = (3,p,3,q,3)	snub {p,q} or {q,p}

	  (3|4|5|)     (6,8,10)
	  (3|4|5 )     (3,8,5,8)
	  (3|4 5|)     (5,6,4,6)
	  (3 4|5|)     (4,10,3,10)
	  (3|4 5 )     (3,4)^5
	  (3 4|5 )     (4,5)^3
	  (3 4 5|)     (5,3)^4
	  (3||4||5||)  (3,3,4,3,5,3)

	  (3|2|2|2|)   (6,4,4,4)    NOT the same as runcinated {6,4}, see below
	  (3|2|2|2 )   (3,4,4,2,4,4) = (3,4,4,4,4)
	  (3|2|2 2|)   (2,6,4,2,4,6) = (6,4,4,6)
	  (3|2|2 2 )   (3,4,2,4)^2 = (3,4,4)^2
	  (3 2|2|2 )   (2,4,2,4)^3 = 4^6
	  (3|2 2 2|)   (2,6,2,6)^2 = 6^4
	  (3||2||2||2||) (3,4,2,4,2,4,2,4) = (3,4,4,4,4)

*/

	 					case 9:{ //bcgs
str="<html>- The edges surrounding the central vertex are numbered 0,1,2,... counterclockwise with edge 0 pointing to the left (unlike John Conway's drawings, which go clockwise with edge 0 pointing up).<br><br>"; 
str=str+"- The first part of the symbol ( (3,6,6,6)^1 in the above examples) is the face list surrounding the vertex. The face between edges 0&1 is listed first, then 1&2, etc.<br>";
str=str+"The list is repeated the number of times specified in the exponent; for example (6,7,8)^2 would produce the following actual faces around a vertex: 6,7,8,6,7,8. <br><br>";
str=str+"- The second part of the symbol is a permutation of the edges (not including replication by the exponent), the purpose of which is to describe where each edge appears in the corresponding neighbor vertex's edge list.<br>";
str=str+"The permutation is specified by its cycles, each of which must be of length 1 or 2. For example, the permutation (0 3)(1)(2) would mean that 0 goes to 3, 3 goes to 0, and each of 1 and 2 remains fixed.<br>";
str=str+"Each cycle is surrounded by either [] or () according to whether the neighbor vertex along that edge is a mirror image or a rotated image of the central vertex. <br><br>";
str=str+"- Note that the \"*\" that appears in the BCGS orbifold symbol, denoting a mirror through the vertex, is not recognized; you must specify the full permutation on all the edges (not replicated by the exponent) instead. <br>";
str=str+"- You can use /</> in the permutation part if you like (in the orbifold symbol, this represents an edge that is split along its length by a mirror symmetry, i.e. \"both [] and ()\"). It is implemented as [].<br>";
str=str+"(This only matters if it turns out that the tiling didn't really have all the symmetries implied by the /</> after all.) <br>";
str=str+"You may also put /</> around one or two single entries in the face list if you like (to denote that a mirror through the vertex passes through the face). <br>";
str=str+"These are currently ignored by the program, but you might want to use them if you want to express all the symmetry information from the BCGS orbifold symbol. <br><br>";
str=str+"- Notes: An exponent of ^1 may be omitted. Cycles of length 1 surrounded by [] may be omitted from the permutation.<br>";
str=str+"(In particular, if all edge-reversing reflections are isometries of the tiling, then the permutation part may be omitted entirely.) The right brackets or parens of the cycles may be omitted; <br>";
str=str+"this can be convenient when experimenting (it's easier to change one character than two). For example, \"(3,6,6,6)^1 (0 1)[2](3)\" may be abbreviated as either \"(3,6,6,6) (0 1[2(3\" or \"(3,6,6,6) (0 1(3\".<br>";
str=str+"Underscores may be used instead of spaces throughout (this can be useful if you are embedding this notation in a URL which is not allowed to contain space characters). <br><br></html>";

break;}
	 					case 10:{ // triangle de schwarz
str="page 112 triangle de schwarz (p q r) par reflection sur leurs cotés couvrent la sphère (2,2,n)  (3,2,3)  (3,2,4) ou (3,2,5)<br>";
str=str+"et le plan (3,3,3) (4,2,4) (3,2,6) (6,6,3/2) = (6,2,3) + (2,6,3)<html><br></html>";
break;}
	 					case 11:{	// wythoff et truncation
str="<html>on peut complètement tronquer les coins des polygones, en joignant les milieux des cotés<br>";
str=str+"les deux polygones sont inverses par rapport au cercle inscrit du premier, qui est le cercle circonscrit du second<br>";
str=str+"on peut aussi tronquer de manière intermédiaire, dans un rapport a/b avec a>=b<br>";
str=str+"tronquer fait apparaitre à chacun des p sommets un polygone de q cotés, qui grossit plus on tronque, jusqu'à remplacer le polygone primaire par son dual<br>";
str=str+"si a/b=0, c'est le polygone primaire, non tronqué<br>";

str=str+"si a/b=4, on obtient le polygone dual<br>";
str=str+"si a/b=5, runcinated<br>";
str=str+"si a/b=6, omnitruncated<br>";
						/*		Truncate=0 (primal)	{1,0,0},
                Truncate=.5					{.75,.25,0},
                Truncate=1					{.5,.5,0}, uniform truncation
                Truncate=1.5				{.25,.75,0},
                Truncate=2		milieu	{0,1,0},   rectification
                Truncate=2.5				{0,.75,.25},
                Truncate=3					{0,.5,.5},
                Truncate=3.5				{0,.25,.75},
                Truncate=4 (dual)		{0,0,1},
                halfway between dual and runcinated	{.25,0,.75},
                Runcinated					{.5,0,.5},
                halfway between runcinated and primal	{.75,0,.25},
                Omnitruncated				{1,1,1},*/
          
str=str+"</html>";
break;}
	 					case 12:{	// alternation, snub, runcination
str="<html>an alternation (also called partial truncation, snub or snubification) is an operation on a polyhedron or tiling that removes alternate vertices.<br>";
str=str+"Only even-sided polyhedra can be alternated. Every 2n-sided face becomes n-sided. Square faces disappear into new edges.";
str=str+"A snub is an alternation applied to an omnitruncated regular polyhedron. An omnitruncated regular polyhedron or tiling always has even-sided faces and so can always be alternated.<br>";
str=str+"runcination is an operation that cuts a regular polygone simultaneously along the edges, creating new facets in place of the original face, edge, and vertex centers.<br>";
str=str+"the original {p} cells remain, but become separated. The gaps at the separated edges become rectangles.<br>";
str=str+"an omnitruncated polygone is a truncated quasiregular polygone<br>";

/*The fundamental tiling itself 
The fundamental tiling with one of its edge types (i.e. one edge and all its reflections) shrunk to a point 
The fundamental tiling with one of its face types pi (i.e. two consecutive edge types) shrunk to a point 
The "snub" of the fundamental tiling (this will be described below). */

str=str+"</html>";
break;}
	 					case 13:{str="<html>plusieurs types de coloration sont possibles :<br>par le nombre d'itération avant la divergence vers l'infini<br>par le module de divergence<br>par l'angle de divergence<br>par le module minimum de fn(0)<br>Par le nombre d'itération pour atteindre le minimum de fn(0), nombre qui, résultat surprenant, suit la suite de Fibonacci (Fn=Fn-1  + Fn-2)<br><br>En visualisant les points et bassins d'attraction, la colorisation se fait par le nombre d'itération avant la convergence vers l'infini un point fixe ou un cycle<br>La détection du cycle est plus longue car il faut detecter l'égalité à epsilon près avec un des n points précédents</html>";break;}
	 					case 14:{str="souris gauche dans Mandelbrot : choix du point C, recentrage et zoom\nsouris gauche dans Julia : recentrage et zoom\nsouris droite dans Mandelbrot : choix du point C et affichage du Julia correspondant\nsouris droite dans Julia : choix d'un point Z et suivi de son cycle\nsouris du milieu : recentrage et zoom dynamique";break;}
	 					case 15:{str="le nombre complexe x + i y se représente par le point de coordonnées cartésiennes (x,y)\ntoutes les opérations et fonctions classiques sont définies piur des complexex\nZ= x + i y = r exp(i t) = r * (cos(t) + i sin(t)) avec r=sqrt(x*x + y*y) et t = arctang(y/x)\nx + i y     +    m + i n = x+m   + i (y+n)\n(x + i y) * (m + i n) = x * m - y * n  +i (x * n + y m)\nsin(x + i y) = sin(x)*ch(y) + i cos(x)*sh(y)\ncos(x + i y) = cos(x)*ch(y) - i sin(x)*sh(y)\nexp(x + i y) =exp(x) * (cos(y) + i sin(y))\nz**z'=exp(z'(log(r)i t))\n";break;}
	 					case 16:{str="on étudie az2+bz+c\nor, par un changement d'axe on peut se limiter à étudier\na(z2 + b/a z + c/a)\n puis (z+b/2a)2- (b2-4ac)\nsoit Z2+C";break;} 
	 					default:{}
	 					} 
	 				affiche(str,reponse);
	 				indexhelp++; 
	 				if (indexhelp==tabhelp.length) {indexhelp=0;}
	 				} // switch
	 		} // for
	 } // while
} // help
/* bouton- */

 }
 
 /*
 
 */