GENERIC INTERFACE VectorN (VRep);
(*
  In-place ("destructive") vector space operations on objects of
  some type "VRep.T".

  Assumes a "VRep.T" object "v" has "Dim(v)" numeric
  coordinates, numbered from 0 to "Dim(v)-1".

  Operations taking two or more "VRep.T" arguments usually
  expect that all have the same number of coordinates. *)

IMPORT Random, Wr, Thread;

TYPE
  T = VRep.T;
  ElemT = VRep.ElemT;
  RealT = ARRAY OF REAL;
  LongRealT = ARRAY OF LONGREAL;
  
PROCEDURE Dim(READONLY a: T): CARDINAL;
  (*
    Number of coordinates in "a". *)

PROCEDURE Fill(VAR a: T; x: LONGREAL);
  (*
    Sets all coordinates of "a" to "x". *)

PROCEDURE Axis(VAR a: T; i: CARDINAL);
  (*
    Set "a" to the "i"-th canonical vector,
    i.e. all zeros, except for a "1" in the "i"-th coordinate.
    Requires "i" in the range "[0..Dim(a)-1]" *)

PROCEDURE Get(READONLY a: T; i: CARDINAL): LONGREAL;
  (*
    Extracts coordinate "i" of "a", converting it to LONGREAL *)
    
PROCEDURE Set(VAR a: T; i: CARDINAL; x: LONGREAL);
  (*
    Sets coordinate "i" of "a" to "x", rounded if necessary. *)

PROCEDURE Add(VAR a: T;  READONLY b: T);
  (*
    Sets "a := a + b" *)
    
PROCEDURE Sub(VAR a: T;  READONLY b: T);
  (*
    Sets "a := a + b" *)

PROCEDURE Neg(VAR a: T);
  (*
    Sets "a := -a" *)
    
PROCEDURE Scale(s: LONGREAL; VAR a: T);
  (*
    Sets "a := s*a"; *)

PROCEDURE Weigh(READONLY w: LongRealT; VAR a: T);
  (*
    Sets "a[i] := w[i] * a[i]" for all "i". *)
    
PROCEDURE Mix(s: LONGREAL; VAR a: T; t: LONGREAL; READONLY b: T);
  (*
    Sets "a := s*a + t*b". *)

PROCEDURE Modify(VAR a: T; s: LONGREAL; READONLY b: T): BOOLEAN;
  (*
    Sets "a := a + s*b"; returns TRUE iff "a" changed. *)
    
PROCEDURE Norm(READONLY a: T): LONGREAL;
  (*
    Returns the Euclidean length (norm) of vector "a". *)
    
PROCEDURE NormSqr(READONLY a: T): LONGREAL;
  (*
    The Euclidean norm of "a", squared. *)
    
PROCEDURE Dir(VAR a: T): LONGREAL;
  (*
    Scales "a" by "1/Norm(a)". Returns the old norm as a result. *)
    
PROCEDURE Dist(READONLY a, b: T): LONGREAL;
  (*
    The Euclidean distance betwen a and b *)
    
PROCEDURE DistSqr(READONLY a, b: T): LONGREAL;
  (*
    The Euclidean distance betwen a and b, squared *)
    
PROCEDURE LInfNorm(READONLY a: T): LONGREAL;
  (*
    Returns the $L_\infinity$ norm (max absolute coordinate) of "a" *)
    
PROCEDURE LInfDist(READONLY a, b: T): LONGREAL;
  (*
    The $L_\infinity$ distance betwen a and b *)
    
PROCEDURE LInfDir(VAR s: T): LONGREAL;
  (*
    Scales "a" by "1/LInfNorm(a)". Returns the old norm as a result. *)

PROCEDURE Dot(READONLY a, b: T): LONGREAL;
  (*
    Returns the dot product of vectors "a" and "b". *)

PROCEDURE Cos(READONLY a, b: T): LONGREAL;
  (*
    Returns the cosine of the angle between "a" and "b". *)

PROCEDURE Sin(READONLY a, b: T): LONGREAL;
  (*
    Returns the (non-negative) sine of the angle between "a" and "b". *)

PROCEDURE Decompose(VAR a, b: T);
  (*
    Decomposes "a" into components "ao" and "ap" that are
    respectively orthogonal and parallel to "b"; then 
    sets "a := ao", "b := ap". *)

PROCEDURE Triangularize(VAR m: ARRAY OF T; total := FALSE);
  (*
    Views the vectors "m[i]" as the rows of a rectangular
    matrix, and reduces "m" to upper triangular form by elementary row
    operations.

    A matrix "m" is {\em upper triangular} if "m[i,j]" is zero
    whenever "i>j".  An {\em elementary row operation} consists of
    adding a multiple of one row to another row; a {\em row swap}
    consists of exchanging two rows and negating one of them.  Observe
    that these operations are equivalent to premultiplying the matrix
    by a square matrix with unit determinant.
    
    If "total" is true, the procedure keeps applying elementary row 
    operations until "m" is {\em totally reduced}, that is,
    each row that is not entirely zero has at least one more leading 
    zero than the preceding row. *)

PROCEDURE ToReal(READONLY a: T; VAR c: RealT);
PROCEDURE ToLongReal(READONLY a: T; VAR c: LongRealT);
  (*
    Convert from "VRep.T" to plain array of "REAL" or "LONGREAL".
    Require "NUMBER(c) = Dim(a)". *)

PROCEDURE FromReal(VAR a: T; READONLY c: RealT);
PROCEDURE FromLongReal(VAR a: T; READONLY c: LongRealT);
  (*
    Convert from plain array of "REAL" or "LONGREAL" to "VRep.T".
    Require "NUMBER(c) = Dim(a)". *)

PROCEDURE URandom(VAR a: T; rnd: Random.T; min, max: LONGREAL);
  (*
    Set the coordinates of "a" to independent random numbers,
    uniformly distributed in "[min __ max)". *)
    
PROCEDURE NRandom(VAR a: T; rnd: Random.T; avg, dev: LONGREAL);
  (*
    Set the coordinates of "a" to independent Gaussian random 
    numbers with mean "avg" and standard deviation "dev". *)

PROCEDURE Print(
    wr: Wr.T;
    READONLY a: T;
    lp := "("; sep := " "; rp := ")";
    fmt: PROCEDURE(x: ElemT): TEXT := VRep.DefaultElemFmt;
  ) RAISES {Wr.Failure, Thread.Alerted};
  (*
    Prints "a" on the given writer, formatting each coordinate
    with "fmt".
    
    The parameters "style", "prec", and "pad" govern the printing of
    each coordinate.  The strings "lp", "sep", and "rp" are printed
    respectively before, between, and after all the coordinates of "a". *)

PROCEDURE ToText(READONLY m: T): TEXT;
  (*
    Formats "m" in some standard format. *)

END VectorN.