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