packages feed

language-Modula2-0.1: examples/Modula-2_Libraries/andrea-m2/lib/generic/saynumber.mod

IMPLEMENTATION MODULE SayNumbers;

(* Make a cardinal number into a phrase of words of numbers *)
(* V1.0, J. Andrea, Oct.11/92 *)
(* This code may be freely used and distributed, it may not be sold *)

(*
The term "order" here is used as a power of one thousand, i.e.
0 to 999 is order 0, 1000 to 999999 is order 1, 1000000 is order 2, etc.
*)

FROM MoreMath IMPORT SizeCard;
FROM StringOperations IMPORT Assign, Delete, Append, Insert;

CONST
  longest = 10; (* number of decimal digits in the longest number *)
                (* 5 is good for 16 bits, 9 for 32 bits *)
  max_order = 4; (* the power of 1 thousand for the longest number *)
                 (* 1 is good for 16 bits, 2 for 32 bits *)

VAR
  n_order_values :CARDINAL;
  order_values   :ARRAY [0..max_order] OF CARDINAL;

  size_to_order  :ARRAY [0..longest] OF CARDINAL;

  order_name :ARRAY [0..max_order] OF ARRAY [1..11] OF CHAR;

  digit_name :ARRAY [0..9] OF ARRAY [1..5] OF CHAR;
  teen_name  :ARRAY [0..9] OF ARRAY [1..9] OF CHAR;
  tens_name  :ARRAY [1..9] OF ARRAY [1..7] OF CHAR;

  i :CARDINAL;

  (* ---------------------------------------------------- *)
  PROCEDURE SayInt( x :INTEGER; VAR word :ARRAY OF CHAR );
  BEGIN
    IF x = 0 THEN
      Assign( digit_name[0], word );
    ELSE
      SayCard( CARDINAL( ABS( x ) ), word );
      Insert( 'minus ', 1, word );
    END;
  END SayInt;

  (* ---------------------------------------------------- *)
  PROCEDURE SayCard( x :CARDINAL; VAR word :ARRAY OF CHAR );
  VAR
    top, order_size, order_value :CARDINAL;

    (* -------------------------------------------------- *)
    PROCEDURE Say100( x :CARDINAL );
    VAR
      d :CARDINAL;
    BEGIN
      IF x > 99 THEN
        d := x DIV 100;
        Append( ' ', word );
        Append( digit_name[d], word );
        Append( ' hundred', word );
        x := x - d * 100;
      END;

      IF ( x >= 10 ) & ( x <= 19 ) THEN
        Append( ' ', word ); Append( teen_name[x-10], word );
      ELSE

        IF x > 10 THEN
          d := x DIV 10;
          Append( ' ', word ); Append( tens_name[d], word );
          x := x - d * 10;
        END;

        IF x > 0 THEN
          Append( ' ', word ); Append( digit_name[x], word );
        END;
      END;

    END Say100;

  BEGIN

     IF x = 0 THEN
       Assign( digit_name[0], word );
     ELSE

       word[0] := 0C;

       WHILE x > 0 DO
         order_size := size_to_order[ SizeCard( x ) ];


         WHILE order_size > n_order_values DO
           order_value := order_values[n_order_values];
           n_order_values := n_order_values + 1;
           order_values[n_order_values] := order_value * 1000;
         END;

         order_value := order_values[ order_size ];

         (* get the value which is less than one thousand *)
         top := x DIV order_value;

         Say100( top );

         IF order_size > 0 THEN
           (* and now say order too *)
           Append( ' ', word ); Append( order_name[order_size], word );
         END;

         x := x - top * order_value;
       END;

       WHILE word[0] = ' ' DO
          Delete( word, 1, 1 );
       END;

     END;

  END SayCard;

BEGIN

     Assign( "ones",     order_name[0] );
     Assign( "thousand", order_name[1] );
     Assign( "million",  order_name[2] );
     Assign( "billion",  order_name[3] );
     Assign( "trillion", order_name[4] );
(*
     IF max_order > 4 THEN Assign( "quadrillion", order_name[5] ) END;
     IF max_order > 5 THEN Assign( "quintillion", order_name[6] ) END;
     IF max_order > 6 THEN Assign( "sextillion", order_name[7] ) END;
     IF max_order > 7 THEN Assign( "septillion", order_name[8] ) END;
     IF max_order > 8 THEN Assign( "octillion", order_name[9] ) END;
*)

     Assign( "zero",  digit_name[0] );
     Assign( "one",   digit_name[1] );
     Assign( "two",   digit_name[2] );
     Assign( "three", digit_name[3] );
     Assign( "four",  digit_name[4] );
     Assign( "five",  digit_name[5] );
     Assign( "six",   digit_name[6] );
     Assign( "seven", digit_name[7] );
     Assign( "eight", digit_name[8] );
     Assign( "nine",  digit_name[9] );

     Assign( "ten",       teen_name[0] );
     Assign( "eleven",    teen_name[1] );
     Assign( "twelve",    teen_name[2] );
     Assign( "thirteen",  teen_name[3] );
     Assign( "fourteen",  teen_name[4] );
     Assign( "fifteen",   teen_name[5] );
     Assign( "sixteen",   teen_name[6] );
     Assign( "seventeen", teen_name[7] );
     Assign( "eighteen",  teen_name[8] );
     Assign( "nineteen",  teen_name[9] );

     Assign( "ten",     tens_name[1] );
     Assign( "twenty",  tens_name[2] );
     Assign( "thirty",  tens_name[3] );
     Assign( "fourty",  tens_name[4] );
     Assign( "fifty",   tens_name[5] );
     Assign( "sixty",   tens_name[6] );
     Assign( "seventy", tens_name[7] );
     Assign( "eighty",  tens_name[8] );
     Assign( "ninety",  tens_name[9] );

     (* precompute the order for any length of number *)
     size_to_order[0] := 0;
     FOR i := 1 TO longest DO
        size_to_order[i] := ( i - 1 ) DIV 3;
     END;

     (* and precompute some values of the orders *)
     (* if a nigher level value is needed, it will be computed later *)
     (* only having smal ones here also allows code to be moved *)
     (* from platform to platform with no changes *)
     n_order_values := 1;
     order_values[0] := 1;
     order_values[1] := 1000;

END SayNumbers.