MODULE; BigIntegerFmtLex
Arithmetic for Modula-3, see doc for details
IMPORT Rd, Thread, Fmt AS F, Lex AS L; IMPORT Word, Text; IMPORT BigIntegerRep AS BR, BigInteger AS BB; FROM FmtLexSupport IMPORT Precedence; IMPORT Arithmetic AS Arith;
IMPORT IO;
<* UNUSED *> CONST Module = "BigIntegerFmtLex.";* Format the contents of the number data structure for debug purposes. PROCEDURE Dump (READONLY x: T; ): TEXT = BEGIN RETURN
(
& F.Int(x.size) & ,
& ARRAY BOOLEAN OF TEXT{+
, -
}[x.sign]
& ,
& FastFmtU(x, 16, Word.Size DIV 4) & )
;
END Dump;
PROCEDUREcan be optimized with a division routine that is specialised to small divisorsFastFmtU (READONLY x: T; base: F.Base; pad: [1 .. Word.Size]; ): TEXT = VAR txt: TEXT; BEGIN IF x.size = 0 THEN RETURN "0"; ELSE txt := F.Unsigned(x.data[x.size - 1], base); FOR k := x.size - 2 TO 0 BY -1 DO txt := txt & F.Pad(F.Unsigned(x.data[k], base), pad, '0'); END; RETURN txt; END; END FastFmtU;
PROCEDURESlowFmtU (x: T; base: F.Base; ): TEXT = VAR qr := BB.QuotRem{x, BB.Zero}; b : T; txt := ""; digit: [0 .. LAST(F.Base) - 1]; BEGIN TRY b := BB.FromInteger(base); WHILE NOT BB.IsZero(qr.quot) DO (* IO.Put(Dump(qr.quot) & "\n"); *) qr := BR.DivModU(qr.quot, b); <* ASSERT qr.rem.size <= 1 *> digit := qr.rem.data[0]; IF digit < 10 THEN txt := Text.FromChar(VAL(ORD('0') + digit, CHAR)) & txt; ELSE txt := Text.FromChar(VAL(ORD('a') + digit - 10, CHAR)) & txt; END; END; EXCEPT | Arith.Error (err) => <* ASSERT NOT ISTYPE(err, Arith.ErrorDivisionByZero) *> END; IF Text.Empty(txt) THEN RETURN "0"; ELSE RETURN txt; END; END SlowFmtU; PROCEDUREFmt (READONLY x: T; READONLY style := FmtStyle{}; ): TEXT = VAR txt: TEXT; BEGIN CASE style.base OF | 2 => txt := FastFmtU(x, 2, Word.Size); | 4 => txt := FastFmtU(x, 4, Word.Size DIV 2); | 16 => txt := FastFmtU(x, 16, Word.Size DIV 4); ELSE txt := SlowFmtU(x, style.base); END; IF x.sign THEN RETURN "-" & txt; ELSE RETURN txt; END; END Fmt; PROCEDURETex (x: T; READONLY style := TexStyle{}; <* UNUSED *> within: Precedence; ): TEXT = BEGIN IF style.base = 10 THEN RETURN Fmt(x, FmtStyle{base := style.base}); ELSE RETURN Fmt(x, FmtStyle{base := style.base}) & "_{" & F.Int(style.base) & "}"; END; END Tex; PROCEDURELex (rd: Rd.T; <* UNUSED *> READONLY style: LexStyle; ): T RAISES {L.Error, Rd.Failure, Thread.Alerted} = VAR z := BB.Zero; b := BB.FromInteger(10); neg: BOOLEAN; BEGIN TRY neg := Rd.GetChar(rd) = '-'; IF NOT neg THEN Rd.UnGetChar(rd); END; EXCEPT | Rd.EndOfFile => RAISE L.Error; END; TRY LOOP VAR c := Rd.GetChar(rd); BEGIN IF NOT c IN SET OF CHAR{'0' .. '9'} THEN EXIT END; z := BB.Add(BB.Mul(z, b), BB.FromInteger(ORD(c) - ORD('0'))); END; END; EXCEPT | Rd.EndOfFile => END; IF neg THEN RETURN BB.Neg(z); ELSE RETURN z; END; END Lex; BEGIN END BigIntegerFmtLex.