Thursday, March 14, 2019

WinBatch - Hyper-Extreme Numeric to Literal Translation Overkill

Here's a fun little WinBatch exercise that converts a numeric value to a literal English translation. The GUI is designed to simulate a paycheck. When you enter an amount & press the XLATE button the literal nomenclature is presented. The system date & LDAP name (based on the current userid) is also filled in on the check. I added the verbage "*** For Entertainment ONLY ***" to discourage anyone from printing the simulated check & trying to cash it. You will not get away with it...

The reason this blog post has "Hyper-", "Extreme" & "Overkill" in the title is due to the level of precision to the left of the decimal point. It would be a fair expectation, at least for a check, to translate to millions or even billions... or trillions (to write a check to resolve the national debt). But I figured why stop there... quadrillion, quintillion, sextillion... not even close... How about "Vigintillion"? 10^63 seems extreme enough... at least for now. With hyperinflation looming across the globe - particularly those societies devastated by socialism - I may have to revisit this...

Winbatch provides a slick little GUI Dialog Builder that allows you to design your page - edit/text boxes, buttons, checkboxes, images, etc. Below is the layout of the simulated check.

 
The GUI input is used to formulate the precise code for the page - the code can then be utilized in the main Winbatch program.



Now to test the "Simulated Check". Below I entered a simple amount of 10546.53 - a fairly modest first attempt. The verbage "Ten-Thousand Five-Hundred Forty-Six and 53/100" is displayed.



Time to get a little crazy with testing... $1,989,745,123.02 makes a nice payday!




Here's an extreme version that blows out the text box completely! I prefixed the result on the page with [***TOO BIG***] to indicate that the result is... too big.


An error box is triggered explaining the issue & contains the full literal translation... this example is the maximum translation I accounted for...



The full literal translation is also pasted to the clipboard to use any way you seem fit:

Nine Hundred Ninety-Nine Vigintillion Nine Hundred Ninety-Nine Novemdecillion Nine Hundred Ninety-Nine Octodecillion Nine Hundred Ninety-Nine Septendecillion Nine Hundred Ninety-Nine Sexdecillion Nine Hundred Ninety-Nine Quindecillion Nine Hundred Ninety-Nine Quatuordecillion Nine Hundred Ninety-Nine Tredecillion Nine Hundred Ninety-Nine Duodecillion Nine Hundred Ninety-Nine Undecillion Nine Hundred Ninety-Nine Decillion Nine Hundred Ninety-Nine Nonillion Nine Hundred Ninety-Nine Octillion Nine Hundred Ninety-Nine Septillion Nine Hundred Ninety-Nine Sextillion Nine Hundred Ninety-Nine Quintillion Nine Hundred Ninety-Nine Quadrillion Nine Hundred Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine and 99/100

Here's a glimpse into the WinBatch code - the main routine handles the page input - the entered amount & the button pressed. It then calls my translation function TDFUN_Num_To_Lit().

;**********************************************************************
;*       Display Dialog Box                                           *
;**********************************************************************

IntControl(4,0,0,0,0)

gosub TDInit

sw                        = @YES

while sw                 == @YES           ; CANCEL Button exits loop

   ButtonPushed           = Dialog("TD")

   Select ButtonPushed

      Case 1

         TDlit            = TDFUN_Num_To_Lit(TDpay)
         TDamt            = TDpay
         TDlen            = StrLen(TDlit)

         ClipPut(TDlit)

         if TDlen         > 256
            Message("Literal String Exceeds Panel Capacity (256B)",TDlit)
            TDlit         = StrCat("[***TOO BIG***] ",TDlit)
         endif
         break

      Case 2

         gosub TDhelp
         break

   EndSelect

endwhile

Exit

;**********************************************************************


And here is the TDFUN_Num_To_Lit() function... one of the most fun aspects of programming is dealing with arrays, indexes, pointers, segments & loops. This example has all of that...

;**********************************************************************
;*       NUMBER-TO-LITERAL FUNCTION                                   *
;**********************************************************************
;*                                                                    *
;*        INPUT: I_num      - Numeric Value                           *
;*       FORMAT: O_lit      - Literal Interpretation                  *
;*                                                                    *
;**********************************************************************

#DefineFunction TDFUN_Num_To_Lit(I_num)

O_lit                = ""

if ~ IsDefined(NLmtx)

   NLmtx             = ArrDimension(28)

;  Direct Indexing Literals

   NLmtx[1]          = "One"
   NLmtx[2]          = "Two"
   NLmtx[3]          = "Three"
   NLmtx[4]          = "Four"
   NLmtx[5]          = "Five"
   NLmtx[6]          = "Six"
   NLmtx[7]          = "Seven"
   NLmtx[8]          = "Eight"
   NLmtx[9]          = "Nine"
   NLmtx[10]         = "Ten"
   NLmtx[11]         = "Eleven"
   NLmtx[12]         = "Twelve"
   NLmtx[13]         = "Thirteen"
   NLmtx[14]         = "Fourteen"
   NLmtx[15]         = "Fifteen"
   NLmtx[16]         = "Sixteen"
   NLmtx[17]         = "Seventeen"
   NLmtx[18]         = "Eighteen"
   NLmtx[19]         = "Nineteen"
   NLmtx[20]         = "Twenty"

;  Combination Indexing Literals
   NLmtx[21]         = "Thirty"
   NLmtx[22]         = "Forty"
   NLmtx[23]         = "Fifty"
   NLmtx[24]         = "Sixty"
   NLmtx[25]         = "Seventy"
   NLmtx[26]         = "Eighty"
   NLmtx[27]         = "Ninety"

   NLmax             = 27

endif

;   Set Level Parameters (Element String, Length, Occurs)
L_str                = ""
L_str                = StrCat(L_str,"~Vigintillion~$$$$$$")  ; 10^63
L_str                = StrCat(L_str,"~Novemdecillion~$$$$")  ; 10^60
L_str                = StrCat(L_str,"~Octodecillion~$$$$$")  ; 10^57
L_str                = StrCat(L_str,"~Septendecillion~$$$")  ; 10^54
L_str                = StrCat(L_str,"~Sexdecillion~$$$$$$")  ; 10^51
L_str                = StrCat(L_str,"~Quindecillion~$$$$$")  ; 10^48
L_str                = StrCat(L_str,"~Quatuordecillion~$$")  ; 10^45
L_str                = StrCat(L_str,"~Tredecillion~$$$$$$")  ; 10^42
L_str                = StrCat(L_str,"~Duodecillion~$$$$$$")  ; 10^39
L_str                = StrCat(L_str,"~Undecillion~$$$$$$$")  ; 10^36
L_str                = StrCat(L_str,"~Decillion~$$$$$$$$$")  ; 10^33
L_str                = StrCat(L_str,"~Nonillion~$$$$$$$$$")  ; 10^30
L_str                = StrCat(L_str,"~Octillion~$$$$$$$$$")  ; 10^27
L_str                = StrCat(L_str,"~Septillion~$$$$$$$$")  ; 10^24
L_str                = StrCat(L_str,"~Sextillion~$$$$$$$$")  ; 10^21
L_str                = StrCat(L_str,"~Quintillion~$$$$$$$")  ; 10^18
L_str                = StrCat(L_str,"~Quadrillion~$$$$$$$")  ; 10^15
L_str                = StrCat(L_str,"~Trillion~$$$$$$$$$$")  ; 10^12
L_str                = StrCat(L_str,"~Billion~$$$$$$$$$$$")  ; 10^9
L_str                = StrCat(L_str,"~Million~$$$$$$$$$$$")  ; 10^6
L_str                = StrCat(L_str,"~Thousand~$$$$$$$$$$")  ; 10^3
L_str                = StrCat(L_str,"$$$$$$$$$$$$$$$$$$$$")  ; Hundred
L_str                = StrCat(L_str,"$$$$$$$$$$$$$$$$$$$$")  ; Cents

L_str                = StrReplace(L_str, "$", " ")
L_pos                = 1
L_len                = 20
L_occ                = StrLen(L_str) / L_len

L_tot                = (L_occ * 3)
L_int                = (L_occ * 3)   - 3
L_dec                = (L_occ * 3)   - 1

;   Format Numeric String
ptr                  = StrIndex(I_num,".",1,@FWDSCAN)
if ptr              == 0
   I_num             = StrCat(I_num, ".")
   ptr               = StrLen(I_num)
endif
I_num                = StrCat(I_num,"00")
ptr                  = ptr + 2
I_num                = StrSub(I_num, 1, ptr)
I_num                = StrFixLeft(I_num, "0", L_tot)
W_num                = I_num
W_int                = StrSub(I_num,    1, L_int)
W_dec                = StrSub(I_num,L_dec,     2)

pos                  = 1
lev                  = 1

O_lit                = ""

while lev           <= (L_occ - 1)

   seg               = StrSub(W_num, pos, 3)

   if  seg           > '000'

       str           = StrTrim(StrSub(L_str, L_pos, L_len))
       str           = StrReplace(str, "~", " ")

       segHi         = StrSub(seg, 1, 1)
       segLo         = StrSub(seg, 2, 2)

       hi            = %segHi%
       lo            = %segLo%

       if  hi        > 0
           NLtxt     = NLmtx[hi]
           O_lit     = StrCat(O_lit, NLtxt, " Hundred")
           if  lo    > 0
               O_lit = StrCat(O_lit," ")
           endif
       endif

       ;   Combination Indexing
       if  lo        > 20
           adj       = Abs(lo/10) + 18
           NLtxt     = NLmtx[adj]
           O_lit     = StrCat(O_lit, NLtxt)
           lo        = lo mod 10
           if lo     > 0
              O_lit  = StrCat(O_lit,"-")
           endif
       endif

       ;   Direct Indexing
       if  lo        > 0
           NLtxt     = NLmtx[lo]
           O_lit     = StrCat(O_lit,NLtxt)
       endif

       O_lit         = StrCat(O_lit,str)

   endif

   lev               = lev   + 1
   pos               = pos   + 3
   L_pos             = L_pos + L_len

endwhile

if O_lit            == ""
   O_lit             = "Zero"
endif

;   Now process the cents portion
if  W_dec           <> '00'
    O_lit            = StrCat(O_lit," and ",W_dec, "/100")
endif

Return O_lit

#EndFunction

;**********************************************************************


I hope you enjoyed this post... and once again, please do not try to cash the simulated check output! This is your final warning...