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