001/*
002 * $Id: BigQuaternion.java 5945 2018-10-27 10:19:22Z kredel $
003 */
004
005package edu.jas.arith;
006
007
008import java.util.List;
009import java.util.Random;
010
011import org.apache.logging.log4j.Logger;
012import org.apache.logging.log4j.LogManager; 
013
014import edu.jas.structure.GcdRingElem;
015import edu.jas.structure.StarRingElem;
016
017
018/**
019 * BigQuaternion class based on BigRational implementing the RingElem interface
020 * and with the familiar MAS static method names. Objects of this class are
021 * immutable. The integer quaternion methods are implemented after
022 * https://de.wikipedia.org/wiki/Hurwitzquaternion see also
023 * https://en.wikipedia.org/wiki/Hurwitz_quaternion
024 * @author Heinz Kredel
025 */
026
027public /*final*/ class BigQuaternion implements StarRingElem<BigQuaternion>, GcdRingElem<BigQuaternion> {
028
029
030    /**
031     * Real part of the data structure.
032     */
033    public final BigRational re; // real part
034
035
036    /**
037     * Imaginary part i of the data structure.
038     */
039    public final BigRational im; // i imaginary part
040
041
042    /**
043     * Imaginary part j of the data structure.
044     */
045    public final BigRational jm; // j imaginary part
046
047
048    /**
049     * Imaginary part k of the data structure.
050     */
051    public final BigRational km; // k imaginary part
052
053
054    /**
055     * Corresponding BigQuaternion ring.
056     */
057    public final BigQuaternionRing ring;
058
059
060    protected final static Random random = new Random();
061
062
063    private static final Logger logger = LogManager.getLogger(BigQuaternion.class);
064
065
066    private static final boolean debug = logger.isDebugEnabled();
067
068
069    /**
070     * Constructor for a BigQuaternion from BigRationals.
071     * @param fac BigQuaternionRing.
072     * @param r BigRational.
073     * @param i BigRational.
074     * @param j BigRational.
075     * @param k BigRational.
076     */
077    public BigQuaternion(BigQuaternionRing fac, BigRational r, BigRational i, BigRational j, BigRational k) {
078        ring = fac;
079        re = r;
080        im = i;
081        jm = j;
082        km = k;
083    }
084
085
086    /**
087     * Constructor for a BigQuaternion from BigRationals.
088     * @param fac BigQuaternionRing.
089     * @param r BigRational.
090     * @param i BigRational.
091     * @param j BigRational.
092     */
093    public BigQuaternion(BigQuaternionRing fac, BigRational r, BigRational i, BigRational j) {
094        this(fac, r, i, j, BigRational.ZERO);
095    }
096
097
098    /**
099     * Constructor for a BigQuaternion from BigRationals.
100     * @param fac BigQuaternionRing.
101     * @param r BigRational.
102     * @param i BigRational.
103     */
104    public BigQuaternion(BigQuaternionRing fac, BigRational r, BigRational i) {
105        this(fac, r, i, BigRational.ZERO);
106    }
107
108
109    /**
110     * Constructor for a BigQuaternion from BigRationals.
111     * @param fac BigQuaternionRing.
112     * @param r BigRational.
113     */
114    public BigQuaternion(BigQuaternionRing fac, BigRational r) {
115        this(fac, r, BigRational.ZERO);
116    }
117
118
119    /**
120     * Constructor for a BigQuaternion from BigComplex.
121     * @param fac BigQuaternionRing.
122     * @param r BigComplex.
123     */
124    public BigQuaternion(BigQuaternionRing fac, BigComplex r) {
125        this(fac, r.re, r.im);
126    }
127
128
129    /**
130     * Constructor for a BigQuaternion from long.
131     * @param fac BigQuaternionRing.
132     * @param r long.
133     */
134    public BigQuaternion(BigQuaternionRing fac, long r) {
135        this(fac, new BigRational(r), BigRational.ZERO);
136    }
137
138
139    /**
140     * Constructor for a BigQuaternion with no arguments.
141     * @param fac BigQuaternionRing.
142     */
143    public BigQuaternion(BigQuaternionRing fac) {
144        this(fac, BigRational.ZERO);
145    }
146
147
148    /**
149     * The BigQuaternion string constructor accepts the following formats: empty
150     * string, "rational", or "rat i rat j rat k rat" with no blanks around i, j
151     * or k if used as polynoial coefficient.
152     * @param fac BigQuaternionRing.
153     * @param s String.
154     * @throws NumberFormatException
155     */
156    public BigQuaternion(BigQuaternionRing fac, String s) throws NumberFormatException {
157        ring = fac;
158        if (s == null || s.length() == 0) {
159            re = BigRational.ZERO;
160            im = BigRational.ZERO;
161            jm = BigRational.ZERO;
162            km = BigRational.ZERO;
163            return;
164        }
165        //System.out.println("init: s = " + s);
166        s = s.trim();
167        int r = s.indexOf("i") + s.indexOf("j") + s.indexOf("k");
168        if (r == -3) {
169            re = new BigRational(s);
170            im = BigRational.ZERO;
171            jm = BigRational.ZERO;
172            km = BigRational.ZERO;
173            return;
174        }
175
176        s = s.replaceAll("~", "-"); // when used with GenPolynomialTokenizer
177        int i = s.indexOf("i");
178        String sr = "";
179        if (i > 0) {
180            sr = s.substring(0, i);
181        } else if (i < 0) {
182            throw new NumberFormatException("BigQuaternion missing i: " + s);
183        }
184        String si = "";
185        if (i < s.length()) {
186            s = s.substring(i + 1, s.length());
187        }
188        int j = s.indexOf("j");
189        if (j > 0) {
190            si = s.substring(0, j);
191        } else if (j < 0) {
192            throw new NumberFormatException("BigQuaternion missing j: " + s);
193        }
194        String sj = "";
195        if (j < s.length()) {
196            s = s.substring(j + 1, s.length());
197        }
198        int k = s.indexOf("k");
199        if (k > 0) {
200            sj = s.substring(0, k);
201        } else if (k < 0) {
202            throw new NumberFormatException("BigQuaternion missing k: " + s);
203        }
204        String sk = "";
205        if (k < s.length()) {
206            s = s.substring(k + 1, s.length());
207        }
208        sk = s;
209
210        re = new BigRational(sr.trim());
211        im = new BigRational(si.trim());
212        jm = new BigRational(sj.trim());
213        km = new BigRational(sk.trim());
214    }
215
216
217    /**
218     * Get the corresponding element factory.
219     * @return factory for this Element.
220     * @see edu.jas.structure.Element#factory()
221     */
222    public BigQuaternionRing factory() {
223        return ring;
224    }
225
226
227    /**
228     * Clone this.
229     * @see java.lang.Object#clone()
230     */
231    @Override
232    public BigQuaternion copy() {
233        return new BigQuaternion(ring, re, im, jm, km);
234    }
235
236
237    /**
238     * Get the real part.
239     * @return re.
240     */
241    public BigRational getRe() {
242        return re;
243    }
244
245
246    /**
247     * Get the imaginary part im.
248     * @return im.
249     */
250    public BigRational getIm() {
251        return im;
252    }
253
254
255    /**
256     * Get the imaginary part jm.
257     * @return jm.
258     */
259    public BigRational getJm() {
260        return jm;
261    }
262
263
264    /**
265     * Get the imaginary part km.
266     * @return km.
267     */
268    public BigRational getKm() {
269        return km;
270    }
271
272
273    /**
274     * Get the string representation. Is compatible with the string constructor.
275     * @see java.lang.Object#toString()
276     */
277    @Override
278    public String toString() {
279        StringBuffer sb = new StringBuffer(re.toString());
280        int i = im.compareTo(BigRational.ZERO);
281        int j = jm.compareTo(BigRational.ZERO);
282        int k = km.compareTo(BigRational.ZERO);
283        if (debug) {
284            logger.debug("compareTo " + im + " ? 0 = " + i);
285            logger.debug("compareTo " + jm + " ? 0 = " + j);
286            logger.debug("compareTo " + km + " ? 0 = " + k);
287        }
288        if (i == 0 && j == 0 && k == 0) {
289            return sb.toString();
290        }
291        sb.append("i" + im);
292        sb.append("j" + jm);
293        sb.append("k" + km);
294        String s = sb.toString();
295        //s = s.replaceAll("-","~"); 
296        return s;
297    }
298
299
300    /**
301     * Get a scripting compatible string representation.
302     * @return script compatible representation for this Element.
303     * @see edu.jas.structure.Element#toScript()
304     */
305    @Override
306    public String toScript() {
307        // Python case
308        StringBuffer s = new StringBuffer();
309        boolean i = im.isZERO();
310        boolean j = jm.isZERO();
311        boolean k = km.isZERO();
312        if (i && j && k) {
313            if (re.isZERO()) {
314                return "0 ";
315            }
316            if (!re.isONE()) {
317                s.append(re.toScript() + "*");
318            }
319            s.append("oneQ ");
320            return s.toString();
321        }
322        if (!re.isZERO()) {
323            if (!re.isONE()) {
324                s.append(re.toScript() + "*");
325            }
326            s.append("oneQ ");
327        }
328        if (!i) {
329            if (s.length() > 0) {
330                s.append("+ ");
331            }
332            if (!im.isONE()) {
333                s.append(im.toScript() + "*");
334            }
335            s.append("IQ ");
336        }
337        if (!j) {
338            if (s.length() > 0) {
339                s.append("+ ");
340            }
341            if (!jm.isONE()) {
342                s.append(jm.toScript() + "*");
343            }
344            s.append("JQ ");
345        }
346        if (!k) {
347            if (s.length() > 0) {
348                s.append("+ ");
349            }
350            if (!km.isONE()) {
351                s.append(km.toScript() + "*");
352            }
353            s.append("KQ ");
354        }
355        return s.toString();
356    }
357
358
359    /**
360     * Get a scripting compatible string representation of the factory.
361     * @return script compatible representation for this ElemFactory.
362     * @see edu.jas.structure.Element#toScriptFactory()
363     */
364    @Override
365    public String toScriptFactory() {
366        // Python case
367        return ring.toScript();
368    }
369
370
371    /**
372     * Is Quaternion number zero.
373     * @param A BigQuaternion.
374     * @return true if A is 0, else false.
375     */
376    public static boolean isQZERO(BigQuaternion A) {
377        if (A == null)
378            return false;
379        return A.isZERO();
380    }
381
382
383    /**
384     * Is BigQuaternion number zero.
385     * @return true if this is 0, else false.
386     * @see edu.jas.structure.RingElem#isZERO()
387     */
388    public boolean isZERO() {
389        return re.isZERO() && im.isZERO() && jm.isZERO() && km.isZERO();
390    }
391
392
393    /**
394     * Is BigQuaternion number one.
395     * @param A is a quaternion number.
396     * @return true if A is 1, else false.
397     */
398    public static boolean isQONE(BigQuaternion A) {
399        if (A == null)
400            return false;
401        return A.isONE();
402    }
403
404
405    /**
406     * Is BigQuaternion number one.
407     * @see edu.jas.structure.RingElem#isONE()
408     * @return true if this is 1, else false.
409     */
410    public boolean isONE() {
411        return re.isONE() && im.isZERO() && jm.isZERO() && km.isZERO();
412    }
413
414
415    /**
416     * Is BigQuaternion imaginary one.
417     * @return true if this is i, else false.
418     */
419    public boolean isIMAG() {
420        return re.isZERO() && im.isONE() && jm.isZERO() && km.isZERO();
421    }
422
423
424    /**
425     * Is BigQuaternion unit element.
426     * @return If this is a unit then true is returned, else false.
427     * @see edu.jas.structure.RingElem#isUnit()
428     */
429    public boolean isUnit() {
430        //if (ring.integral) { not meaningful to test
431        //    System.out.println("*** entier isUnit case not implemented ***");
432        //}
433        return !isZERO();
434    }
435
436
437    /**
438     * Is BigQuaternion entier element.
439     * @return If this is an integer Hurwitz element then true is returned, else
440     *         false.
441     */
442    public boolean isEntier() {
443        if (re.isEntier() && im.isEntier() && jm.isEntier() && km.isEntier()) {
444            return true;
445        }
446        java.math.BigInteger TWO = BigInteger.TWO.val;
447        return re.den.equals(TWO) && im.den.equals(TWO) && jm.den.equals(TWO) && km.den.equals(TWO);
448    }
449
450
451    /**
452     * Comparison with any other object.
453     * @see java.lang.Object#equals(java.lang.Object)
454     */
455    @Override
456    public boolean equals(Object b) {
457        if (!(b instanceof BigQuaternion)) {
458            return false;
459        }
460        BigQuaternion B = (BigQuaternion) b;
461        // ring == B.ring ?
462        return re.equals(B.re) && im.equals(B.im) && jm.equals(B.jm) && km.equals(B.km);
463    }
464
465
466    /**
467     * Hash code for this BigQuaternion.
468     * @see java.lang.Object#hashCode()
469     */
470    @Override
471    public int hashCode() {
472        int h = re.hashCode();
473        h += h * 37 + im.hashCode();
474        h += h * 37 + jm.hashCode();
475        h += h * 37 + km.hashCode();
476        return h;
477    }
478
479
480    /**
481     * Since quaternion numbers are unordered, we use lexicographical order of
482     * re, im, jm and km.
483     * @param b BigQuaternion.
484     * @return 0 if b is equal to this, 1 if this is greater b and -1 else.
485     */
486    @Override
487    public int compareTo(BigQuaternion b) {
488        int s = re.compareTo(b.re);
489        if (s != 0) {
490            return s;
491        }
492        s = im.compareTo(b.im);
493        if (s != 0) {
494            return s;
495        }
496        s = jm.compareTo(b.jm);
497        if (s != 0) {
498            return s;
499        }
500        return km.compareTo(b.km);
501    }
502
503
504    /**
505     * Since quaternion numbers are unordered, we use lexicographical order of
506     * re, im, jm and km.
507     * @return 0 if this is equal to 0; 1 if re > 0, or re == 0 and im > 0, or
508     *         ...; -1 if re < 0, or re == 0 and im < 0, or ...
509     * @see edu.jas.structure.RingElem#signum()
510     */
511    public int signum() {
512        int s = re.signum();
513        if (s != 0) {
514            return s;
515        }
516        s = im.signum();
517        if (s != 0) {
518            return s;
519        }
520        s = jm.signum();
521        if (s != 0) {
522            return s;
523        }
524        return km.signum();
525    }
526
527
528    /* arithmetic operations: +, -, -
529     */
530
531    /**
532     * BigQuaternion summation.
533     * @param B BigQuaternion.
534     * @return this+B.
535     */
536    public BigQuaternion sum(BigQuaternion B) {
537        return new BigQuaternion(ring, re.sum(B.re), im.sum(B.im), jm.sum(B.jm), km.sum(B.km));
538    }
539
540
541    /**
542     * Quaternion number sum.
543     * @param A BigQuaternion.
544     * @param B BigQuaternion.
545     * @return A+B.
546     */
547    public static BigQuaternion QSUM(BigQuaternion A, BigQuaternion B) {
548        if (A == null)
549            return null;
550        return A.sum(B);
551    }
552
553
554    /**
555     * Quaternion number difference.
556     * @param A BigQuaternion.
557     * @param B BigQuaternion.
558     * @return A-B.
559     */
560    public static BigQuaternion QDIF(BigQuaternion A, BigQuaternion B) {
561        if (A == null)
562            return null;
563        return A.subtract(B);
564    }
565
566
567    /**
568     * BigQuaternion subtraction.
569     * @param B BigQuaternion.
570     * @return this-B.
571     */
572    public BigQuaternion subtract(BigQuaternion B) {
573        return new BigQuaternion(ring, re.subtract(B.re), im.subtract(B.im), jm.subtract(B.jm),
574                        km.subtract(B.km));
575    }
576
577
578    /**
579     * Quaternion number negative.
580     * @param A is a quaternion number
581     * @return -A.
582     */
583    public static BigQuaternion QNEG(BigQuaternion A) {
584        if (A == null)
585            return null;
586        return A.negate();
587    }
588
589
590    /**
591     * BigQuaternion number negative.
592     * @return -this.
593     * @see edu.jas.structure.RingElem#negate()
594     */
595    public BigQuaternion negate() {
596        return new BigQuaternion(ring, re.negate(), im.negate(), jm.negate(), km.negate());
597    }
598
599
600    /**
601     * Quaternion number conjugate.
602     * @param A is a quaternion number.
603     * @return the quaternion conjugate of A.
604     */
605    public static BigQuaternion QCON(BigQuaternion A) {
606        if (A == null)
607            return null;
608        return A.conjugate();
609    }
610
611
612    /* arithmetic operations: conjugate, absolute value 
613     */
614
615    /**
616     * BigQuaternion conjugate.
617     * @return conjugate(this).
618     */
619    public BigQuaternion conjugate() {
620        return new BigQuaternion(ring, re, im.negate(), jm.negate(), km.negate());
621    }
622
623
624    /**
625     * Quaternion number norm.
626     * @see edu.jas.structure.StarRingElem#norm()
627     * @return ||this||.
628     */
629    public BigQuaternion norm() {
630        // this.multiply(this.conjugate());
631        BigRational v = re.multiply(re);
632        v = v.sum(im.multiply(im));
633        v = v.sum(jm.multiply(jm));
634        v = v.sum(km.multiply(km));
635        return new BigQuaternion(ring, v);
636    }
637
638
639    /**
640     * Quaternion number absolute value.
641     * @see edu.jas.structure.RingElem#abs()
642     * @return |this|. 
643     */
644    public BigQuaternion abs() {
645        BigQuaternion n = norm();
646        BigRational r = Roots.sqrt(n.re);
647        //logger.error("abs() square root missing");
648        return new BigQuaternion(ring, r);
649    }
650
651
652    /**
653     * Quaternion number absolute value.
654     * @param A is a quaternion number.
655     * @return the absolute value of A, a rational number. Note: The square root
656     *         is not jet implemented.
657     */
658    public static BigRational QABS(BigQuaternion A) {
659        if (A == null)
660            return null;
661        return A.abs().re;
662    }
663
664
665    /**
666     * Quaternion number product.
667     * @param A BigQuaternion.
668     * @param B BigQuaternion.
669     * @return A*B.
670     */
671    public static BigQuaternion QPROD(BigQuaternion A, BigQuaternion B) {
672        if (A == null)
673            return null;
674        return A.multiply(B);
675    }
676
677
678    /* arithmetic operations: *, inverse, / 
679     */
680
681    /**
682     * BigQuaternion multiply with BigRational.
683     * @param b BigRational.
684     * @return this*b.
685     */
686    public BigQuaternion multiply(BigRational b) {
687        BigRational r = re.multiply(b);
688        BigRational i = im.multiply(b);
689        BigRational j = jm.multiply(b);
690        BigRational k = km.multiply(b);
691        return new BigQuaternion(ring, r, i, j, k);
692    }
693
694
695    /**
696     * BigQuaternion multiply.
697     * @param B BigQuaternion.
698     * @return this*B.
699     */
700    public BigQuaternion multiply(BigQuaternion B) {
701        BigRational r = re.multiply(B.re);
702        r = r.subtract(im.multiply(B.im));
703        r = r.subtract(jm.multiply(B.jm));
704        r = r.subtract(km.multiply(B.km));
705
706        BigRational i = re.multiply(B.im);
707        i = i.sum(im.multiply(B.re));
708        i = i.sum(jm.multiply(B.km));
709        i = i.subtract(km.multiply(B.jm));
710
711        BigRational j = re.multiply(B.jm);
712        j = j.subtract(im.multiply(B.km));
713        j = j.sum(jm.multiply(B.re));
714        j = j.sum(km.multiply(B.im));
715
716        BigRational k = re.multiply(B.km);
717        k = k.sum(im.multiply(B.jm));
718        k = k.subtract(jm.multiply(B.im));
719        k = k.sum(km.multiply(B.re));
720
721        return new BigQuaternion(ring, r, i, j, k);
722    }
723
724
725    /**
726     * Quaternion number inverse.
727     * @param A is a non-zero quaternion number.
728     * @return S with S * A = A * S = 1.
729     */
730    public static BigQuaternion QINV(BigQuaternion A) {
731        if (A == null)
732            return null;
733        return A.inverse();
734    }
735
736
737    /**
738     * BigQuaternion inverse.
739     * @return S with S * this = this * S = 1.
740     * @see edu.jas.structure.RingElem#inverse()
741     */
742    public BigQuaternion inverse() {
743        BigRational a = norm().re.inverse();
744        return new BigQuaternion(ring, re.multiply(a), im.negate().multiply(a), jm.negate().multiply(a),
745                        km.negate().multiply(a));
746    }
747
748
749    /**
750     * BigQuaternion remainder.
751     * @param S BigQuaternion.
752     * @return 0.
753     */
754    public BigQuaternion remainder(BigQuaternion S) {
755        if (S.isZERO()) {
756            throw new ArithmeticException("division by zero");
757        }
758        if (ring.integral) {
759            //System.out.println(
760            //       "*** entier right remainder(" + this + ", " + S + "): " + ring + " ***");
761            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
762            BigQuaternionInteger d = new BigQuaternionInteger(ring, S);
763            return c.rightRemainder(d);
764        }
765        return ring.getZERO();
766    }
767
768
769    /**
770     * Quaternion number quotient.
771     * @param A BigQuaternion.
772     * @param B BigQuaternion.
773     * @return R/S.
774     */
775    public static BigQuaternion QQ(BigQuaternion A, BigQuaternion B) {
776        if (A == null)
777            return null;
778        return A.divide(B);
779    }
780
781
782    /**
783     * BigQuaternion right divide.
784     * @param b BigQuaternion.
785     * @return this * b**(-1).
786     */
787    public BigQuaternion divide(BigQuaternion b) {
788        return rightDivide(b);
789    }
790
791
792    /**
793     * BigQuaternion right divide.
794     * @param b BigQuaternion.
795     * @return this * b**(-1).
796     */
797    @Override
798    public BigQuaternion rightDivide(BigQuaternion b) {
799        if (ring.integral) {
800            //System.out.println("*** entier right divide(" + this + ", " + b + "): " + ring + " ***");
801            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
802            BigQuaternionInteger d = new BigQuaternionInteger(ring, b);
803            return c.rightDivide(d);
804        }
805        return this.multiply(b.inverse());
806    }
807
808
809    /**
810     * BigQuaternion left divide.
811     * @param b BigQuaternion.
812     * @return b**(-1) * this.
813     */
814    @Override
815    public BigQuaternion leftDivide(BigQuaternion b) {
816        if (ring.integral) {
817            //System.out.println("*** entier left divide(" + this + ", " + b + "): " + ring + " ***");
818            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
819            BigQuaternionInteger d = new BigQuaternionInteger(ring, b);
820            return c.leftDivide(d);
821        }
822        return b.inverse().multiply(this);
823    }
824
825
826    /**
827     * BigQuaternion divide.
828     * @param b BigRational.
829     * @return this/b.
830     */
831    public BigQuaternion divide(BigRational b) {
832        BigRational bi = b.inverse();
833        return new BigQuaternion(ring, re.multiply(bi), im.multiply(bi), jm.multiply(bi), km.multiply(bi));
834    }
835
836
837    /**
838     * Quotient and remainder by division of this by S.
839     * @param S a quaternion number
840     * @return [this*S**(-1), this - (this*S**(-1))*S].
841     */
842    public BigQuaternion[] quotientRemainder(BigQuaternion S) {
843        if (ring.integral) {
844            //System.out.println(
845            //     "*** entier left quotient remainder(" + this + ", " + S + "): " + ring + " ***");
846            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
847            BigQuaternionInteger d = new BigQuaternionInteger(ring, S);
848            return c.rightQuotientAndRemainder(d);
849        }
850        return new BigQuaternion[] { divide(S), ring.getZERO() };
851    }
852
853
854    /**
855     * Quaternion number greatest common divisor.
856     * @param S BigQuaternion.
857     * @return gcd(this,S).
858     */
859    public BigQuaternion gcd(BigQuaternion S) {
860        return leftGcd(S);
861    }
862
863
864    /**
865     * Quaternion number greatest common divisor.
866     * @param S BigQuaternion.
867     * @return leftCcd(this,S).
868     */
869    public BigQuaternion leftGcd(BigQuaternion S) {
870        if (S == null || S.isZERO()) {
871            return this;
872        }
873        if (this.isZERO()) {
874            return S;
875        }
876        if (ring.integral) {
877            //System.out.println("*** entier left gcd(" + this + ", " + S + "): " + ring + " ***");
878            BigQuaternionInteger a = new BigQuaternionInteger(ring, this);
879            BigQuaternionInteger b = new BigQuaternionInteger(ring, S);
880            return a.leftGcd(b);
881        }
882        return ring.getONE();
883    }
884
885
886    /**
887     * Quaternion number greatest common divisor.
888     * @param S BigQuaternion.
889     * @return rightCcd(this,S).
890     */
891    public BigQuaternion rightGcd(BigQuaternion S) {
892        if (S == null || S.isZERO()) {
893            return this;
894        }
895        if (this.isZERO()) {
896            return S;
897        }
898        if (ring.integral) {
899            //System.out.println("*** entier right gcd(" + this + ", " + S + "): " + ring + " ***");
900            BigQuaternionInteger a = new BigQuaternionInteger(ring, this);
901            BigQuaternionInteger b = new BigQuaternionInteger(ring, S);
902            return a.rightGcd(b);
903        }
904        return ring.getONE();
905    }
906
907
908    /**
909     * BigQuaternion extended greatest common divisor.
910     * @param S BigQuaternion.
911     * @return [ gcd(this,S), a, b ] with a*this + b*S = gcd(this,S).
912     */
913    public BigQuaternion[] egcd(BigQuaternion S) {
914        if (ring.integral) {
915            System.out.println("*** entier egcd case not implemented ***");
916        }
917        BigQuaternion[] ret = new BigQuaternion[3];
918        ret[0] = null;
919        ret[1] = null;
920        ret[2] = null;
921        if (S == null || S.isZERO()) {
922            ret[0] = this;
923            return ret;
924        }
925        if (this.isZERO()) {
926            ret[0] = S;
927            return ret;
928        }
929        BigQuaternion half = new BigQuaternion(ring, new BigRational(1, 2));
930        ret[0] = ring.getONE();
931        ret[1] = this.inverse().multiply(half);
932        ret[2] = S.inverse().multiply(half);
933        return ret;
934    }
935
936
937    /**
938     * Returns the number of bits in the representation of this BigQuaternion,
939     * including a sign bit. It is equivalent to
940     * {@code re.bitLength()+im.bitLength()+jm.bitLength()+km.bitLength()}.)
941     * @return number of bits in the representation of this BigQuaternion,
942     *         including a sign bit.
943     */
944    public long bitLength() {
945        return re.bitLength() + im.bitLength() + jm.bitLength() + km.bitLength();
946    }
947
948
949    /**
950     * BigQuaternion ceiling, component wise.
951     * @return ceiling of this.
952     */
953    public BigQuaternion ceil() {
954        BigRational r = new BigRational(re.ceil());
955        BigRational i = new BigRational(im.ceil());
956        BigRational j = new BigRational(jm.ceil());
957        BigRational k = new BigRational(km.ceil());
958        return new BigQuaternion(ring, r, i, j, k);
959    }
960
961
962    /**
963     * BigQuaternion floor, component wise.
964     * @return floor of this.
965     */
966    public BigQuaternion floor() {
967        BigRational r = new BigRational(re.floor());
968        BigRational i = new BigRational(im.floor());
969        BigRational j = new BigRational(jm.floor());
970        BigRational k = new BigRational(km.floor());
971        return new BigQuaternion(ring, r, i, j, k);
972    }
973
974
975    /**
976     * BigQuaternion round to next Lipschitz integer. BigQuaternion with all
977     * integer components.
978     * @return Lipschitz integer of this.
979     */
980    public BigQuaternionInteger roundToLipschitzian() {
981        BigRational half = BigRational.HALF;
982        BigRational r = new BigRational(re.sum(half).floor());
983        BigRational i = new BigRational(im.sum(half).floor());
984        BigRational j = new BigRational(jm.sum(half).floor());
985        BigRational k = new BigRational(km.sum(half).floor());
986        return new BigQuaternionInteger(ring, r, i, j, k);
987    }
988
989
990    /**
991     * BigQuaternion round to next Hurwitz integer. BigQuaternion with all
992     * integer or all 1/2 times integer components.
993     * @return Hurwitz integer near this.
994     */
995    public BigQuaternionInteger roundToHurwitzian() {
996        if (isEntier()) {
997            //System.out.println("*** short cut to round ***");
998            return new BigQuaternionInteger(ring, this);
999        }
1000        BigQuaternionInteger g = this.roundToLipschitzian();
1001        BigQuaternion d = ring.getZERO();
1002        //BigRational half = BigRational.HALF;
1003        BigQuaternion s = this.subtract(g).norm();
1004        //System.out.println("s = " + s.toScript());
1005        //if (s.re.compareTo(half) < 0) { // wrong
1006        List<BigQuaternion> units = ring.unitsOfHurwitzian();
1007        BigQuaternion t = null;
1008        for (BigQuaternion ue : units) {
1009            //t = this.subtract(g).sum(ue).norm(); // bug
1010            t = this.subtract(g.sum(ue)).norm();
1011            if (t.re.compareTo(s.re) < 0) {
1012                s = t;
1013                d = ue;
1014            }
1015        }
1016        //System.out.println("ring = " + ring);
1017        g = new BigQuaternionInteger(ring, g.sum(d));
1018        return g;
1019    }
1020
1021}