Announcement

Collapse
No announcement yet.

Silly programming contest here at work.

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Silly programming contest here at work.

    We must be bored.....

    Write a function to write out a number in words using the least code.
    Rules:
    1, Pick a language.
    2, This is not an obfuscated code contest :P
    3, um....?

    So far I am ahead at 48 lines writing in Oracle PL/SQL:
    Output at bottom of post.

    Code:
    CREATE OR REPLACE function TAX.write_num(v_arg in number) return char
    as
    v_in   varchar2(256) := ltrim(to_char(v_arg, '99999999999999999'));
    v_out  varchar2(256) := '';
    v_curr varchar2(256);
    v_next varchar2(256);
    v_big  varchar2(256) := '';
    v_tail number := length(v_in);
    begin
      if v_tail > 0 then
         if length(v_in) > 12 then v_big := 'TRILLION ';
         elsif length(v_in) > 9 then v_big := 'BILLON ';
         elsif length(v_in) > 6 then v_big := 'MILLION ';
         elsif length(v_in) > 3 then v_big := 'THOUSAND ';
         end if;
         while length(substr(v_in,1, v_tail)) > 3 loop v_tail := v_tail - 3; end loop;
         v_curr := ltrim(to_char(to_number(substr(v_in, 1, v_tail)), '900'));
         v_next := tax.write_num(substr(v_in, v_tail + 1));
         if length(v_curr) = 3 then v_out := tax.write_num(substr(v_curr, 1, 1)) || 'HUNDRED ' || tax.write_num(substr(v_curr, 2, 2));
         elsif v_curr = '00' then v_out := 'ZERO';
         elsif v_curr = '01' then v_out := 'ONE ';
         elsif v_curr = '02' then v_out := 'TWO ';
         elsif v_curr = '03' then v_out := 'THREE ';
         elsif v_curr = '04' then v_out := 'FOUR ';
         elsif v_curr = '05' then v_out := 'FIVE ';
         elsif v_curr = '06' then v_out := 'SIX ';
         elsif v_curr = '07' then v_out := 'SEVEN ';
         elsif v_curr = '08' then v_out := 'EIGHT ';
         elsif v_curr = '09' then v_out := 'NINE ';
         elsif v_curr = '10' then v_out := 'TEN ';
         elsif v_curr = '11' then v_out := 'ELEVEN ';
         elsif v_curr = '12' then v_out := 'TWELVE ';
         elsif substr(v_curr, 1, 1) = '1' then v_out := rtrim(replace(replace(tax.write_num(substr(v_curr, 2, 1)), 'VE', 'F'),'REE','IR')) || 'TEEN ';
         elsif substr(v_curr, 1, 1) = '2' then v_out := 'TWENTY '  || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '3' then v_out := 'THIRTY '  || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '4' then v_out := 'FOURTY '  || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '5' then v_out := 'FIFTY '   || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '6' then v_out := 'SIXTY '   || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '7' then v_out := 'SEVENTY ' || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '8' then v_out := 'EIGHTY '  || tax.write_num(substr(v_curr, 2, 2));
         elsif substr(v_curr, 1, 1) = '9' then v_out := 'NINETY '  || tax.write_num(substr(v_curr, 2, 2));
         end if;
         v_out := v_out || v_big || v_next;
         if substr(v_out, 1, 4) <> 'ZERO' then v_out := replace(v_out, 'ZERO', ''); end if;
         return v_out;
      end if;
      return '';
    end;
    output:
    Code:
    SQL>  select write_num(98765432115)
      2   from dual;
    
    WRITE_NUM(98765432115)
    --------------------------------------------------------------------------------
    NINETY EIGHT BILLON SEVEN HUNDRED SIXTY FIVE MILLION FOUR HUNDRED THIRTY TWO THOUSAND ONE HUNDRED FIFTEEN
    
    SQL> select write_num(13000915)
      2  from dual;
    
    WRITE_NUM(13000915)
    ------------------------------------------
    THIRTEEN MILLION NINE HUNDRED FIFTEEN
    
    SQL>
    If I have time I'll translate it into c at home.

    chuck
    Last edited by cjolley; 22 October 2001, 13:51.
    Chuck
    秋音的爸爸

  • #2
    I would probably try Perl (don't have the time to code it right now).

    One thing (don't know sql) I would do in Perl is use an array, and index using the number, rather than all of those elsifs for v_out.
    Gigabyte P35-DS3L with a Q6600, 2GB Kingston HyperX (after *3* bad pairs of Crucial Ballistix 1066), Galaxy 8800GT 512MB, SB X-Fi, some drives, and a Dell 2005fpw. Running WinXP.

    Comment


    • #3
      Heh, got out another line. (case of 13)

      This doesn't contain any actual SQL;
      More like an ORACLE version of c. (with := assignments like pascal )
      chuck
      Chuck
      秋音的爸爸

      Comment


      • #4
        Code:
        #!/usr/bin/perl
         
        @below20   = split (/,/,',one,two,three,four,five,six,seven,eight,nine,ten,'
                               .'eleven,twelve,thirteen,fourteen,fifteen,sixteen,'
                               .'seventeen,eighteen,nineteen');
        @tens      = split (/,/,',,twenty,thirty,forty,fifty,sixty,seventy,eighty,ninety');
        @magnitude = split (/,/,',thousand,million,billion,trillion');
         
        $revnumber = reverse shift;
        die "Number too large. I can only count up to\n". `$0 999999999999999` ."Sorry 'bout that.\n"
          if length $revnumber > 15;
         
        while ( $revnumber =~ /(.)(.?)(.?)/g ) {
          ($h,$t,$s,$m) = ('') x 4;
          $h = "$below20[$3] hundred" if $3;
          if ( $2 && $2 >= 2 ) {
            $t = $tens[$2];
            $s = $below20[$1];
          } else {
            $s = $below20["$2$1"];
          }
          $mag = shift @magnitude;
          $m = $mag if "$h$t$s";
          $word = join " ", grep { $_ } ($h,$t,$s,$m,$word);
        }
        print "$word\n";
        I haven't tested extensively, and maybe I've cut a few corners too many. To make it a little smaller, I've also disabled "strict" and "warnings". Given the appropriate "my" declarations however, the code will run under strict and warnings. Input checking is virtually non-existant.

        Some sample output:
        Code:
        $ ./num2words 98765432115
        ninety eight billion seven hundred sixty five million four hundred thirty two thousand one hundred fifteen
        $ ./num2words 13000915
        thirteen million nine hundred fifteen
        $ ./num2words 123456789012345
        one hundred twenty three trillion four hundred fifty six billion seven hundred eighty nine million twelve thousand three hundred forty five
        $ ./num2words 1111111111111111
        Number too large. I can only count up to
        nine hundred ninety nine trillion nine hundred ninety nine billion nine hundred ninety nine million nine hundred ninety nine thousand nine hundred ninety nine
        Sorry 'bout that.
        $
        Martin

        Comment


        • #5
          I like it!
          Now if I can just figure out how to get my number words into an array....
          Oracles seems to change the way they declare and handle arrays with each version of the database engine.
          I never use them in my work code, so I'm researching.
          chuck
          Last edited by cjolley; 23 October 2001, 06:23.
          Chuck
          秋音的爸爸

          Comment


          • #6
            I'd bet you won't make it that short for Russian numbers.

            Oh my! In this country it's usually if not the largest then definitely most boring piece of code in any financial program one has to write ... ;(
            Computer Revolution makes it possible to substitute educated slaves for ignorant ones. (V.I.Arnold)

            Comment


            • #7
              Forgot to add:

              The following will probably qualify as "cheating", but...

              Code:
              $ perl -MLingua::EN::Numbers -e 'print Lingua::EN::Numbers->new(shift)->get_string()' 98765432115
              Ninety-Eight Billion, Seven-Hundred Sixty-Five Million, Four-Hundred Thirty-Two Thousand, One-Hundred Fifteen
              And BTW, I just realised that the code in my post above doesn't handle "zero" . I'll leave that as an exercise to the reader

              Martin

              Comment


              • #8
                Originally posted by Ees
                ...I'll leave that as an exercise to the reader
                Good one!

                chuck

                PS And yes, that would be considered cheating
                Last edited by cjolley; 23 October 2001, 06:28.
                Chuck
                秋音的爸爸

                Comment


                • #9
                  Swtched to an Oracle 8i database & gained the ability to use arrays..

                  Code:
                  CREATE OR REPLACE function TAX.write_num_new(v_arg in number) return char
                  as
                  TYPE list is VARRAY(32) of varchar2(16);
                  a_big  list := list('', 'THOUSAND ','MILLION ','BILLON ','TRILLION ','ZILLION', ':)');
                  a_ones list := list('ZERO','ONE ','TWO ','THREE ','FOUR ','FIVE ','SIX ','SEVEN ','EIGHT ','NINE ','TEN ','ELEVEN ','TWELVE ');
                  a_tens list := list('','TWENTY ','THIRTY ','FOURTY ','FIFTY ','SIXTY ','SEVENTY ','EIGHTY ','NINETY ');
                  v_in   varchar2(256) := ltrim(to_char(v_arg, '99999999999999999'));
                  v_curr varchar2(256) := ltrim(to_char(to_number(substr(ltrim(to_char(v_arg, '99999999999999999')), 1, mod(length(ltrim(to_char(nvl(v_arg, 0), '99999999999999999'))), 3))), '900'));
                  v_tail number := mod(length(ltrim(to_char(nvl(v_arg, 0), '99999999999999999'))), 3);
                  v_big  varchar2(256) := a_big( nvl(trunc((length(ltrim(to_char(v_arg, '99999999999999999'))) + 2)/ 3, 0), 1)) ;
                  v_out  varchar2(256) := '';
                  begin
                    if v_arg is not null then
                       if v_tail = 3 then v_out := tax.write_num(substr(v_curr, 1, 1)) || 'HUNDRED ' || tax.write_num(substr(v_curr, 2, 2));
                       elsif v_curr between '00' and '12' then v_out := a_ones(to_number(v_curr));
                       elsif v_curr between '13' and '19' then v_out := rtrim(replace(replace(tax.write_num(substr(v_curr, 2, 1)), 'VE', 'F'),'REE','IR')) || 'TEEN ';
                       else  v_out := a_tens(to_number(substr(v_curr, 1, 1))) || tax.write_num(substr(v_curr, 2, 2));
                       end if;
                       v_out := v_out || v_big || tax.write_num(substr(v_in, v_tail + 1));
                       return v_out;
                    end if;
                    return '';
                  end;
                  One guy got it down to 8 lines of c, but I think it has bugs.
                  chuck
                  Chuck
                  秋音的爸爸

                  Comment


                  • #10
                    I got it down to this:

                    Code:
                    #!/usr/bin/perl
                     
                    @below20   = ('',qw/one two three four five six seven eight nine ten eleven twelve/,
                                   map { $_."teen" } qw/thir four fif six seven eigh nine/);
                    @tens      = ('','', map {$_."ty"} qw/twen thir for fif six seven eigh nine/);
                    @mag       = ('',qw/thousand million billion trillion/);
                     
                    die "Too large. I can only count up to:\n". `$0 999999999999999` if length ($num=shift) > 15;
                    if ($num=~/\D/ || ($num = reverse sprintf "%015s",$num)==0) { print "zero\n"; exit }
                     
                    while ( $num =~ /(.)(.)(.)/g ) {
                      $h = $3 ? "$below20[$3] hundred" : "";
                      $t = $2 > 1 ? $tens[$2] : "";
                      $s = $t ? $below20[$1] : $below20[$2.$1];
                      $word = join " ", grep { $_ } ($h,$t,$s,$mag[$m],$word) if "$h$t$s"; $m++;
                    }
                    print "$word\n";
                    738 bytes, and I haven't really squeezed it yet. Number of lines is meaningless IMHO: I could trivially make this a one-liner.

                    How do I rank?
                    Martin

                    Comment


                    • #11
                      Hehe, just realised that I needed the zero-padding in another version, but not this one:

                      Code:
                      #!/usr/bin/perl
                       
                      @below20   = ('',qw/one two three four five six seven eight nine ten eleven twelve/,
                                     map { $_."teen" } qw/thir four fif six seven eigh nine/);
                      @tens      = ('','', map {$_."ty"} qw/twen thir for fif six seven eigh nine/);
                      @mag       = ('',qw/thousand million billion trillion/);
                       
                      die "Too large. I can only count up to:\n". `$0 999999999999999` if length ($num= reverse shift) > 15;
                      if ($num=~/\D/ || $num==0) { print "zero\n"; exit }
                       
                      while ( $num =~ /(.)(.?)(.?)/g ) {
                        $h = $3 ? "$below20[$3] hundred" : "";
                        $t = $2 > 1 ? $tens[$2] : "";
                        $s = $t ? $below20[$1] : $below20[$2.$1];
                        $word = join " ", grep { $_ } ($h,$t,$s,$mag[$m],$word) if "$h$t$s"; $m++;
                      }
                      print "$word\n";
                      716 bytes

                      Comment


                      • #12
                        Wow,
                        Looks like perl is the way to go with this one.
                        I wish I knew more about it.
                        Some of it's features look very interesting.


                        We had already desided that we would use statement terminators instead of lines.
                        I had to agree not to do it in pure SQL, It would be very wordy but be only one statement by definition.

                        Funny how everyones versions are converging on the same decision tree, with differences only in language.

                        chuck
                        Chuck
                        秋音的爸爸

                        Comment

                        Working...
                        X