function morph() {
        this.str = null;
        this.n = 0;
        this.alpha = new Array(); // alphabet dans l'ordre d'apparition
        this.inv = new Array(); // positions des lettres
        this.max = 0;
        this.lm = 4;

        function init(s, lm) {
                this.str = s;
                this.lm = parseInt(lm);
                var prealpha = new Array();
                var k=0;
                for(var i=0; i< this.str.length;i++) {
                        var c = s.charAt(i);
                        if(prealpha[c]==null) {
                                this.alpha[k] = c;
                                k++;
                        }
                        prealpha[c]=1;
                }

                this.n = this.alpha.length;
                for(var i=0; i < this.n; i++) {
                        this.inv[this.alpha[i]] = i;
                }
        }
        this.init=init;

        function scan() {
                var sols="";
                var v;
                var lg = new Array();
                for(var i=0; i < this.n; i++)
                        lg[i] = 0; // longueur du morphisme du caract de rang i=0 1 2 ...
                var t = new Array(); // t[i] = chaîne de longueur lg[i]
                while(this.nextl(lg) != -1) {
                        this.decoupe(t, lg);
                        v = this.appl(t, lg);
                        if(v > this.max) this.max=v;
                        if(v >= this.str.length) {
                                sols += "#  "+v+"\n"
                                for(i=0;i<this.n;i++) {
                                        sols += this.alpha[i]+" -> "+t[i]+"\n";
                                }
                                sols += "-----------------\n";
                        }
                }
                if(sols=="") sols= "# max : "+this.max+"/"+this.str.length;
                return sols;
        }
        this.scan=scan;

        function appl(t, tb) {
                var c, s1="";
                for(var i=0; i < this.str.length && s1.length<this.str.length; i++) {
                        c=this.str.charAt(i);
                        s1 += t[this.inv[c]];
                }
                for(var i=0; i<this.str.length;i++) {
                        if(this.str.charAt(i)!=s1.charAt(i)) {
                                return (i+1);
                        }
                }
                return this.str.length;
        }
        this.appl=appl;

        /*  tb table de n places contenant des nombres de 0 à k-1
                comme  nchiffres 2132 en base k
                pour n=4 places et k=4 (arbitraire)
                2132 -> 2133 -> 2134=2140=2200  -> 2201 ...
         */

        function nextl(tb) {
                var i;
                for(i=0; i< tb.length; i++) {
                        tb[i]++;
                        if(tb[i]< this.lm) { // this.lm long. maxi d'une ch. de morph.
                                return i;
                        } else {
                                tb[i]=0;
                        }
                }
                //épuise les possibilités
                return -1;
        }
        this.nextl=nextl;

        /*  ex. s = 1001020103...
                si on utilise le morphisme 1->aaa 0->bbb 2->ccc on remplace 1, 0, 0, 1,
                aaa bbb bbb aaa bbb ccc ...
                |   |               |
                0   x=a             y=a+...
                à chaque chiffre on ajoute la longueur de chaîne correspondante

                sachant que les chiffres sont 1 0 2 3 dans l'ordre d'apparition
                qu'on a des morphismes de longueurs a, b, c, d
                1 -> 100...de longueur a    en 0
                0 ->                   b    en a
                2                      c en a+b+b+a+b
                3                      d    a+b+b+a+b+b+a+b
                de toute manière on augmente à chaque lettre

                aucune autre vérification à ce niveau (pour les lettres doublées)

                t[i] le morphisme
        */
        function decoupe(t,tb) {
                var a=0, ch, ci;
                var db = new Array();
                var k=0;
                for(var i=0; i < this.str.length && k < this.n; i++) {
                   ch= this.str.charAt(i);    // la lettre en i
                   ci = this.inv[ch];         // son numéro
                   if(db[ci]==null) {  // c'est la bonne
                        db[ci]=a;
                        k++;
                   }
                   a += tb[ci];
                }
                for(var i=0; i < tb.length; i++) {
                        t[i]= this.str.substr(db[i],tb[i]);
                }
        }
        this.decoupe=decoupe;
}

function morphisme() {
        var s0, s = document.frm.mot.value;
        do {
                s0=s;
                s = s.replace(/\n+/g,"");
                s = s.replace(/\s+/g,"");
        } while(s0!=s);
        var lm = 1+parseInt(document.frm.lm.value);
        var m = new morph();
        m.init(s, lm);
        var sols = m.scan();
        document.frm.fact.value = sols;
}


