001/*
002 * $Id$
003 */
004
005package edu.jas.arith;
006
007
008import java.io.Reader;
009import java.math.BigInteger;
010import java.util.ArrayList;
011import java.util.List;
012import java.util.Random;
013
014import org.apache.logging.log4j.LogManager;
015import org.apache.logging.log4j.Logger;
016
017import edu.jas.kern.StringUtil;
018import edu.jas.structure.GcdRingElem;
019import edu.jas.structure.RingFactory;
020import edu.jas.structure.StarRingElem;
021
022
023/**
024 * BigOctonion class based on BigRational implementing the RingElem interface
025 * and with the familiar MAS static method names. Objects of this class are
026 * immutable.
027 * @author Heinz Kredel
028 */
029
030public final class BigOctonion
031                implements StarRingElem<BigOctonion>, GcdRingElem<BigOctonion>, RingFactory<BigOctonion> {
032
033
034    /**
035     * First part of the data structure.
036     */
037    public final BigQuaternion or;
038
039
040    /**
041     * Second part of the data structure.
042     */
043    public final BigQuaternion oi;
044
045
046    private final static Random random = new Random();
047
048
049    private static final Logger logger = LogManager.getLogger(BigOctonion.class);
050
051
052    private static final boolean debug = logger.isDebugEnabled();
053
054
055    /**
056     * Constructor for a BigOctonion from Quaternions.
057     * @param r BigQuaternion.
058     * @param i BigQuaternion.
059     */
060    public BigOctonion(BigQuaternion r, BigQuaternion i) {
061        if (i == null) {
062            throw new IllegalArgumentException("null i not allowed");
063        }
064        this.or = r;
065        this.oi = i;
066        //if (ZERO == null) {
067        //ZERO = new BigOctonion(r.ring.ZERO, i.ring.ZERO);
068        //ONE = new BigOctonion(r.ring.ONE, i.ring.ZERO);
069        //I = new BigOctonion(r.ring.ZERO, i.ring.ONE);
070        //}
071    }
072
073
074    /**
075     * Constructor for a BigOctonion from BigQuaternion.
076     * @param r BigQuaternion.
077     */
078    public BigOctonion(BigQuaternion r) {
079        this(r, r.ring.ZERO);
080    }
081
082
083    /**
084     * Constructor for a BigOctonion from BigComplex.
085     * @param fac BigQuaternionRing.
086     * @param r BigComplex.
087     */
088    public BigOctonion(BigQuaternionRing fac, BigComplex r) {
089        this(new BigQuaternion(fac, r));
090    }
091
092
093    /**
094     * Constructor for a BigOctonion from BigRational.
095     * @param fac BigQuaternionRing.
096     * @param r BigRational.
097     */
098    public BigOctonion(BigQuaternionRing fac, BigRational r) {
099        this(new BigQuaternion(fac, r));
100    }
101
102
103    /**
104     * Constructor for a BigOctonion from long.
105     * @param fac BigQuaternionRing.
106     * @param r long.
107     */
108    public BigOctonion(BigQuaternionRing fac, long r) {
109        this(new BigQuaternion(fac, r));
110    }
111
112
113    /**
114     * Constructor for a BigOctonion with no arguments.
115     * @param fac BigQuaternionRing.
116     */
117    public BigOctonion(BigQuaternionRing fac) {
118        this(new BigQuaternion(fac));
119    }
120
121
122    /**
123     * The BigOctonion string constructor accepts the following formats: empty
124     * string, "quaternion", or "quat o quat" with no blanks around o if used as
125     * polynoial coefficient.
126     * @param fac BigQuaternionRing.
127     * @param s String.
128     * @throws NumberFormatException
129     */
130    public BigOctonion(BigQuaternionRing fac, String s) throws NumberFormatException {
131        if (s == null || s.length() == 0) {
132            or = getZERO().or;
133            oi = getZERO().oi;
134            return;
135        }
136        s = s.trim();
137        int o = s.indexOf("o");
138        if (o == -1) {
139            or = new BigQuaternion(fac, s);
140            oi = getZERO().oi;
141            return;
142        }
143        String sr = s.substring(0, o - 1);
144        String so = s.substring(o + 1, s.length());
145        or = new BigQuaternion(fac, sr.trim());
146        oi = new BigQuaternion(fac, so.trim());
147    }
148
149
150    /**
151     * Get the corresponding element factory.
152     * @return factory for this Element.
153     * @see edu.jas.structure.Element#factory()
154     */
155    public BigOctonion factory() {
156        return this;
157    }
158
159
160    /**
161     * Get a list of the generating elements.
162     * @return list of generators for the algebraic structure.
163     * @see edu.jas.structure.ElemFactory#generators()
164     */
165    public List<BigOctonion> generators() {
166        List<BigQuaternion> qg = or.ring.generators();
167        List<BigOctonion> g = new ArrayList<BigOctonion>(qg.size() * 2);
168        for (BigQuaternion q : qg) {
169            g.add(new BigOctonion(q));
170            g.add(new BigOctonion(or.ring.ZERO, q));
171        }
172        return g;
173    }
174
175
176    /**
177     * Is this structure finite or infinite.
178     * @return true if this structure is finite, else false.
179     * @see edu.jas.structure.ElemFactory#isFinite()
180     */
181    public boolean isFinite() {
182        return false;
183    }
184
185
186    /**
187     * Clone this.
188     * @see java.lang.Object#clone()
189     */
190    @Override
191    public BigOctonion copy() {
192        return new BigOctonion(or, oi);
193    }
194
195
196    /**
197     * Copy BigOctonion element c.
198     * @param c BigOctonion.
199     * @return a copy of c.
200     */
201    public BigOctonion copy(BigOctonion c) {
202        if (c == null) {
203            throw new IllegalArgumentException("copy of null not allowed");
204        }
205        return new BigOctonion(c.or, c.oi);
206    }
207
208
209    /**
210     * Get the zero element.
211     * @return 0 as BigOctonion.
212     */
213    public BigOctonion getZERO() {
214        if (ZERO == null) {
215            ZERO = new BigOctonion(or.ring.ZERO, or.ring.ZERO);
216            I = new BigOctonion(or.ring.ZERO, or.ring.ONE);
217        }
218        return ZERO;
219    }
220
221
222    /**
223     * Get the one element.
224     * @return q as BigOctonion.
225     */
226    public BigOctonion getONE() {
227        if (ONE == null) {
228            ONE = new BigOctonion(or.ring.ONE, or.ring.ZERO);
229        }
230        return ONE;
231    }
232
233
234    /**
235     * Query if this ring is commutative.
236     * @return false.
237     */
238    public boolean isCommutative() {
239        return false;
240    }
241
242
243    /**
244     * Query if this ring is associative.
245     * @return false.
246     */
247    public boolean isAssociative() {
248        return false;
249    }
250
251
252    /**
253     * Query if this ring is a field.
254     * @return true.
255     */
256    public boolean isField() {
257        return true;
258    }
259
260
261    /**
262     * Characteristic of this ring.
263     * @return characteristic of this ring.
264     */
265    public java.math.BigInteger characteristic() {
266        return java.math.BigInteger.ZERO;
267    }
268
269
270    /**
271     * Get a BigOctonion element from a BigInteger.
272     * @param a BigInteger.
273     * @return a BigOctonion.
274     */
275    public BigOctonion fromInteger(BigInteger a) {
276        return new BigOctonion(or.ring.fromInteger(a));
277    }
278
279
280    /**
281     * Get a BigOctonion element from a long.
282     * @param a long.
283     * @return a BigOctonion.
284     */
285    public BigOctonion fromInteger(long a) {
286        return new BigOctonion(or.ring.fromInteger(a));
287    }
288
289
290    /**
291     * The constant 0.
292     */
293    public BigOctonion ZERO; // = new BigOctonion(or.ring);
294
295
296    /**
297     * The constant 1.
298     */
299    public BigOctonion ONE; // = new BigOctonion(or.ring.ONE);
300
301
302    /**
303     * The constant i.
304     */
305    public BigOctonion I; // = new BigOctonion(or.ring.ZERO, oi.ring.ONE);
306
307
308    /**
309     * Get the or part.
310     * @return or.
311     */
312    public BigQuaternion getR() {
313        return or;
314    }
315
316
317    /**
318     * Get the oi part.
319     * @return oi.
320     */
321    public BigQuaternion getI() {
322        return oi;
323    }
324
325
326    /**
327     * Get the string representation. Is compatible with the string constructor.
328     * @see java.lang.Object#toString()
329     */
330    @Override
331    public String toString() {
332        String s = or.toString();
333        boolean i = oi.isZERO();
334        if (debug) {
335            logger.debug("compareTo {} ? 0 = {}", oi, i);
336        }
337        if (i) {
338            return s;
339        }
340        s += "o" + oi;
341        return s;
342    }
343
344
345    /**
346     * Get a scripting compatible string representation.
347     * @return script compatible representation for this Element.
348     * @see edu.jas.structure.Element#toScript()
349     */
350    @Override
351    public String toScript() {
352        // Python case
353        boolean i = oi.isZERO();
354        if (i && or.isZERO()) {
355            return "0 ";
356        }
357        StringBuffer s = new StringBuffer();
358        if (!or.isZERO()) {
359            String rs = or.toScript();
360            rs = rs.replaceAll("Q", "OR");
361            s.append(rs);
362            s.append(" ");
363        }
364        if (!i) {
365            if (s.length() > 0) {
366                s.append("+ ");
367            }
368            String is = oi.toScript();
369            is = is.replaceAll("Q", "OI");
370            s.append(is);
371        }
372        return s.toString();
373    }
374
375
376    /**
377     * Get a scripting compatible string representation of the factory.
378     * @return script compatible representation for this ElemFactory.
379     * @see edu.jas.structure.Element#toScriptFactory()
380     */
381    @Override
382    public String toScriptFactory() {
383        // Python case
384        return "Oct()";
385    }
386
387
388    /**
389     * Is Octonion number zero.
390     * @param A BigOctonion.
391     * @return true if A is 0, else false.
392     */
393    public static boolean isOZERO(BigOctonion A) {
394        if (A == null)
395            return false;
396        return A.isZERO();
397    }
398
399
400    /**
401     * Is BigOctonion number zero.
402     * @return true if this is 0, else false.
403     * @see edu.jas.structure.RingElem#isZERO()
404     */
405    public boolean isZERO() {
406        return or.isZERO() && oi.isZERO();
407    }
408
409
410    /**
411     * Is BigOctonion number one.
412     * @param A is a quaternion number.
413     * @return true if A is 1, else false.
414     */
415    public static boolean isOONE(BigOctonion A) {
416        if (A == null)
417            return false;
418        return A.isONE();
419    }
420
421
422    /**
423     * Is BigOctonion number one.
424     * @see edu.jas.structure.RingElem#isONE()
425     * @return true if this is 1, else false.
426     */
427    public boolean isONE() {
428        return or.isONE() && oi.isZERO();
429    }
430
431
432    /**
433     * Is BigOctonion imaginary one.
434     * @return true if this is i, else false.
435     */
436    public boolean isIMAG() {
437        return or.isZERO() && oi.isONE();
438    }
439
440
441    /**
442     * Is BigOctonion unit element.
443     * @return If this is a unit then true is returned, else false.
444     * @see edu.jas.structure.RingElem#isUnit()
445     */
446    public boolean isUnit() {
447        return !isZERO();
448    }
449
450
451    /**
452     * Comparison with any other object.
453     * @see java.lang.Object#equals(java.lang.Object)
454     */
455    @Override
456    public boolean equals(Object b) {
457        if (!(b instanceof BigOctonion))
458            return false;
459        BigOctonion B = (BigOctonion) b;
460        return or.equals(B.or) && oi.equals(B.oi);
461    }
462
463
464    /**
465     * Hash code for this BigOctonion.
466     * @see java.lang.Object#hashCode()
467     */
468    @Override
469    public int hashCode() {
470        int h = 41 * or.hashCode();
471        h += oi.hashCode();
472        return h;
473    }
474
475
476    /**
477     * Since quaternion numbers are unordered, we use lexicographical order of
478     * re, im, jm and km.
479     * @param b BigOctonion.
480     * @return 0 if b is equal to this, 1 if this is greater b and -1 else.
481     */
482    @Override
483    public int compareTo(BigOctonion b) {
484        int s = or.compareTo(b.or);
485        if (s != 0) {
486            return s;
487        }
488        return oi.compareTo(b.oi);
489    }
490
491
492    /**
493     * Since quaternion numbers are unordered, we use lexicographical order of
494     * re, im, jm and km.
495     * @return 0 if this is equal to 0; 1 if or &gt; 0, or or == 0 and oi &gt;
496     *         0; -1 if or &lt; 0, or or == 0 and oi &lt; 0.
497     * @see edu.jas.structure.RingElem#signum()
498     */
499    public int signum() {
500        int s = or.signum();
501        if (s != 0) {
502            return s;
503        }
504        return oi.signum();
505    }
506
507
508    /* arithmetic operations: +, -, -
509     */
510
511    /**
512     * BigOctonion summation.
513     * @param B BigOctonion.
514     * @return this+B.
515     */
516    public BigOctonion sum(BigOctonion B) {
517        return new BigOctonion(or.sum(B.or), oi.sum(B.oi));
518    }
519
520
521    /**
522     * Octonion number sum.
523     * @param A BigOctonion.
524     * @param B BigOctonion.
525     * @return A+B.
526     */
527    public static BigOctonion OSUM(BigOctonion A, BigOctonion B) {
528        if (A == null)
529            return null;
530        return A.sum(B);
531    }
532
533
534    /**
535     * Octonion number difference.
536     * @param A BigOctonion.
537     * @param B BigOctonion.
538     * @return A-B.
539     */
540    public static BigOctonion ODIF(BigOctonion A, BigOctonion B) {
541        if (A == null)
542            return null;
543        return A.subtract(B);
544    }
545
546
547    /**
548     * BigOctonion subtraction.
549     * @param B BigOctonion.
550     * @return this-B.
551     */
552    public BigOctonion subtract(BigOctonion B) {
553        return new BigOctonion(or.subtract(B.or), oi.subtract(B.oi));
554    }
555
556
557    /**
558     * Octonion number negative.
559     * @param A is a octonion number
560     * @return -A.
561     */
562    public static BigOctonion ONEG(BigOctonion A) {
563        if (A == null)
564            return null;
565        return A.negate();
566    }
567
568
569    /**
570     * BigOctonion number negative.
571     * @return -this.
572     * @see edu.jas.structure.RingElem#negate()
573     */
574    public BigOctonion negate() {
575        return new BigOctonion(or.negate(), oi.negate());
576    }
577
578
579    /**
580     * Octonion number conjugate.
581     * @param A is a quaternion number.
582     * @return the quaternion conjugate of A.
583     */
584    public static BigOctonion OCON(BigOctonion A) {
585        if (A == null)
586            return null;
587        return A.conjugate();
588    }
589
590
591    /* arithmetic operations: conjugate, absolute value 
592     */
593
594    /**
595     * BigOctonion conjugate.
596     * @return conjugate(this).
597     */
598    public BigOctonion conjugate() {
599        return new BigOctonion(or.conjugate(), oi.negate());
600    }
601
602
603    /**
604     * Octonion number norm.
605     * @see edu.jas.structure.StarRingElem#norm()
606     * @return ||this||.
607     */
608    public BigOctonion norm() {
609        // this.multiply(this.conjugate());
610        BigQuaternion v = or.norm();
611        v = v.sum(oi.norm());
612        return new BigOctonion(v);
613    }
614
615
616    /**
617     * Octonion number absolute value.
618     * @see edu.jas.structure.RingElem#abs()
619     * @return |this|.
620     */
621    public BigOctonion abs() {
622        BigOctonion n = norm();
623        BigRational r = Roots.sqrt(n.or.re);
624        //logger.error("abs() square root missing");
625        return new BigOctonion(new BigQuaternion(n.or.ring, r));
626    }
627
628
629    /**
630     * Octonion number absolute value.
631     * @param A is a quaternion number.
632     * @return the absolute value of A, a rational number. Note: The square root
633     *         is not jet implemented.
634     */
635    public static BigRational OABS(BigOctonion A) {
636        if (A == null)
637            return null;
638        return A.abs().or.re;
639    }
640
641
642    /**
643     * Octonion number product.
644     * @param A BigOctonion.
645     * @param B BigOctonion.
646     * @return A*B.
647     */
648    public static BigOctonion OPROD(BigOctonion A, BigOctonion B) {
649        if (A == null)
650            return null;
651        return A.multiply(B);
652    }
653
654
655    /* arithmetic operations: *, inverse, / 
656     */
657
658    /**
659     * BigOctonion multiply.
660     * @param B BigOctonion.
661     * @return this*B.
662     */
663    public BigOctonion multiply(BigOctonion B) {
664        // (r1,i1)(r2,i2) = ( r1 r2 - i2 i1^, r1^ i2 + r2 i1 ) Baez, jas
665        // (r1,i1)(r2,i2) = ( r1 r2 - i2^ i1, i1 r2^ + i2 r1 ) Dieudonne, mas
666        BigQuaternion r = or.multiply(B.or);
667        r = r.subtract(B.oi.multiply(oi.conjugate()));
668        BigQuaternion i = or.conjugate().multiply(B.oi);
669        i = i.sum(B.or.multiply(oi));
670        return new BigOctonion(r, i);
671    }
672
673
674    /**
675     * Octonion number inverse.
676     * @param A is a non-zero quaternion number.
677     * @return S with S * A = 1.
678     */
679    public static BigOctonion OINV(BigOctonion A) {
680        if (A == null)
681            return null;
682        return A.inverse();
683    }
684
685
686    /**
687     * BigOctonion inverse.
688     * @return S with S * this = 1.
689     * @see edu.jas.structure.RingElem#inverse()
690     */
691    public BigOctonion inverse() {
692        BigRational a = norm().or.re;
693        return conjugate().divide(a);
694    }
695
696
697    /**
698     * BigOctonion remainder.
699     * @param S BigOctonion.
700     * @return 0.
701     */
702    public BigOctonion remainder(BigOctonion S) {
703        if (S.isZERO()) {
704            throw new ArithmeticException("division by zero");
705        }
706        return ZERO;
707    }
708
709
710    /**
711     * Octonion number quotient.
712     * @param A BigOctonion.
713     * @param B BigOctonion.
714     * @return R/S.
715     */
716    public static BigOctonion OQ(BigOctonion A, BigOctonion B) {
717        if (A == null)
718            return null;
719        return A.divide(B);
720    }
721
722
723    /**
724     * BigOctonion divide.
725     * @param b BigOctonion.
726     * @return this * b**(-1).
727     */
728    public BigOctonion divide(BigOctonion b) {
729        return rightDivide(b);
730    }
731
732
733    /**
734     * BigOctonion right divide.
735     * @param b BigOctonion.
736     * @return this * b**(-1).
737     */
738    public BigOctonion rightDivide(BigOctonion b) {
739        return this.multiply(b.inverse());
740    }
741
742
743    /**
744     * BigOctonion left divide.
745     * @param b BigOctonion.
746     * @return b**(-1) * this.
747     */
748    public BigOctonion leftDivide(BigOctonion b) {
749        return b.inverse().multiply(this);
750    }
751
752
753    /**
754     * BigOctonion divide.
755     * @param b BigRational.
756     * @return this/b.
757     */
758    public BigOctonion divide(BigRational b) {
759        BigRational bi = b.inverse();
760        return new BigOctonion(or.multiply(bi), oi.multiply(bi));
761    }
762
763
764    /**
765     * Quotient and remainder by division of this by S.
766     * @param S a octonion number
767     * @return [this/S, this - (this/S)*S].
768     */
769    public BigOctonion[] quotientRemainder(BigOctonion S) {
770        return new BigOctonion[] { divide(S), ZERO };
771    }
772
773
774    /**
775     * BigOctonion random. Random rational numbers A, B, C and D are generated
776     * using random(n). Then R is the quaternion number with real part A and
777     * imaginary parts B, C and D.
778     * @param n such that 0 &le; A, B, C, D &le; (2<sup>n</sup>-1).
779     * @return R, a random BigOctonion.
780     */
781    public BigOctonion random(int n) {
782        return random(n, random);
783    }
784
785
786    /**
787     * BigOctonion random. Random rational numbers A, B, C and D are generated
788     * using RNRAND(n). Then R is the quaternion number with real part A and
789     * imaginary parts B, C and D.
790     * @param n such that 0 &le; A, B, C, D &le; (2<sup>n</sup>-1).
791     * @param rnd is a source for random bits.
792     * @return R, a random BigOctonion.
793     */
794    public BigOctonion random(int n, Random rnd) {
795        BigQuaternion rr = or.ring.random(n, rnd);
796        BigQuaternion ir = oi.ring.random(n, rnd);
797        return new BigOctonion(rr, ir);
798    }
799
800
801    /*
802     * Octonion number, random. Random rational numbers A, B, C and D are
803     * generated using RNRAND(n). Then R is the quaternion number with real part
804     * A and imaginary parts B, C and D.
805     * @param n such that 0 &le; A, B, C, D &le; (2<sup>n</sup>-1).
806     * @return R, a random BigOctonion.
807    public static BigOctonion ORAND(int n) {
808        return random(n, random);
809    }
810    */
811
812    /**
813     * Parse quaternion number from String.
814     * @param s String.
815     * @return BigOctonion from s.
816     */
817    public BigOctonion parse(String s) {
818        return new BigOctonion(or.ring, s);
819    }
820
821
822    /**
823     * Parse quaternion number from Reader.
824     * @param r Reader.
825     * @return next BigOctonion from r.
826     */
827    public BigOctonion parse(Reader r) {
828        return parse(StringUtil.nextString(r));
829    }
830
831
832    /**
833     * Octonion number greatest common divisor.
834     * @param S BigOctonion.
835     * @return gcd(this,S).
836     */
837    public BigOctonion gcd(BigOctonion S) {
838        if (S == null || S.isZERO()) {
839            return this;
840        }
841        if (this.isZERO()) {
842            return S;
843        }
844        return ONE;
845    }
846
847
848    /**
849     * BigOctonion extended greatest common divisor.
850     * @param S BigOctonion.
851     * @return [ gcd(this,S), a, b ] with a*this + b*S = gcd(this,S).
852     */
853    public BigOctonion[] egcd(BigOctonion S) {
854        BigOctonion[] ret = new BigOctonion[3];
855        ret[0] = null;
856        ret[1] = null;
857        ret[2] = null;
858        if (S == null || S.isZERO()) {
859            ret[0] = this;
860            return ret;
861        }
862        if (this.isZERO()) {
863            ret[0] = S;
864            return ret;
865        }
866        BigOctonion half = new BigOctonion(or.ring, new BigRational(1, 2));
867        ret[0] = ONE;
868        ret[1] = this.inverse().multiply(half);
869        ret[2] = S.inverse().multiply(half);
870        return ret;
871    }
872
873
874    /**
875     * Returns the number of bits in the representation of this BigOctonion,
876     * including a sign bit. It is equivalent to
877     * {@code or.bitLength() + oi.bitLength()}.)
878     * @return number of bits in the representation of this BigOctonion,
879     *         including a sign bit.
880     */
881    public long bitLength() {
882        return or.bitLength() + oi.bitLength();
883    }
884
885}