MODULE R4 EXPORTS R4, R4Extras;
(*
  Hand-optimized implementation OF the interfaces "R4" and "R4Extras".

  Created by J. Stolfi, with contributions by M. C. Carrard. *)

IMPORT R4Rep, RN, Math, Wr, Thread, Random, GaussRandom;

PROCEDURE Zero(): T =
  BEGIN 
    RETURN T{0.0, ..}
  END Zero;
    
PROCEDURE All(x: LONGREAL): T =
  BEGIN
    WITH xx = FLOAT(x, REAL) DO 
      RETURN T{xx, ..}
    END
  END All;

PROCEDURE Axis(i: [0..N-1]): T =
  VAR r: T := T{0.0, ..};
  BEGIN
    r[i] := 1.0;
    RETURN r
  END Axis;

PROCEDURE Get(READONLY a: T; i: [0..N-1]): LONGREAL =
  BEGIN
    RETURN FLOAT(a[i], LONGREAL)
  END Get;
    
PROCEDURE Set(VAR a: T; i: [0..N-1]; x: LONGREAL) =
  BEGIN
    a[i] := FLOAT(x, ElemT)
  END Set;

PROCEDURE Add(READONLY a, b: T): T =
  BEGIN
    RETURN T{a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]}
  END Add;

PROCEDURE Sub(READONLY a, b: T): T =
  BEGIN
    RETURN T{a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]}
  END Sub;

PROCEDURE Neg(READONLY a: T): T =
  BEGIN
    RETURN T{- a[0], - a[1], - a[2], - a[3]}
  END Neg;

PROCEDURE Scale(s: LONGREAL; READONLY a: T): T = 
  BEGIN
    WITH
      a0 = s * FLOAT(a[0], LONGREAL),
      a1 = s * FLOAT(a[1], LONGREAL),
      a2 = s * FLOAT(a[2], LONGREAL),
      a3 = s * FLOAT(a[3], LONGREAL)
    DO
      RETURN T{FLOAT(a0), FLOAT(a1), FLOAT(a2), FLOAT(a3)}
    END
  END Scale;

PROCEDURE Weigh(READONLY w: LongRealT; READONLY a: T): T =
  BEGIN
    WITH
      c0 = FLOAT(a[0], LONGREAL) * w[0],
      c1 = FLOAT(a[1], LONGREAL) * w[1],
      c2 = FLOAT(a[2], LONGREAL) * w[2],
      c3 = FLOAT(a[3], LONGREAL) * w[3]
    DO
      RETURN T{FLOAT(c0), FLOAT(c1), FLOAT(c2), FLOAT(c3)}
    END
  END Weigh;

PROCEDURE Mix(s: LONGREAL; READONLY a: T; t: LONGREAL; READONLY b: T): T = 
  BEGIN
    WITH
      c0 = s * FLOAT(a[0], LONGREAL) + t * FLOAT(b[0], LONGREAL),
      c1 = s * FLOAT(a[1], LONGREAL) + t * FLOAT(b[1], LONGREAL),
      c2 = s * FLOAT(a[2], LONGREAL) + t * FLOAT(b[2], LONGREAL),
      c3 = s * FLOAT(a[3], LONGREAL) + t * FLOAT(b[3], LONGREAL)
    DO
      RETURN T{FLOAT(c0), FLOAT(c1), FLOAT(c2), FLOAT(c3)}
    END
  END Mix;

PROCEDURE Norm(READONLY a: T): LONGREAL =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),
      (* This can't overflow, and is probably faster than "Math.hypot": *)
      norm = Math.sqrt(a0*a0 + a1*a1 + a2*a2 + a3*a3)
    DO
      RETURN norm
    END
  END Norm;

PROCEDURE NormSqr(READONLY a: T): LONGREAL =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),
      normSqr = a0*a0 + a1*a1 + a2*a2 + a3*a3
    DO
      RETURN normSqr
    END
  END NormSqr;

PROCEDURE Dist(READONLY a, b: T): LONGREAL =
  BEGIN
    WITH
      t0 = FLOAT(a[0], LONGREAL) - FLOAT(b[0], LONGREAL),
      t1 = FLOAT(a[1], LONGREAL) - FLOAT(b[1], LONGREAL),
      t2 = FLOAT(a[2], LONGREAL) - FLOAT(b[2], LONGREAL),
      t3 = FLOAT(a[3], LONGREAL) - FLOAT(b[3], LONGREAL),
      (* This can't overflow, and is probably faster than "Math.hypot": *)
      dist = Math.sqrt(t0*t0 + t1*t1 + t2*t2 + t3*t3)
    DO
      RETURN dist
    END
  END Dist;
    
PROCEDURE DistSqr(READONLY a, b: T): LONGREAL =
  BEGIN
    WITH
      t0 = FLOAT(a[0], LONGREAL) - FLOAT(b[0], LONGREAL),
      t1 = FLOAT(a[1], LONGREAL) - FLOAT(b[1], LONGREAL),
      t2 = FLOAT(a[2], LONGREAL) - FLOAT(b[2], LONGREAL),
      t3 = FLOAT(a[3], LONGREAL) - FLOAT(b[3], LONGREAL),
      distSqr = t0*t0 + t1*t1 + t2*t2 + t3*t3
    DO
      RETURN distSqr
    END
  END DistSqr;

PROCEDURE Dir(READONLY a: T): T =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),
      (* This can't overflow, and is probably faster than "Math.hypot": *)
      norm = Math.sqrt(a0*a0 + a1*a1 + a2*a2 + a3*a3)
    DO
      RETURN T{FLOAT(a0/norm), FLOAT(a1/norm), FLOAT(a2/norm), FLOAT(a3/norm)}
    END
  END Dir; 

PROCEDURE LInfNorm(READONLY a: T): LONGREAL =
  BEGIN
    WITH 
      norm = MAX(MAX(ABS(a[0]), ABS(a[1])), MAX(ABS(a[2]), ABS(a[3])))
    DO
      RETURN FLOAT(norm, LONGREAL)
    END
  END LInfNorm;  

PROCEDURE LInfDist(READONLY a, b: T): LONGREAL =
  BEGIN
    WITH 
      dist = MAX(
        MAX(ABS(a[0]-b[0]), ABS(a[1]-b[1])), 
        MAX(ABS(a[2]-b[2]), ABS(a[3]-b[3]))
      )
    DO
      RETURN FLOAT(dist, LONGREAL)
    END
  END LInfDist;  

PROCEDURE LInfDir(READONLY a: T): T =
  BEGIN
    WITH 
      norm = MAX(MAX(ABS(a[0]), ABS(a[1])), MAX(ABS(a[2]), ABS(a[3])))
    DO
      RETURN T{a[0]/norm, a[1]/norm, a[2]/norm, a[3]/norm}
    END
  END LInfDir;  

PROCEDURE Dot(READONLY a, b: T): LONGREAL =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),

      b0 = FLOAT(b[0], LONGREAL),
      b1 = FLOAT(b[1], LONGREAL),
      b2 = FLOAT(b[2], LONGREAL),
      b3 = FLOAT(b[3], LONGREAL),

      dot = a0*b0 + a1*b1 + a2*b2 + a3*b3
    DO
      RETURN dot
    END
  END Dot;

PROCEDURE Cos(READONLY a, b: T): LONGREAL =
  BEGIN
    RETURN RN.Cos(a, b)
  END Cos;

PROCEDURE Sin(READONLY a, b: T): LONGREAL =
  BEGIN 
    RETURN RN.Sin(a, b)
  END Sin;

PROCEDURE Det(READONLY a, b, c, d: T): LONGREAL =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),
      
      b0 = FLOAT(b[0], LONGREAL),
      b1 = FLOAT(b[1], LONGREAL),
      b2 = FLOAT(b[2], LONGREAL),
      b3 = FLOAT(b[3], LONGREAL),
      
      ab_01 = a0 * b1 - a1 * b0,
      ab_02 = a0 * b2 - a2 * b0,
      ab_12 = a1 * b2 - a2 * b1,
      ab_03 = a0 * b3 - a3 * b0,
      ab_13 = a1 * b3 - a3 * b1,
      ab_23 = a2 * b3 - a3 * b2,

      c0 = FLOAT(c[0], LONGREAL),
      c1 = FLOAT(c[1], LONGREAL),
      c2 = FLOAT(c[2], LONGREAL),
      c3 = FLOAT(c[3], LONGREAL),
      
      d0 = FLOAT(d[0], LONGREAL),
      d1 = FLOAT(d[1], LONGREAL),
      d2 = FLOAT(d[2], LONGREAL),
      d3 = FLOAT(d[3], LONGREAL),
      
      cd_01 = c0 * d1 - c1 * d0,
      cd_02 = c0 * d2 - c2 * d0,
      cd_12 = c1 * d2 - c2 * d1,
      cd_03 = c0 * d3 - c3 * d0,
      cd_13 = c1 * d3 - c3 * d1,
      cd_23 = c2 * d3 - c3 * d2,
      
      d = + ab_01 * cd_23 - ab_02 * cd_13 + ab_12 * cd_03
          + ab_03 * cd_12 - ab_13 * cd_02 + ab_23 * cd_01
    DO
      RETURN d
    END
  END Det;  
  
PROCEDURE Cross(READONLY a, b, c: T): T =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),

      b0 = FLOAT(b[0], LONGREAL),
      b1 = FLOAT(b[1], LONGREAL),
      b2 = FLOAT(b[2], LONGREAL),
      b3 = FLOAT(b[3], LONGREAL),

      ab_01 = a0*b1 - a1*b0,
      ab_02 = a0*b2 - a2*b0,
      ab_12 = a1*b2 - a2*b1,
      ab_03 = a0*b3 - a3*b0,
      ab_13 = a1*b3 - a3*b1,
      ab_23 = a2*b3 - a3*b2,
      
      c0 = FLOAT(c[0], LONGREAL),
      c1 = FLOAT(c[1], LONGREAL),
      c2 = FLOAT(c[2], LONGREAL),
      c3 = FLOAT(c[3], LONGREAL),

      abc_012 = ab_01 * c2 - ab_02 * c1 + ab_12 * c0,
      abc_013 = ab_01 * c3 - ab_03 * c1 + ab_13 * c0,
      abc_023 = ab_02 * c3 - ab_03 * c2 + ab_23 * c0,
      abc_123 = ab_12 * c3 - ab_13 * c2 + ab_23 * c1
    DO
      RETURN T{
        FLOAT(- abc_123),
        FLOAT(+ abc_023),
        FLOAT(- abc_013),
        FLOAT(+ abc_012)
      }
    END;
  END Cross;

PROCEDURE Project(READONLY a, u: T): T =
  BEGIN
    WITH
      a0 = FLOAT(a[0], LONGREAL),
      a1 = FLOAT(a[1], LONGREAL),
      a2 = FLOAT(a[2], LONGREAL),
      a3 = FLOAT(a[3], LONGREAL),

      u0 = FLOAT(u[0], LONGREAL),
      u1 = FLOAT(u[1], LONGREAL),
      u2 = FLOAT(u[2], LONGREAL),
      u3 = FLOAT(u[3], LONGREAL),

      sau = a0*u0 + a1*u1 + a2*u2 + a3*u3
    DO
      IF sau = 0.0d0 THEN
        RETURN T{0.0, 0.0, 0.0, 0.0}
      ELSE
        WITH
          suu = u0*u0 + u1*u1 + u2*u2 + u3*u3,
          c = sau/suu
        DO
          RETURN T{FLOAT(c*u0), FLOAT(c*u1), FLOAT(c*u2), FLOAT(c*u3)}
        END
      END
    END;
  END Project;
  
PROCEDURE Orthize(READONLY a, u: T): T =
  BEGIN
    WITH
      u0 = FLOAT(u[0], LONGREAL),
      u1 = FLOAT(u[1], LONGREAL),
      u2 = FLOAT(u[2], LONGREAL),
      u3 = FLOAT(u[3], LONGREAL),

      suu = u0*u0 + u1*u1 + u2*u2 + u3*u3
    DO
      IF suu = 0.0d0 THEN
        RETURN a
      ELSE
        WITH
          a0 = FLOAT(a[0], LONGREAL),
          a1 = FLOAT(a[1], LONGREAL),
          a2 = FLOAT(a[2], LONGREAL),
          a3 = FLOAT(a[3], LONGREAL),

          sau = a0*u0 + a1*u1 + a2*u2 + a3*u3
        DO
          IF sau = 0.0d0 THEN
            RETURN a
          ELSE
            WITH
              c = sau/suu
            DO
              RETURN T{FLOAT(a0-c*u0), FLOAT(a1-c*u1), FLOAT(a2-c*u2), FLOAT(a3-c*u3)}
            END
          END
        END
      END
    END;
  END Orthize;
  
PROCEDURE ToReal(READONLY a: T): RealT =
  BEGIN RETURN a END ToReal;
  
PROCEDURE FromReal(READONLY a: RealT): T =
  BEGIN RETURN a END FromReal;

PROCEDURE ToLongReal(READONLY a: T): LongRealT =
  BEGIN
    RETURN LongRealT{
      FLOAT(a[0], LONGREAL),
      FLOAT(a[1], LONGREAL),
      FLOAT(a[2], LONGREAL),
      FLOAT(a[3], LONGREAL)
    }
  END ToLongReal;
  
PROCEDURE FromLongReal(READONLY a: LongRealT): T =
  BEGIN
    RETURN T{
      FLOAT(a[0], REAL),
      FLOAT(a[1], REAL),
      FLOAT(a[2], REAL),
      FLOAT(a[3], REAL)
    }
  END FromLongReal;

PROCEDURE URandom(rnd: Random.T; min, max: LONGREAL): T =
  VAR r: T;
  BEGIN
    FOR i := 0 TO N-1 DO
      r[i] := rnd.real(FLOAT(min), FLOAT(max))
    END;
    RETURN r
  END URandom;
    
PROCEDURE NRandom(rnd: Random.T; avg, dev: LONGREAL): T =
  VAR r: T;
  BEGIN
    FOR i := 0 TO N-1 DO
      r[i] := FLOAT(avg + dev * FLOAT(GaussRandom.Real(rnd), LONGREAL))
    END;
    RETURN r
  END NRandom;

PROCEDURE Print(
    wr: Wr.T;
    READONLY a: T; 
    lp := "("; sep := " "; rp := ")";
    fmt: PROCEDURE (x: ElemT): TEXT := R4Rep.DefaultElemFmt;
  ) RAISES {Wr.Failure, Thread.Alerted} =
  BEGIN
    RN.Print(wr, a, lp, sep, rp, fmt)
  END Print;

PROCEDURE ToText(READONLY a: T): TEXT =
  BEGIN
    RETURN RN.ToText(a)
  END ToText;

BEGIN 
END R4.