arithmetic/src/basictypes/physicalunit/PhysicalUnit.m3


MODULE PhysicalUnit;
Arithmetic for Modula-3, see doc for details

IMPORT                           (*IntIntTbl AS Tbl,*)
  Integer32Basic AS I,
  RealBasic      AS R,
  RealTrans      AS RT;

IMPORT Arithmetic AS Arith;

<* UNUSED *>
CONST
  Module = "PhysicalUnit.";

PROCEDURE New (): T =
  BEGIN
    RETURN NEW(T).init(sizeHint := 10);
  END New;

PROCEDURE FromArray (READONLY x: ARRAY OF ExpType; ): T =
  VAR
    y                 := New();
    replaced: BOOLEAN;
  BEGIN
    FOR j := 0 TO LAST(x) DO
      IF x[j] # 0 THEN
        replaced := y.put(j, x[j]);
        <* ASSERT NOT replaced *>
        (* resist writing <*ASSERT NOT y.put(j,x[j])*> because ASSERT
           statements may be removed for final executables *)
      END;
    END;
    RETURN y;
  END FromArray;

PROCEDURE Copy (x: T; ): T =
  VAR
    y                  := New();
    it                 := x.iterate();
    unit, exp: INTEGER;
    replaced : BOOLEAN;
  BEGIN
    WHILE it.next(unit, exp) DO
      replaced := y.put(unit, exp);
      <* ASSERT NOT replaced *>
    END;
    RETURN y;
  END Copy;

PROCEDURE Equal (x, y: T; ): BOOLEAN =
  VAR
    it                        := x.iterate();
    unit, xexp, yexp: ExpType;
  BEGIN
    WHILE it.next(unit, xexp) DO
      IF NOT (y.get(unit, yexp) AND xexp = yexp) THEN RETURN FALSE; END;
    END;
    it := y.iterate();
    WHILE it.next(unit, yexp) DO
      IF NOT x.get(unit, xexp) THEN RETURN FALSE; END;
    END;
    RETURN TRUE;
  END Equal;

PROCEDURE IsZero (x: T; ): BOOLEAN =
  BEGIN
    RETURN x.size() = 0;
  END IsZero;

PROCEDURE Put (x: T; unit: INTEGER; exp: ExpType; ) =
  BEGIN
    IF exp # 0 THEN
      VAR replaced := x.put(unit, exp);
      BEGIN
        <* ASSERT NOT replaced *>
      END;
    END;
  END Put;

PROCEDURE Add (x, y: T; ): T =
  VAR
    z                         := New();
    it                        := x.iterate();
    unit, xexp, yexp: ExpType;
  BEGIN
    WHILE it.next(unit, xexp) DO
      yexp := 0;
      EVAL y.get(unit, yexp);
      Put(z, unit, xexp + yexp);
    END;
    it := y.iterate();
    WHILE it.next(unit, yexp) DO
      IF NOT x.get(unit, xexp) THEN Put(z, unit, yexp); END;
    END;
    RETURN z;
  END Add;

PROCEDURE Sub (x, y: T; ): T =
  VAR
    z                         := New();
    it                        := x.iterate();
    unit, xexp, yexp: ExpType;
  BEGIN
    WHILE it.next(unit, xexp) DO
      yexp := 0;
      EVAL y.get(unit, yexp);
      Put(z, unit, xexp - yexp);
    END;
    it := y.iterate();
    WHILE it.next(unit, yexp) DO
      IF NOT x.get(unit, xexp) THEN Put(z, unit, -yexp); END;
    END;
    RETURN z;
  END Sub;

PROCEDURE Neg (x: T; ): T =
  VAR
    y                  := New();
    it                 := x.iterate();
    unit, exp: ExpType;
    replaced : BOOLEAN;
  BEGIN
    WHILE it.next(unit, exp) DO
      replaced := y.put(unit, -exp);
      <* ASSERT NOT replaced *>
    END;
    RETURN y;
  END Neg;

PROCEDURE Scale (x: T; y: ExpType; ): T =
  VAR
    z                  := New();
    it                 := x.iterate();
    unit, exp: ExpType;
    replaced : BOOLEAN;
  BEGIN
    IF y # 0 THEN
      WHILE it.next(unit, exp) DO
        replaced := z.put(unit, exp * y);
        <* ASSERT NOT replaced *>
      END;
    END;
    RETURN z;
  END Scale;

PROCEDURE ScaleDiv (x: T; y: ExpType; ): T RAISES {Arith.Error} =
  VAR
    z                  := New();
    it                 := x.iterate();
    unit, exp: ExpType;
    replaced : BOOLEAN;
  BEGIN
    WHILE it.next(unit, exp) DO
      replaced := z.put(unit, I.Div(exp, y));
      <* ASSERT NOT replaced *>
    END;
    RETURN z;
  END ScaleDiv;

PROCEDURE ScaleReal (x: T; y: R.T; ): T RAISES {Arith.Error} =
  VAR
    z                         := New();
    it                        := x.iterate();
    unit, xexp, zexp: ExpType;
    zexpr           : R.T;
  BEGIN
    WHILE it.next(unit, xexp) DO
      zexpr := FLOAT(xexp, R.T) * y;
      zexp := ROUND(zexpr);
      IF ABS(FLOAT(zexp, R.T) - zexpr) > RT.Eps THEN
        RAISE Arith.Error(NEW(Arith.ErrorIndivisible).init());
      END;
      Put(z, unit, zexp);
    END;
    RETURN z;
  END ScaleReal;

PROCEDURE Norm1 (x: T; ): ExpType =
  VAR
    it                 := x.iterate();
    unit, exp: ExpType;
    sum      : ExpType := 0;
  BEGIN
    WHILE it.next(unit, exp) DO sum := sum + ABS(exp); END;
    RETURN sum;
  END Norm1;

PROCEDURE NormInf (x: T; ): ExpType =
  VAR
    it                 := x.iterate();
    unit, exp: ExpType;
    max      : ExpType := 0;
  BEGIN
    WHILE it.next(unit, exp) DO max := MAX(max, ABS(exp)); END;
    RETURN max;
  END NormInf;

BEGIN
END PhysicalUnit.