001/*
002 * $Id$
003 */
004
005package edu.jas.arith;
006
007
008import java.util.List;
009import java.util.Random;
010
011import org.apache.logging.log4j.LogManager;
012import org.apache.logging.log4j.Logger;
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();
280        int r = re.compareTo(BigRational.ZERO);
281        if (r != 0) {
282            sb.append(re.toString());
283        }
284        int i = im.compareTo(BigRational.ZERO);
285        int j = jm.compareTo(BigRational.ZERO);
286        int k = km.compareTo(BigRational.ZERO);
287        if (debug) {
288            logger.debug("compareTo {} ? 0 = {}", im, i);
289            logger.debug("compareTo {} ? 0 = {}", jm, j);
290            logger.debug("compareTo {} ? 0 = {}", km, k);
291        }
292        if (i == 0 && j == 0 && k == 0) {
293            if (r == 0) {
294                sb.append(re.toString());
295            }
296            return sb.toString();
297        }
298        if (i != 0) {
299            sb.append("i" + im);
300        }
301        if (j != 0) {
302            sb.append("j" + jm);
303        }
304        if (k != 0) {
305            sb.append("k" + km);
306        }
307        String s = sb.toString();
308        //s = s.replaceAll("-","~"); 
309        return s;
310    }
311
312
313    /**
314     * Get a scripting compatible string representation.
315     * @return script compatible representation for this Element.
316     * @see edu.jas.structure.Element#toScript()
317     */
318    @Override
319    public String toScript() {
320        // Python case
321        StringBuffer s = new StringBuffer();
322        boolean i = im.isZERO();
323        boolean j = jm.isZERO();
324        boolean k = km.isZERO();
325        if (i && j && k) {
326            if (re.isZERO()) {
327                return "0 ";
328            }
329            if (!re.isONE()) {
330                s.append(re.toScript() + "*");
331            }
332            s.append("oneQ ");
333            return s.toString();
334        }
335        if (!re.isZERO()) {
336            if (!re.isONE()) {
337                s.append(re.toScript() + "*");
338            }
339            s.append("oneQ ");
340        }
341        if (!i) {
342            if (s.length() > 0) {
343                s.append("+ ");
344            }
345            if (!im.isONE()) {
346                s.append(im.toScript() + "*");
347            }
348            s.append("IQ ");
349        }
350        if (!j) {
351            if (s.length() > 0) {
352                s.append("+ ");
353            }
354            if (!jm.isONE()) {
355                s.append(jm.toScript() + "*");
356            }
357            s.append("JQ ");
358        }
359        if (!k) {
360            if (s.length() > 0) {
361                s.append("+ ");
362            }
363            if (!km.isONE()) {
364                s.append(km.toScript() + "*");
365            }
366            s.append("KQ ");
367        }
368        return s.toString();
369    }
370
371
372    /**
373     * Get a scripting compatible string representation of the factory.
374     * @return script compatible representation for this ElemFactory.
375     * @see edu.jas.structure.Element#toScriptFactory()
376     */
377    @Override
378    public String toScriptFactory() {
379        // Python case
380        return ring.toScript();
381    }
382
383
384    /**
385     * Is Quaternion number zero.
386     * @param A BigQuaternion.
387     * @return true if A is 0, else false.
388     */
389    public static boolean isQZERO(BigQuaternion A) {
390        if (A == null)
391            return false;
392        return A.isZERO();
393    }
394
395
396    /**
397     * Is BigQuaternion number zero.
398     * @return true if this is 0, else false.
399     * @see edu.jas.structure.RingElem#isZERO()
400     */
401    public boolean isZERO() {
402        return re.isZERO() && im.isZERO() && jm.isZERO() && km.isZERO();
403    }
404
405
406    /**
407     * Is BigQuaternion number one.
408     * @param A is a quaternion number.
409     * @return true if A is 1, else false.
410     */
411    public static boolean isQONE(BigQuaternion A) {
412        if (A == null)
413            return false;
414        return A.isONE();
415    }
416
417
418    /**
419     * Is BigQuaternion number one.
420     * @see edu.jas.structure.RingElem#isONE()
421     * @return true if this is 1, else false.
422     */
423    public boolean isONE() {
424        return re.isONE() && im.isZERO() && jm.isZERO() && km.isZERO();
425    }
426
427
428    /**
429     * Is BigQuaternion imaginary one.
430     * @return true if this is i, else false.
431     */
432    public boolean isIMAG() {
433        return re.isZERO() && im.isONE() && jm.isZERO() && km.isZERO();
434    }
435
436
437    /**
438     * Is BigQuaternion unit element.
439     * @return If this is a unit then true is returned, else false.
440     * @see edu.jas.structure.RingElem#isUnit()
441     */
442    public boolean isUnit() {
443        //if (ring.integral) { not meaningful to test
444        //    System.out.println("*** entier isUnit case not implemented ***");
445        //}
446        return !isZERO();
447    }
448
449
450    /**
451     * Is BigQuaternion entier element.
452     * @return If this is an integer Hurwitz element then true is returned, else
453     *         false.
454     */
455    public boolean isEntier() {
456        if (re.isEntier() && im.isEntier() && jm.isEntier() && km.isEntier()) {
457            return true;
458        }
459        java.math.BigInteger TWO = BigInteger.TWO.val;
460        return re.den.equals(TWO) && im.den.equals(TWO) && jm.den.equals(TWO) && km.den.equals(TWO);
461    }
462
463
464    /**
465     * Comparison with any other object.
466     * @see java.lang.Object#equals(java.lang.Object)
467     */
468    @Override
469    public boolean equals(Object b) {
470        if (!(b instanceof BigQuaternion)) {
471            return false;
472        }
473        BigQuaternion B = (BigQuaternion) b;
474        // ring == B.ring ?
475        return re.equals(B.re) && im.equals(B.im) && jm.equals(B.jm) && km.equals(B.km);
476    }
477
478
479    /**
480     * Hash code for this BigQuaternion.
481     * @see java.lang.Object#hashCode()
482     */
483    @Override
484    public int hashCode() {
485        int h = re.hashCode();
486        h += h * 37 + im.hashCode();
487        h += h * 37 + jm.hashCode();
488        h += h * 37 + km.hashCode();
489        return h;
490    }
491
492
493    /**
494     * Since quaternion numbers are unordered, we use lexicographical order of
495     * re, im, jm and km.
496     * @param b BigQuaternion.
497     * @return 0 if b is equal to this, 1 if this is greater b and -1 else.
498     */
499    @Override
500    public int compareTo(BigQuaternion b) {
501        int s = re.compareTo(b.re);
502        if (s != 0) {
503            return s;
504        }
505        s = im.compareTo(b.im);
506        if (s != 0) {
507            return s;
508        }
509        s = jm.compareTo(b.jm);
510        if (s != 0) {
511            return s;
512        }
513        return km.compareTo(b.km);
514    }
515
516
517    /**
518     * Since quaternion numbers are unordered, we use lexicographical order of
519     * re, im, jm and km.
520     * @return 0 if this is equal to 0; 1 if re &gt; 0, or re == 0 and im &gt;
521     *         0, or ...; -1 if re &lt; 0, or re == 0 and im &lt; 0, or ...
522     * @see edu.jas.structure.RingElem#signum()
523     */
524    public int signum() {
525        int s = re.signum();
526        if (s != 0) {
527            return s;
528        }
529        s = im.signum();
530        if (s != 0) {
531            return s;
532        }
533        s = jm.signum();
534        if (s != 0) {
535            return s;
536        }
537        return km.signum();
538    }
539
540
541    /* arithmetic operations: +, -, -
542     */
543
544    /**
545     * BigQuaternion summation.
546     * @param B BigQuaternion.
547     * @return this+B.
548     */
549    public BigQuaternion sum(BigQuaternion B) {
550        return new BigQuaternion(ring, re.sum(B.re), im.sum(B.im), jm.sum(B.jm), km.sum(B.km));
551    }
552
553
554    /**
555     * Quaternion number sum.
556     * @param A BigQuaternion.
557     * @param B BigQuaternion.
558     * @return A+B.
559     */
560    public static BigQuaternion QSUM(BigQuaternion A, BigQuaternion B) {
561        if (A == null)
562            return null;
563        return A.sum(B);
564    }
565
566
567    /**
568     * Quaternion number difference.
569     * @param A BigQuaternion.
570     * @param B BigQuaternion.
571     * @return A-B.
572     */
573    public static BigQuaternion QDIF(BigQuaternion A, BigQuaternion B) {
574        if (A == null)
575            return null;
576        return A.subtract(B);
577    }
578
579
580    /**
581     * BigQuaternion subtraction.
582     * @param B BigQuaternion.
583     * @return this-B.
584     */
585    public BigQuaternion subtract(BigQuaternion B) {
586        return new BigQuaternion(ring, re.subtract(B.re), im.subtract(B.im), jm.subtract(B.jm),
587                        km.subtract(B.km));
588    }
589
590
591    /**
592     * Quaternion number negative.
593     * @param A is a quaternion number
594     * @return -A.
595     */
596    public static BigQuaternion QNEG(BigQuaternion A) {
597        if (A == null)
598            return null;
599        return A.negate();
600    }
601
602
603    /**
604     * BigQuaternion number negative.
605     * @return -this.
606     * @see edu.jas.structure.RingElem#negate()
607     */
608    public BigQuaternion negate() {
609        return new BigQuaternion(ring, re.negate(), im.negate(), jm.negate(), km.negate());
610    }
611
612
613    /**
614     * Quaternion number conjugate.
615     * @param A is a quaternion number.
616     * @return the quaternion conjugate of A.
617     */
618    public static BigQuaternion QCON(BigQuaternion A) {
619        if (A == null)
620            return null;
621        return A.conjugate();
622    }
623
624
625    /* arithmetic operations: conjugate, absolute value 
626     */
627
628    /**
629     * BigQuaternion conjugate.
630     * @return conjugate(this).
631     */
632    public BigQuaternion conjugate() {
633        return new BigQuaternion(ring, re, im.negate(), jm.negate(), km.negate());
634    }
635
636
637    /**
638     * Quaternion number norm.
639     * @see edu.jas.structure.StarRingElem#norm()
640     * @return ||this||.
641     */
642    public BigQuaternion norm() {
643        // this.multiply(this.conjugate());
644        BigRational v = re.multiply(re);
645        v = v.sum(im.multiply(im));
646        v = v.sum(jm.multiply(jm));
647        v = v.sum(km.multiply(km));
648        return new BigQuaternion(ring, v);
649    }
650
651
652    /**
653     * Quaternion number absolute value.
654     * @see edu.jas.structure.RingElem#abs()
655     * @return |this|.
656     */
657    public BigQuaternion abs() {
658        BigQuaternion n = norm();
659        BigRational r = Roots.sqrt(n.re);
660        //logger.error("abs() square root missing");
661        return new BigQuaternion(ring, r);
662    }
663
664
665    /**
666     * Quaternion number absolute value.
667     * @param A is a quaternion number.
668     * @return the absolute value of A, a rational number. Note: The square root
669     *         is not jet implemented.
670     */
671    public static BigRational QABS(BigQuaternion A) {
672        if (A == null)
673            return null;
674        return A.abs().re;
675    }
676
677
678    /**
679     * Quaternion number product.
680     * @param A BigQuaternion.
681     * @param B BigQuaternion.
682     * @return A*B.
683     */
684    public static BigQuaternion QPROD(BigQuaternion A, BigQuaternion B) {
685        if (A == null)
686            return null;
687        return A.multiply(B);
688    }
689
690
691    /* arithmetic operations: *, inverse, / 
692     */
693
694    /**
695     * BigQuaternion multiply with BigRational.
696     * @param b BigRational.
697     * @return this*b.
698     */
699    public BigQuaternion multiply(BigRational b) {
700        BigRational r = re.multiply(b);
701        BigRational i = im.multiply(b);
702        BigRational j = jm.multiply(b);
703        BigRational k = km.multiply(b);
704        return new BigQuaternion(ring, r, i, j, k);
705    }
706
707
708    /**
709     * BigQuaternion multiply.
710     * @param B BigQuaternion.
711     * @return this*B.
712     */
713    public BigQuaternion multiply(BigQuaternion B) {
714        BigRational r = re.multiply(B.re);
715        r = r.subtract(im.multiply(B.im));
716        r = r.subtract(jm.multiply(B.jm));
717        r = r.subtract(km.multiply(B.km));
718
719        BigRational i = re.multiply(B.im);
720        i = i.sum(im.multiply(B.re));
721        i = i.sum(jm.multiply(B.km));
722        i = i.subtract(km.multiply(B.jm));
723
724        BigRational j = re.multiply(B.jm);
725        j = j.subtract(im.multiply(B.km));
726        j = j.sum(jm.multiply(B.re));
727        j = j.sum(km.multiply(B.im));
728
729        BigRational k = re.multiply(B.km);
730        k = k.sum(im.multiply(B.jm));
731        k = k.subtract(jm.multiply(B.im));
732        k = k.sum(km.multiply(B.re));
733
734        return new BigQuaternion(ring, r, i, j, k);
735    }
736
737
738    /**
739     * BigQuaternion multiply left.
740     * @param B BigQuaternion.
741     * @return B*this.
742     */
743    public BigQuaternion multiplyLeft(BigQuaternion B) {
744        return B.multiply(this);
745    }
746
747
748    /**
749     * Quaternion number inverse.
750     * @param A is a non-zero quaternion number.
751     * @return S with S * A = A * S = 1.
752     */
753    public static BigQuaternion QINV(BigQuaternion A) {
754        if (A == null)
755            return null;
756        return A.inverse();
757    }
758
759
760    /**
761     * BigQuaternion inverse.
762     * @return S with S * this = this * S = 1.
763     * @see edu.jas.structure.RingElem#inverse()
764     */
765    public BigQuaternion inverse() {
766        BigRational a = norm().re.inverse();
767        return new BigQuaternion(ring, re.multiply(a), im.negate().multiply(a), jm.negate().multiply(a),
768                        km.negate().multiply(a));
769    }
770
771
772    /**
773     * BigQuaternion remainder.
774     * @param S BigQuaternion.
775     * @return 0.
776     */
777    public BigQuaternion remainder(BigQuaternion S) {
778        if (S.isZERO()) {
779            throw new ArithmeticException("division by zero");
780        }
781        if (ring.integral) {
782            //System.out.println(
783            //       "*** entier right remainder(" + this + ", " + S + "): " + ring + " ***");
784            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
785            BigQuaternionInteger d = new BigQuaternionInteger(ring, S);
786            return c.rightRemainder(d);
787        }
788        return ring.getZERO();
789    }
790
791
792    /**
793     * Quaternion number quotient.
794     * @param A BigQuaternion.
795     * @param B BigQuaternion.
796     * @return R/S.
797     */
798    public static BigQuaternion QQ(BigQuaternion A, BigQuaternion B) {
799        if (A == null)
800            return null;
801        return A.divide(B);
802    }
803
804
805    /**
806     * BigQuaternion right divide.
807     * @param b BigQuaternion.
808     * @return this * b**(-1).
809     */
810    public BigQuaternion divide(BigQuaternion b) {
811        return rightDivide(b);
812    }
813
814
815    /**
816     * BigQuaternion right divide.
817     * @param b BigQuaternion.
818     * @return this * b**(-1).
819     */
820    @Override
821    public BigQuaternion rightDivide(BigQuaternion b) {
822        if (ring.integral) {
823            //System.out.println("*** entier right divide(" + this + ", " + b + "): " + ring + " ***");
824            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
825            BigQuaternionInteger d = new BigQuaternionInteger(ring, b);
826            return c.rightDivide(d);
827        }
828        return this.multiply(b.inverse());
829    }
830
831
832    /**
833     * BigQuaternion left divide.
834     * @param b BigQuaternion.
835     * @return b**(-1) * this.
836     */
837    @Override
838    public BigQuaternion leftDivide(BigQuaternion b) {
839        if (ring.integral) {
840            //System.out.println("*** entier left divide(" + this + ", " + b + "): " + ring + " ***");
841            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
842            BigQuaternionInteger d = new BigQuaternionInteger(ring, b);
843            return c.leftDivide(d);
844        }
845        return b.inverse().multiply(this);
846    }
847
848
849    /**
850     * BigQuaternion divide.
851     * @param b BigRational.
852     * @return this/b.
853     */
854    public BigQuaternion divide(BigRational b) {
855        BigRational bi = b.inverse();
856        return new BigQuaternion(ring, re.multiply(bi), im.multiply(bi), jm.multiply(bi), km.multiply(bi));
857    }
858
859
860    /**
861     * Quotient and remainder by division of this by S.
862     * @param S a quaternion number
863     * @return [this*S**(-1), this - (this*S**(-1))*S].
864     */
865    public BigQuaternion[] quotientRemainder(BigQuaternion S) {
866        if (ring.integral) {
867            //System.out.println(
868            //     "*** entier left quotient remainder(" + this + ", " + S + "): " + ring + " ***");
869            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
870            BigQuaternionInteger d = new BigQuaternionInteger(ring, S);
871            return c.rightQuotientAndRemainder(d);
872        }
873        return new BigQuaternion[] { divide(S), ring.getZERO() };
874    }
875
876
877    /**
878     * Quaternion number greatest common divisor.
879     * @param S BigQuaternion.
880     * @return gcd(this,S).
881     */
882    public BigQuaternion gcd(BigQuaternion S) {
883        return leftGcd(S);
884    }
885
886
887    /**
888     * Quaternion number greatest common divisor.
889     * @param S BigQuaternion.
890     * @return leftCcd(this,S).
891     */
892    public BigQuaternion leftGcd(BigQuaternion S) {
893        if (S == null || S.isZERO()) {
894            return this;
895        }
896        if (this.isZERO()) {
897            return S;
898        }
899        if (ring.integral) {
900            //System.out.println("*** entier left gcd(" + this + ", " + S + "): " + ring + " ***");
901            BigQuaternionInteger a = new BigQuaternionInteger(ring, this);
902            BigQuaternionInteger b = new BigQuaternionInteger(ring, S);
903            return a.leftGcd(b);
904        }
905        return ring.getONE();
906    }
907
908
909    /**
910     * Quaternion number greatest common divisor.
911     * @param S BigQuaternion.
912     * @return rightCcd(this,S).
913     */
914    public BigQuaternion rightGcd(BigQuaternion S) {
915        if (S == null || S.isZERO()) {
916            return this;
917        }
918        if (this.isZERO()) {
919            return S;
920        }
921        if (ring.integral) {
922            //System.out.println("*** entier right gcd(" + this + ", " + S + "): " + ring + " ***");
923            BigQuaternionInteger a = new BigQuaternionInteger(ring, this);
924            BigQuaternionInteger b = new BigQuaternionInteger(ring, S);
925            return a.rightGcd(b);
926        }
927        return ring.getONE();
928    }
929
930
931    /**
932     * BigQuaternion extended greatest common divisor.
933     * @param S BigQuaternion.
934     * @return [ gcd(this,S), a, b ] with a*this + b*S = gcd(this,S).
935     */
936    public BigQuaternion[] egcd(BigQuaternion S) {
937        if (ring.integral) {
938            System.out.println("*** entier egcd case not implemented ***");
939        }
940        BigQuaternion[] ret = new BigQuaternion[3];
941        ret[0] = null;
942        ret[1] = null;
943        ret[2] = null;
944        if (S == null || S.isZERO()) {
945            ret[0] = this;
946            return ret;
947        }
948        if (this.isZERO()) {
949            ret[0] = S;
950            return ret;
951        }
952        BigQuaternion half = new BigQuaternion(ring, new BigRational(1, 2));
953        ret[0] = ring.getONE();
954        ret[1] = this.inverse().multiply(half);
955        ret[2] = S.inverse().multiply(half);
956        return ret;
957    }
958
959
960    /**
961     * Returns the number of bits in the representation of this BigQuaternion,
962     * including a sign bit. It is equivalent to
963     * {@code re.bitLength()+im.bitLength()+jm.bitLength()+km.bitLength()}.)
964     * @return number of bits in the representation of this BigQuaternion,
965     *         including a sign bit.
966     */
967    public long bitLength() {
968        return re.bitLength() + im.bitLength() + jm.bitLength() + km.bitLength();
969    }
970
971
972    /**
973     * BigQuaternion ceiling, component wise.
974     * @return ceiling of this.
975     */
976    public BigQuaternion ceil() {
977        BigRational r = new BigRational(re.ceil());
978        BigRational i = new BigRational(im.ceil());
979        BigRational j = new BigRational(jm.ceil());
980        BigRational k = new BigRational(km.ceil());
981        return new BigQuaternion(ring, r, i, j, k);
982    }
983
984
985    /**
986     * BigQuaternion floor, component wise.
987     * @return floor of this.
988     */
989    public BigQuaternion floor() {
990        BigRational r = new BigRational(re.floor());
991        BigRational i = new BigRational(im.floor());
992        BigRational j = new BigRational(jm.floor());
993        BigRational k = new BigRational(km.floor());
994        return new BigQuaternion(ring, r, i, j, k);
995    }
996
997
998    /**
999     * BigQuaternion round to next Lipschitz integer. BigQuaternion with all
1000     * integer components.
1001     * @return Lipschitz integer of this.
1002     */
1003    public BigQuaternionInteger roundToLipschitzian() {
1004        BigRational half = BigRational.HALF;
1005        BigRational r = new BigRational(re.sum(half).floor());
1006        BigRational i = new BigRational(im.sum(half).floor());
1007        BigRational j = new BigRational(jm.sum(half).floor());
1008        BigRational k = new BigRational(km.sum(half).floor());
1009        return new BigQuaternionInteger(ring, r, i, j, k);
1010    }
1011
1012
1013    /**
1014     * BigQuaternion round to next Hurwitz integer. BigQuaternion with all
1015     * integer or all 1/2 times integer components.
1016     * @return Hurwitz integer near this.
1017     */
1018    public BigQuaternionInteger roundToHurwitzian() {
1019        if (isEntier()) {
1020            //System.out.println("*** short cut to round ***");
1021            return new BigQuaternionInteger(ring, this);
1022        }
1023        BigQuaternionInteger g = this.roundToLipschitzian();
1024        BigQuaternion d = ring.getZERO();
1025        //BigRational half = BigRational.HALF;
1026        BigQuaternion s = this.subtract(g).norm();
1027        //System.out.println("s = " + s.toScript());
1028        //if (s.re.compareTo(half) < 0) { // wrong
1029        List<BigQuaternion> units = ring.unitsOfHurwitzian();
1030        BigQuaternion t = null;
1031        for (BigQuaternion ue : units) {
1032            //t = this.subtract(g).sum(ue).norm(); // bug
1033            t = this.subtract(g.sum(ue)).norm();
1034            if (t.re.compareTo(s.re) < 0) {
1035                s = t;
1036                d = ue;
1037            }
1038        }
1039        //System.out.println("ring = " + ring);
1040        g = new BigQuaternionInteger(ring, g.sum(d));
1041        return g;
1042    }
1043
1044}