(* ----------------------------------------------------------------------------
 * $Id: MASO.mi,v 1.3 1992/10/15 16:28:13 kredel Exp $
 * ----------------------------------------------------------------------------
 * This file is part of MAS.
 * ----------------------------------------------------------------------------
 * Copyright (c) 1989 - 1992 Universitaet Passau
 * ----------------------------------------------------------------------------
 * $Log: MASO.mi,v $
 * Revision 1.3  1992/10/15  16:28:13  kredel
 * Changed rcsid variable
 *
 * Revision 1.2  1992/09/28  18:34:53  kredel
 * Updated revision string.
 *
 * Revision 1.1  1992/09/28  17:30:31  kredel
 * Initial revision
 *
 * ----------------------------------------------------------------------------
 *)

IMPLEMENTATION MODULE MASO;

(* MAS Octonion Number Implementation Module. *)


(* Import lists and declarations. *)

FROM MASBIOS IMPORT SWRITE, CWRITE, CREADB, MASORD, BKSP, BLINES;

FROM MASSTOR IMPORT LIST, ADV, COMP, FIRST, SIL, INV; 

FROM MASERR IMPORT severe, ERROR;

FROM SACLIST IMPORT OWRITE, SECOND, FIRST2, LIST2, COMP2,  
                    AWRITE, EQUAL;

FROM SACRN IMPORT RNSUM, RNQ;

FROM MASQ IMPORT QCON, QABS, QNEG, QINT, QRN, QONE, QRAND, 
                 QPROD, QDIF, QSUM, 
                 QDREAD, QDWRITE, QNREAD, QNWRITE;

CONST rcsidi = "$Id: MASO.mi,v 1.3 1992/10/15 16:28:13 kredel Exp $";
CONST copyrighti = "Copyright (c) 1989 - 1992 Universitaet Passau";


(* Representation of ( a, b ), with a, b quaternion numbers, is 

                         0   if a=0 and b=0 
                     (a,b)   else   *)


PROCEDURE OABS(R: LIST): LIST;
(*Octonion number absolute value.  R is a octonion number.  S is the
absolute value of R, a rational number. *)
VAR   S, r, i: LIST;
BEGIN
(*1*) IF R = 0 THEN S:=R; RETURN(S) END;
(*2*) FIRST2(R,r,i); 
      S:=RNSUM(QABS(r),QABS(i));
      RETURN(S);
(*4*) END OABS;


PROCEDURE OCON(R: LIST): LIST;
(*Octonion number conjugate.  R is a octonion number. S is the
octonion conjugate of R. *)
VAR   S, r, i: LIST;
BEGIN
(*1*) IF R = 0 THEN RETURN(0) END;
(*2*) FIRST2(R,r,i); 
      S:=COMP2(QCON(r),QNEG(i),SIL);
      RETURN(S);
(*4*) END OCON;


PROCEDURE OCOMP(R,S: LIST): LIST;
(*Octonion number comparison.  R and S are octonion numbers.
t=0 if R=S, t=1 else. *)
VAR   t: LIST;
BEGIN
(*1*) t:=EQUAL(R,S); 
      IF t = 1 THEN t:=0 ELSE t:=1 END;  
      RETURN(t);
(*4*) END OCOMP;


PROCEDURE ODIF(R,S: LIST): LIST;
(*Octonion number difference.  R and S are octonion numbers.  T=R-S. *)
VAR   T, r1, i1, r2, i2: LIST;
BEGIN
(*1*) IF R = 0 THEN T:=ONEG(S); RETURN(T) END;
      IF S = 0 THEN T:=R; RETURN(T) END;
(*2*) FIRST2(R,r1,i1); FIRST2(S,r2,i2); 
      r1:=QDIF(r1,r2); i1:=QDIF(i1,i2); 
      IF (r1 = 0) AND (i1 = 0) THEN T:=0; RETURN(T) END;
      T:=COMP2(r1,i1,SIL);
      RETURN(T);
(*4*) END ODIF;


PROCEDURE ODREAD(): LIST; 
(*Octonion number decimal read.  The octonion number R is read
from the input stream.  Any preceding blanks are skipped. *)
VAR   R, r, i, c: LIST;
BEGIN
(*1*) r:=QDREAD(); i:=0; 
      c:=CREADB(); 
      IF c = MASORD("p") THEN i:=QDREAD(); ELSE BKSP; END;  
      IF (r = 0) AND (i = 0) THEN RETURN(0) END;
      R:=COMP2(r,i,SIL);
      RETURN(R);
(*4*) END ODREAD;


PROCEDURE ODWRITE(R,NL: LIST); 
(*Octonion number decimal write.  R is a octonion number.  n is a
non-negative integer.  R is approximated by a decimal fraction D with
n decimal digits following the decimal point and D is written in the
output stream.  The inaccuracy of the approximation is at most
(1/2)*10**-n. *)
VAR   r, i: LIST;
BEGIN
(*1*) IF R = 0 THEN AWRITE(R); RETURN END;
(*2*) FIRST2(R,r,i); 
      QDWRITE(r,NL); 
      IF i <> 0 THEN SWRITE(" p "); QDWRITE(i,NL) END;  
(*4*) END ODWRITE;


PROCEDURE OEXP(A,NL: LIST): LIST; 
(*Octonion number exponentiation.  A is a octonion number,
n is a non-negative beta-integer.  B=A**n. *)
VAR   B, KL: LIST; 
BEGIN
(*1*) (*n less than or equal to 1.*) 
      IF NL = 0 THEN B:=OINT(1); RETURN(B); END; 
      IF NL = 1 THEN B:=A; RETURN(B); END; 
(*2*) (*recursion.*) KL:=NL DIV 2; B:=OEXP(A,KL); B:=OPROD(B,B); 
      IF NL > 2*KL THEN B:=OPROD(B,A); END; 
      RETURN(B); 
(*5*) END OEXP; 


PROCEDURE OIM(R: LIST): LIST;
(*Octonion number imaginary part p.  R is a octonion number.  b is the
imaginary part p of R, a quaternion number. *)
BEGIN
(*1*) IF R = 0 THEN RETURN(0) END;
(*2*) RETURN(SECOND(R));
(*4*) END OIM;


PROCEDURE OINT(A: LIST): LIST;
(*Octonion number from integer.  A is an integer.  R is the octonion
number with real part equal to A/1 and imaginary part p equal to 0. *)
VAR   R: LIST;
BEGIN
(*1*) IF A = 0 THEN R:=A; RETURN(R) END;
(*2*) R:=COMP2(QINT(A),0,SIL); 
      RETURN(R);
(*4*) END OINT;


PROCEDURE ORE(R: LIST): LIST;
(*Octonion number real part p.  R is a octonion number.  b is the
real part p of R, a quaternion number. *)
BEGIN
(*1*) IF R = 0 THEN RETURN(0) END;
(*2*) RETURN(FIRST(R));
(*4*) END ORE;


PROCEDURE ORN(A: LIST): LIST;
(*Octonion number from rational number.  A is a rational number.  
R is the octonion number with real part equal to A and 
imaginary part p equal to 0. *)
VAR   R: LIST;
BEGIN
(*1*) IF A = 0 THEN RETURN(0) END;
(*2*) R:=COMP2(QRN(A),0,SIL); 
      RETURN(R);
(*4*) END ORN;


PROCEDURE ORNP(A, B: LIST): LIST;
(*Octonion number from pair of quaternion numbers.  A and B are 
quaternion numbers.  R is the octonion number with real part equal 
to A and imaginary part p equal to B. *)
VAR   R: LIST;
BEGIN
(*1*) IF (A = 0) AND (B = 0) THEN RETURN(0) END;
(*2*) R:=COMP2(A,B,SIL); 
      RETURN(R);
(*4*) END ORNP;


PROCEDURE ONINV(R: LIST): LIST;
(*Octonion number inverse.  R is a non-zero octonion number.  S R=1. *)
VAR   S, t, r, i, r1, i1, a: LIST;
BEGIN
(*1*) IF R = 0 THEN ERROR(severe,"OINV: division by zero."); END;
(*2*) a:=OABS(R); S:=OCON(R); 
      FIRST2(S,r,i); r1:=0; i1:=0; 
      IF r <> 0 THEN r1:=SIL; 
         WHILE r <> SIL DO ADV(r,t,r); 
               t:=RNQ(t,a); r1:=COMP(t,r1); END; 
         r1:=INV(r1); END; 
      IF i <> 0 THEN i1:=SIL; 
         WHILE i <> SIL DO ADV(i,t,i); 
               t:=RNQ(t,a); i1:=COMP(t,i1); END; 
         i1:=INV(i1); END; 
      S:=COMP2(r1,i1,SIL); 
      RETURN(S);
(*4*) END ONINV;


PROCEDURE ONEG(R: LIST): LIST;
(*Octonion number negative.  R is a octonion number.  S=-R. *)
VAR   S, r, i: LIST;
BEGIN
(*1*) IF R = 0 THEN RETURN(0) END;
(*2*) FIRST2(R,r,i); 
      S:=COMP2(QNEG(r),QNEG(i),SIL);
      RETURN(S);
(*4*) END ONEG;


PROCEDURE OONE(R: LIST): LIST; 
(*Octonion number one.  R is a octonion number.  s=1 if R=1,
s=0 else. *)
VAR   r, i, t: LIST;
BEGIN
(*1*) IF R = 0 THEN RETURN(0) END;
(*2*) FIRST2(R,r,i); 
      IF i <> 0 THEN RETURN(0) END;
      t:=QONE(r); 
      RETURN(t);
(*4*) END OONE;


PROCEDURE OPROD(R,S: LIST): LIST;
(*Octonion number product.  R and S are octonion numbers.  T=R*S. *)
VAR   T, r1, i1, r2, i2, r, i: LIST;
BEGIN
(*1*) IF (R = 0) OR (S = 0) THEN RETURN(0) END;
(*2*) FIRST2(R,r1,i1); FIRST2(S,r2,i2); 
      r:=QDIF(QPROD(r1,r2),QPROD(QCON(i2),i1)); 
      i:=QSUM(QPROD(i1,QCON(r2)),QPROD(i2,r1)); 
      IF (r = 0) AND (i = 0) THEN RETURN(0) END;
      T:=COMP2(r,i,SIL);
      RETURN(T);
(*4*) END OPROD;


PROCEDURE OQ(R,S: LIST): LIST;
(*Octonion number quotient.  R and S are octonion numbers, S non-zero.
T=R/S. *)
VAR   T: LIST;
BEGIN
(*1*) T:=OPROD(R,ONINV(S)); 
      RETURN(T);
(*4*) END OQ;


PROCEDURE ORAND(NL: LIST): LIST;
(*Octonion number, random.  n is a positive beta-integer.  Random 
quaternion numbers A and B are generated using QRAND(n). Then 
R is the octonion number with real part equal to A and 
imaginary part p equal to B. *)
VAR   T, r, i: LIST;
BEGIN
(*1*) r:=QRAND(NL); i:=QRAND(NL); 
      IF (r = 0) AND (i = 0) THEN RETURN(0) END;
      T:=COMP2(r,i,SIL);
      RETURN(T);
(*4*) END ORAND;


PROCEDURE ONREAD(): LIST;
(*Octonion number read.  The octonion number R is read from the input
stream.  Any preceding blanks are skipped. *)
VAR   R, r, i, c: LIST;
BEGIN
(*1*) r:=QNREAD(); i:=0; 
      c:=CREADB(); 
      IF c = MASORD("p") THEN i:=QNREAD(); ELSE BKSP; END;  
      IF (r = 0) AND (i = 0) THEN RETURN(0) END;
      R:=COMP2(r,i,SIL);
      RETURN(R);
(*4*) END ONREAD;


PROCEDURE OSUM(R,S: LIST): LIST;
(*Octonion number sum.  R and S are octonion numbers.  T=R+S. *)
VAR   T, r1, i1, r2, i2: LIST;
BEGIN
(*1*) IF R = 0 THEN T:=S; RETURN(T) END;
      IF S = 0 THEN T:=R; RETURN(T) END;
(*2*) FIRST2(R,r1,i1); FIRST2(S,r2,i2); 
      r1:=QSUM(r1,r2); i1:=QSUM(i1,i2); 
      IF (r1 = 0) AND (i1 = 0) THEN T:=0; RETURN(T) END;
      T:=COMP2(r1,i1,SIL);
      RETURN(T);
(*4*) END OSUM;


PROCEDURE ONWRITE(R: LIST);
(*Octonion number write. R is a octonion number.  R is converted 
to decimal and written in the output stream. *)
VAR   r, i: LIST;
BEGIN
(*1*) IF R = 0 THEN AWRITE(R); RETURN END;
(*2*) FIRST2(R,r,i); 
      QNWRITE(r);
      IF i <> 0 THEN SWRITE(" p "); QNWRITE(i) END;  
(*4*) END ONWRITE;



END MASO.




(* -EOF- *)