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 right remainder.
774     * @param S BigQuaternion.
775     * @return 0.
776     */
777    public BigQuaternion rightRemainder(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     * BigQuaternion (right) remainder.
794     * @param S BigQuaternion.
795     * @return 0.
796     */
797    public BigQuaternion remainder(BigQuaternion S) {
798        return rightRemainder(S);
799    }
800
801
802    /**
803     * BigQuaternion left remainder.
804     * @param S BigQuaternion.
805     * @return 0.
806     */
807    public BigQuaternion leftRemainder(BigQuaternion S) {
808        if (S.isZERO()) {
809            throw new ArithmeticException("division by zero");
810        }
811        if (ring.integral) {
812            //System.out.println(
813            //       "*** entier right remainder(" + this + ", " + S + "): " + ring + " ***");
814            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
815            BigQuaternionInteger d = new BigQuaternionInteger(ring, S);
816            return c.leftRemainder(d);
817        }
818        return ring.getZERO();
819    }
820
821
822    /**
823     * Quaternion number quotient.
824     * @param A BigQuaternion.
825     * @param B BigQuaternion.
826     * @return R/S.
827     */
828    public static BigQuaternion QQ(BigQuaternion A, BigQuaternion B) {
829        if (A == null)
830            return null;
831        return A.divide(B);
832    }
833
834
835    /**
836     * BigQuaternion right divide.
837     * @param b BigQuaternion.
838     * @return this * b**(-1).
839     */
840    public BigQuaternion divide(BigQuaternion b) {
841        return rightDivide(b);
842    }
843
844
845    /**
846     * BigQuaternion right divide.
847     * @param b BigQuaternion.
848     * @return q = this * b**(-1), such that q * b = this.
849     */
850    @Override
851    public BigQuaternion rightDivide(BigQuaternion b) {
852        if (ring.integral) {
853            //System.out.println("*** entier right divide(" + this + ", " + b + "): " + ring + " ***");
854            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
855            BigQuaternionInteger d = new BigQuaternionInteger(ring, b);
856            return c.rightDivide(d);
857        }
858        return this.multiply(b.inverse());
859    }
860
861
862    /**
863     * BigQuaternion left divide.
864     * @param b BigQuaternion.
865     * @return q = b**(-1) * this, such that b * q = this.
866     */
867    @Override
868    public BigQuaternion leftDivide(BigQuaternion b) {
869        if (ring.integral) {
870            //System.out.println("*** entier left divide(" + this + ", " + b + "): " + ring + " ***");
871            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
872            BigQuaternionInteger d = new BigQuaternionInteger(ring, b);
873            return c.leftDivide(d);
874        }
875        return b.inverse().multiply(this);
876    }
877
878
879    /**
880     * BigQuaternion divide.
881     * @param b BigRational.
882     * @return this/b.
883     */
884    public BigQuaternion divide(BigRational b) {
885        BigRational bi = b.inverse();
886        return new BigQuaternion(ring, re.multiply(bi), im.multiply(bi), jm.multiply(bi), km.multiply(bi));
887    }
888
889
890    /**
891     * Quotient and remainder by division of this by S.
892     * @param S a quaternion number
893     * @return [this*S**(-1), this - (this*S**(-1))*S].
894     */
895    public BigQuaternion[] quotientRemainder(BigQuaternion S) {
896        if (ring.integral) {
897            //System.out.println(
898            //     "*** entier left quotient remainder(" + this + ", " + S + "): " + ring + " ***");
899            BigQuaternionInteger c = new BigQuaternionInteger(ring, this);
900            BigQuaternionInteger d = new BigQuaternionInteger(ring, S);
901            return c.rightQuotientAndRemainder(d);
902        }
903        return new BigQuaternion[] { divide(S), ring.getZERO() };
904    }
905
906
907    /**
908     * Quaternion number greatest common divisor.
909     * @param S BigQuaternion.
910     * @return gcd(this,S).
911     */
912    public BigQuaternion gcd(BigQuaternion S) {
913        return leftGcd(S);
914    }
915
916
917    /**
918     * Quaternion number greatest common divisor.
919     * @param S BigQuaternion.
920     * @return leftCcd(this,S).
921     */
922    public BigQuaternion leftGcd(BigQuaternion S) {
923        if (S == null || S.isZERO()) {
924            return this;
925        }
926        if (this.isZERO()) {
927            return S;
928        }
929        if (ring.integral) {
930            //System.out.println("*** entier left gcd(" + this + ", " + S + "): " + ring + " ***");
931            BigQuaternionInteger a = new BigQuaternionInteger(ring, this);
932            BigQuaternionInteger b = new BigQuaternionInteger(ring, S);
933            return a.leftGcd(b);
934        }
935        return ring.getONE();
936    }
937
938
939    /**
940     * Quaternion number greatest common divisor.
941     * @param S BigQuaternion.
942     * @return rightCcd(this,S).
943     */
944    public BigQuaternion rightGcd(BigQuaternion S) {
945        if (S == null || S.isZERO()) {
946            return this;
947        }
948        if (this.isZERO()) {
949            return S;
950        }
951        if (ring.integral) {
952            //System.out.println("*** entier right gcd(" + this + ", " + S + "): " + ring + " ***");
953            BigQuaternionInteger a = new BigQuaternionInteger(ring, this);
954            BigQuaternionInteger b = new BigQuaternionInteger(ring, S);
955            return a.rightGcd(b);
956        }
957        return ring.getONE();
958    }
959
960
961    /**
962     * BigQuaternion extended greatest common divisor.
963     * @param S BigQuaternion.
964     * @return [ gcd(this,S), a, b ] with a*this + b*S = gcd(this,S).
965     */
966    public BigQuaternion[] egcd(BigQuaternion S) {
967        if (ring.integral) {
968            System.out.println("*** entier egcd case not implemented ***");
969        }
970        BigQuaternion[] ret = new BigQuaternion[3];
971        ret[0] = null;
972        ret[1] = null;
973        ret[2] = null;
974        if (S == null || S.isZERO()) {
975            ret[0] = this;
976            return ret;
977        }
978        if (this.isZERO()) {
979            ret[0] = S;
980            return ret;
981        }
982        BigQuaternion half = new BigQuaternion(ring, new BigRational(1, 2));
983        ret[0] = ring.getONE();
984        ret[1] = this.inverse().multiply(half);
985        ret[2] = S.inverse().multiply(half);
986        return ret;
987    }
988
989
990    /**
991     * Returns the number of bits in the representation of this BigQuaternion,
992     * including a sign bit. It is equivalent to
993     * {@code re.bitLength()+im.bitLength()+jm.bitLength()+km.bitLength()}.)
994     * @return number of bits in the representation of this BigQuaternion,
995     *         including a sign bit.
996     */
997    public long bitLength() {
998        return re.bitLength() + im.bitLength() + jm.bitLength() + km.bitLength();
999    }
1000
1001
1002    /**
1003     * BigQuaternion ceiling, component wise.
1004     * @return ceiling of this.
1005     */
1006    public BigQuaternion ceil() {
1007        BigRational r = new BigRational(re.ceil());
1008        BigRational i = new BigRational(im.ceil());
1009        BigRational j = new BigRational(jm.ceil());
1010        BigRational k = new BigRational(km.ceil());
1011        return new BigQuaternion(ring, r, i, j, k);
1012    }
1013
1014
1015    /**
1016     * BigQuaternion floor, component wise.
1017     * @return floor of this.
1018     */
1019    public BigQuaternion floor() {
1020        BigRational r = new BigRational(re.floor());
1021        BigRational i = new BigRational(im.floor());
1022        BigRational j = new BigRational(jm.floor());
1023        BigRational k = new BigRational(km.floor());
1024        return new BigQuaternion(ring, r, i, j, k);
1025    }
1026
1027
1028    /**
1029     * BigQuaternion round to next Lipschitz integer. BigQuaternion with all
1030     * integer components.
1031     * @return Lipschitz integer of this.
1032     */
1033    public BigQuaternionInteger roundToLipschitzian() {
1034        BigRational half = BigRational.HALF;
1035        BigRational r = new BigRational(re.sum(half).floor());
1036        BigRational i = new BigRational(im.sum(half).floor());
1037        BigRational j = new BigRational(jm.sum(half).floor());
1038        BigRational k = new BigRational(km.sum(half).floor());
1039        return new BigQuaternionInteger(ring, r, i, j, k);
1040    }
1041
1042
1043    /**
1044     * BigQuaternion round to next Hurwitz integer. BigQuaternion with all
1045     * integer or all 1/2 times integer components.
1046     * @return Hurwitz integer near this.
1047     */
1048    public BigQuaternionInteger roundToHurwitzian() {
1049        if (isEntier()) {
1050            //System.out.println("*** short cut to round ***");
1051            return new BigQuaternionInteger(ring, this);
1052        }
1053        BigQuaternionInteger g = this.roundToLipschitzian();
1054        BigQuaternion d = ring.getZERO();
1055        //BigRational half = BigRational.HALF;
1056        BigQuaternion s = this.subtract(g).norm();
1057        //System.out.println("s = " + s.toScript());
1058        //if (s.re.compareTo(half) < 0) { // wrong
1059        List<BigQuaternion> units = ring.unitsOfHurwitzian();
1060        BigQuaternion t = null;
1061        for (BigQuaternion ue : units) {
1062            //t = this.subtract(g).sum(ue).norm(); // bug
1063            t = this.subtract(g.sum(ue)).norm();
1064            if (t.re.compareTo(s.re) < 0) {
1065                s = t;
1066                d = ue;
1067            }
1068        }
1069        //System.out.println("ring = " + ring);
1070        g = new BigQuaternionInteger(ring, g.sum(d));
1071        return g;
1072    }
1073
1074}