001/*
002 * $Id: WordFactory.java 5833 2018-05-16 21:49:10Z kredel $
003 */
004
005package edu.jas.poly;
006
007
008import java.io.Reader;
009import java.io.Serializable;
010import java.math.BigInteger;
011import java.util.ArrayList;
012import java.util.Arrays;
013import java.util.Comparator;
014import java.util.List;
015import java.util.Random;
016
017import org.apache.log4j.Logger;
018
019import edu.jas.kern.StringUtil;
020import edu.jas.structure.MonoidFactory;
021
022
023/**
024 * WordFactory implements alphabet related methods.
025 * @author Heinz Kredel
026 */
027
028public final class WordFactory implements MonoidFactory<Word> {
029
030
031    /**
032     * The data structure is a String of characters which defines the alphabet.
033     */
034    /*package*/final String alphabet;
035
036
037    /**
038     * The empty word for this monoid.
039     */
040    public final Word ONE;
041
042
043    /**
044     * The translation reference string.
045     */
046    public static final String transRef = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
047
048
049    /**
050     * The translation array of Strings.
051     */
052    public final String[] translation;
053
054
055    /**
056     * Random number generator.
057     */
058    private final static Random random = new Random();
059
060
061    /**
062     * Log4j logger object.
063     */
064    private static final Logger logger = Logger.getLogger(WordFactory.class);
065
066
067    /**
068     * Comparator for Words.
069     */
070    public static abstract class WordComparator implements Comparator<Word>, Serializable {
071
072
073        public abstract int compare(Word e1, Word e2);
074    }
075
076
077    /**
078     * Defined descending order comparator. Sorts the highest terms first.
079     */
080    private static final WordComparator horder = new WordComparator() {
081
082
083        @Override
084        public int compare(Word e1, Word e2) {
085            //return e1.gradCompareTo(e2);
086            return e1.gradInvlexCompareTo(e2);
087        }
088    };
089
090
091    /**
092     * Defined ascending order comparator. Sorts the lowest terms first.
093     */
094    private static final WordComparator lorder = new WordComparator() {
095
096
097        @Override
098        public int compare(Word e1, Word e2) {
099            //return -e1.gradCompareTo(e2);
100            return -e1.gradInvlexCompareTo(e2);
101        }
102    };
103
104
105    /**
106     * Constructor for WordFactory.
107     */
108    public WordFactory() {
109        this("");
110    }
111
112
113    /**
114     * Constructor for WordFactory.
115     * @param s String of single letters for alphabet
116     */
117    public WordFactory(String s) {
118        if (s == null) {
119            throw new IllegalArgumentException("null string not allowed");
120        }
121        alphabet = cleanSpace(s);
122        translation = null;
123        ONE = new Word(this, "", false);
124    }
125
126
127    /**
128     * Constructor for WordFactory.
129     * @param S String array for alphabet
130     */
131    public WordFactory(String[] S) {
132        String[] V = cleanAll(S);
133        if (isSingleLetters(V)) {
134            alphabet = concat(V);
135            translation = null;
136        } else {
137            alphabet = transRef.substring(0, V.length);
138            translation = V;
139            logger.info("alphabet = " + alphabet + ", translation = " + Arrays.toString(translation));
140        }
141        ONE = new Word(this, "", false);
142    }
143
144
145    /**
146     * Is this structure finite or infinite.
147     * @return true if this structure is finite, else false.
148     * @see edu.jas.structure.ElemFactory#isFinite()
149     */
150    public boolean isFinite() {
151        if (alphabet.length() == 0) {
152            return true;
153        }
154        return false;
155    }
156
157
158    /**
159     * Query if this monoid is commutative.
160     * @return true if this monoid is commutative, else false.
161     */
162    public boolean isCommutative() {
163        if (alphabet.length() == 0) {
164            return true;
165        }
166        return false;
167    }
168
169
170    /**
171     * Query if this monoid is associative.
172     * @return true if this monoid is associative, else false.
173     */
174    public boolean isAssociative() {
175        return true;
176    }
177
178
179    /**
180     * Get the one element, the empty word.
181     * @return 1 as Word.
182     */
183    public Word getONE() {
184        return ONE;
185    }
186
187
188    /**
189     * Copy word.
190     * @param w word to copy.
191     * @return copy of w.
192     */
193    @Override
194    public Word copy(Word w) {
195        return new Word(this, w.getVal(), false);
196    }
197
198
199    /**
200     * Get the alphabet length.
201     * @return alphabet.length.
202     */
203    public int length() {
204        return alphabet.length();
205    }
206
207
208    /**
209     * Get the alphabet String.
210     * @return alphabet.
211     */
212    /*package*/String getVal() {
213        return alphabet;
214    }
215
216
217    /**
218     * Get the translation array of Strings.
219     * @return alphabet.
220     */
221    /*package*/String[] getTrans() {
222        return translation;
223    }
224
225
226    /**
227     * Get the alphabet letter at position i.
228     * @param i position.
229     * @return val[i].
230     */
231    public char getVal(int i) {
232        return alphabet.charAt(i);
233    }
234
235
236    /**
237     * Get the variable names.
238     * @return array of variable names
239     */
240    public String[] getVars() {
241        String[] vars = new String[alphabet.length()];
242        if (translation == null) {
243            for (int i = 0; i < alphabet.length(); i++) {
244                vars[i] = String.valueOf(getVal(i));
245            }
246        } else {
247            for (int i = 0; i < alphabet.length(); i++) {
248                vars[i] = translation[i];
249            }
250        }
251        return vars;
252    }
253
254
255    /**
256     * Get the string representation.
257     * @see java.lang.Object#toString()
258     */
259    @Override
260    public String toString() {
261        StringBuffer s = new StringBuffer("\"");
262        if (translation == null) {
263            for (int i = 0; i < alphabet.length(); i++) {
264                if (i != 0) {
265                    s.append(",");
266                }
267                s.append(getVal(i));
268            }
269        } else {
270            for (int i = 0; i < alphabet.length(); i++) {
271                if (i != 0) {
272                    s.append(",");
273                }
274                s.append(translation[i]);
275            }
276        }
277        s.append("\"");
278        return s.toString();
279    }
280
281
282    /**
283     * Get a scripting compatible string representation.
284     * @return script compatible representation for this Element.
285     * @see edu.jas.structure.Element#toScript()
286     */
287    @Override
288    public String toScript() {
289        return toString();
290    }
291
292
293    /**
294     * Comparison with any other object.
295     * @see java.lang.Object#equals(java.lang.Object)
296     */
297    @Override
298    public boolean equals(Object B) {
299        if (!(B instanceof WordFactory)) {
300            return false;
301        }
302        WordFactory b = (WordFactory) B;
303        return alphabet.equals(b.alphabet);
304    }
305
306
307    /**
308     * hashCode.
309     * @see java.lang.Object#hashCode()
310     */
311    @Override
312    public int hashCode() {
313        return alphabet.hashCode();
314    }
315
316
317    /**
318     * Get a list of the generating elements.
319     * @return list of generators for the algebraic structure.
320     */
321    public List<Word> generators() {
322        int len = alphabet.length();
323        List<Word> gens = new ArrayList<Word>(len);
324        // ONE is not a word generator
325        for (int i = 0; i < len; i++) {
326            Word w = new Word(this, String.valueOf(alphabet.charAt(i)), false);
327            gens.add(w);
328        }
329        return gens;
330    }
331
332
333    /**
334     * Get the Element for a.
335     * @param a long
336     * @return element corresponding to a.
337     */
338    public Word fromInteger(long a) {
339        throw new UnsupportedOperationException("not implemented for WordFactory");
340    }
341
342
343    /**
344     * Get the Element for a.
345     * @param a java.math.BigInteger.
346     * @return element corresponding to a.
347     */
348    public Word fromInteger(BigInteger a) {
349        throw new UnsupportedOperationException("not implemented for WordFactory");
350    }
351
352
353    /**
354     * Get the Element for an ExpVector.
355     * @param e ExpVector.
356     * @return element corresponding to e.
357     */
358    public Word valueOf(ExpVector e) {
359        Word w = ONE;
360        List<Word> gens = generators();
361        int n = alphabet.length();
362        int m = e.length();
363        if (m > n) {
364            throw new IllegalArgumentException("alphabet to short for exponent " + e + ", alpahbet = "
365                            + alphabet);
366        }
367        for (int i = 0; i < m; i++) {
368            int x = (int) e.getVal(m - i - 1);
369            Word y = gens.get(i);
370            Word u = ONE;
371            for (int j = 0; j < x; j++) {
372                u = u.multiply(y);
373            }
374            w = w.multiply(u);
375        }
376        return w;
377    }
378
379
380    /**
381     * IndexOf for letter in alphabet.
382     * @param s letter character.
383     * @return index of s in the alphabet, or -1 if s is not contained in the alphabet.
384     */
385    public int indexOf(char s) {
386        return alphabet.indexOf(s);
387    }
388
389
390    /**
391     * Generate a random Element with size less equal to n.
392     * @param n
393     * @return a random element.
394     */
395    public Word random(int n) {
396        return random(n, random);
397    }
398
399
400    /**
401     * Generate a random Element with size less equal to n.
402     * @param n
403     * @param random is a source for random bits.
404     * @return a random element.
405     */
406    public Word random(int n, Random random) {
407        StringBuffer sb = new StringBuffer();
408        int len = alphabet.length();
409        for (int i = 0; i < n; i++) {
410            int r = Math.abs(random.nextInt() % len);
411            sb.append(alphabet.charAt(r));
412        }
413        return new Word(this, sb.toString(), false);
414    }
415
416
417    /**
418     * Parse from String.
419     * @param s String.
420     * @return a Element corresponding to s.
421     */
422    public Word parse(String s) {
423        String st = clean(s);
424        String regex;
425        if (translation == null) {
426            regex = "[" + alphabet + " ]*";
427        } else {
428            regex = "[" + concat(translation) + " ]*";
429        }
430        if (!st.matches(regex)) {
431            throw new IllegalArgumentException("word '" + st + "' contains letters not from: " + alphabet
432                            + " or from " + concat(translation));
433        }
434        // now only alphabet or translation chars are contained in st
435        return new Word(this, st, true);
436    }
437
438
439    /**
440     * Parse from Reader. White space is delimiter for word.
441     * @param r Reader.
442     * @return the next Element found on r.
443     */
444    public Word parse(Reader r) {
445        return parse(StringUtil.nextString(r));
446    }
447
448
449    /**
450     * Get the descending order comparator. Sorts the highest terms first.
451     * @return horder.
452     */
453    public WordComparator getDescendComparator() {
454        return horder;
455    }
456
457
458    /**
459     * Get the ascending order comparator. Sorts the lowest terms first.
460     * @return lorder.
461     */
462    public WordComparator getAscendComparator() {
463        return lorder;
464    }
465
466
467    /**
468     * Prepare parse from String.
469     * @param s String.
470     * @return a Element corresponding to s.
471     */
472    public static String cleanSpace(String s) {
473        String st = s.trim();
474        st = st.replaceAll("\\*", "");
475        st = st.replaceAll("\\s", "");
476        st = st.replaceAll("\\(", "");
477        st = st.replaceAll("\\)", "");
478        st = st.replaceAll("\\\"", "");
479        return st;
480    }
481
482
483    /**
484     * Prepare parse from String.
485     * @param s String.
486     * @return a Element corresponding to s.
487     */
488    public static String clean(String s) {
489        String st = s.trim();
490        st = st.replaceAll("\\*", " ");
491        //st = st.replaceAll("\\s", "");
492        st = st.replaceAll("\\(", "");
493        st = st.replaceAll("\\)", "");
494        st = st.replaceAll("\\\"", "");
495        return st;
496    }
497
498
499    /**
500     * Prepare parse from String array.
501     * @param v String array.
502     * @return an array of cleaned strings.
503     */
504    public static String[] cleanAll(String[] v) {
505        String[] t = new String[v.length];
506        for (int i = 0; i < v.length; i++) {
507            t[i] = cleanSpace(v[i]);
508            if (t[i].length() == 0) {
509                logger.error("empty v[i]: '" + v[i] + "'");
510            }
511            //System.out.println("clean all: " + v[i] + " --> " + t[i]);
512        }
513        return t;
514    }
515
516
517    /**
518     * Concat variable names.
519     * @param v an array of strings.
520     * @return the concatination of the strings in v.
521     */
522    public static String concat(String[] v) {
523        StringBuffer s = new StringBuffer();
524        if (v == null) {
525            return s.toString();
526        }
527        for (int i = 0; i < v.length; i++) {
528            //String a = v[i];
529            //if ( a.length() != 1 ) {
530            //    //logger.error("v[i] not single letter "+ a);
531            //    a  = a.substring(0,1);
532            //}
533            s.append(v[i]);
534        }
535        return s.toString();
536    }
537
538
539    /**
540     * Trim all variable names.
541     * @param v an array of strings.
542     * @return an array of strings with all elements trimmed.
543     */
544    public static String[] trimAll(String[] v) {
545        String[] t = new String[v.length];
546        for (int i = 0; i < v.length; i++) {
547            t[i] = v[i].trim();
548            if (t[i].length() == 0) {
549                logger.error("empty v[i]: '" + v[i] + "'");
550            }
551        }
552        return t;
553    }
554
555
556    /**
557     * IndexOf for String array.
558     * @param v an array of strings.
559     * @param s string.
560     * @return index of s in v, or -1 if s is not contained in v.
561     */
562    public static int indexOf(String[] v, String s) {
563        for (int i = 0; i < v.length; i++) {
564            if (s.equals(v[i])) {
565                return i;
566            }
567        }
568        return -1;
569    }
570
571
572    /**
573     * Test if all variables are single letters.
574     * @param v an array of strings.
575     * @return true, if all variables have length 1, else false.
576     */
577    public static boolean isSingleLetters(String[] v) {
578        for (int i = 0; i < v.length; i++) {
579            if (v[i].length() != 1) {
580                return false;
581            }
582        }
583        return true;
584    }
585
586
587    /**
588     * Translate variable names.
589     * @param v an array of strings.
590     * @return the translated string of v with respect to t.
591     */
592    public String translate(String[] v) {
593        StringBuffer s = new StringBuffer();
594        for (int i = 0; i < v.length; i++) {
595            String a = v[i];
596            int k = indexOf(translation, a);
597            if (k < 0) {
598                System.out.println("t = " + Arrays.toString(translation));
599                System.out.println("v = " + Arrays.toString(v));
600                logger.error("v[i] not found in t: " + a);
601                //continue;
602                throw new IllegalArgumentException("v[i] not found in t: " + a);
603            }
604            s.append(transRef.charAt(k));
605        }
606        return s.toString();
607    }
608
609
610    /**
611     * Translate variable name.
612     * @param c internal char.
613     * @return the extenal translated string.
614     */
615    public String transVar(char c) {
616        int k = alphabet.indexOf(c);
617        return translation[k];
618    }
619
620}