Menu

Where is your phone number in Pi?

Programs
Wanderer
2015-04-12
2015-07-28
  • Wanderer

    Wanderer - 2015-04-12

    So here is my first X11-Basic program which I got to work (more or less) as it should. I am not yet quite happy with it because I had to use workarounds for several bugs or missing features in X11-Basic, but I arrived finally at a working version.
    This program calculates Pi, e, and square roots, to as many digits as you wish (if you have time to wait...), and lets you search for any numeric string - for example your phone number - within them.

    (Note: The next post contains the program as an attached *.bas file. I did not find any option for attaching files to the first post of a thread.)

    ' "Where is your phone number in Pi?"
    ' -----------------------------------
    ' Programming Language: X11-Basic
    '
    ' Demonstrates some of the "big integer" capacities of X11-Basic,
    ' applied also to fractional numbers.
    ' May be used and extended freely.
    '
    ' This program lets you search for your phone number (or any other numeric
    ' string) in as many digits of Pi as you wish.
    ' Instead of Pi you can also calculate and search e, the square root of 2,
    ' or (almost) any other square root.
    '
    ' The program writes every number it calculates to a text file,
    ' so you can later open or edit it in a text editor. 
    ' The names of these files are "bn_pi.txt", "bn_e.txt", and "bn_sqrt2.txt".
    ' These are initialized to 10000 digits the first time the program is run.
    ' Then they can be extended by means of the "Recalculate" option. 
    ' The files for other square roots are accordingly called e.g. "bn_sqrt7" for
    ' the square root of 7, etc.
    '
    ' IMPORTANT: This program works only when compiled to bytecode,
    ' but NOT in the X11-Basic interpreter (at least not in the Windows version), 
    ' because of several bugs in the interpreter.
    '
    ' Also, if you wish to calculate millions of digits, the interpreter
    ' would be far too slow. Remember that you would have to calculate about
    ' ten million digits if you want to have a reasonable chance to find a 
    ' seven-digit phone number within them.
    ' On a Pentium IV 2.66 GHz running Windows XP, calculating 100,000 digits of Pi
    ' took 37 seconds for the bytecode file. (Most of this time was used for conver-
    ' ting the calculated number into a string.) Since the computing time increases
    ' approximately proportional to the square of the number of digits, calculating
    ' ten million digits would take approximately 100 hours.
    '
    ' Note: The "Blink" mechanism for the wait screen could be implemented in a
    ' more elegant way using EVERY, but I found this feature to be still unreliable.
    '
    ' - Some PRINT statements which give feedback about program progress are left
    ' in the code; these might be annoying when running under Android where console
    ' and graphics screen are identical, so just comment them out.
    
    If android?
      baseDir$="/mnt/sdcard/bas/"
    Else
      baseDir$=""
    EndIf
    
    Dim fixedNum$(5)
    'Dim filename$() = [" ", "bn_pi.txt", "bn_e.txt", "bn_sqrt2.txt", " "]
    'Dim numberName$() = [" ", "Pi", "e", "Sqrt(2)", " "]
    'Above lines were rejected by the compiler/interpreter.
    Dim filename$(5)
    filename$(1) = "bn_pi.txt"
    filename$(2) = "bn_e.txt"
    filename$(3) = "bn_sqrt2.txt"
    Dim numberName$(5)
    numberName$(1) = "Pi"
    numberName$(2) = "e"
    numberName$(3) = "Sqrt(2)"
    searchNumber$ = "       234"
    squareNumber$ = "        3"
    
    waitX1%=0
    waitY1%=0
    scrsave$=" "
    
    openw 1
    get_geometry 1, scrx%, scry%, scrw%, scrh%
    textW = max(min(min(scrw%,scrh%)/30, 16),8)
    linelength% = Int(scrw%/textW)
    Color color_rgb(0,0,0)
    pbox 0,0, scrw%-1, scrh%-1
    Dim WaitText$(2)
    WaitText$(0) = "Initializing Numbers"
    WaitText$(1) = "Please Wait"
    ret = @OpenWaitScreen(WaitText$())
    For i% = 1 To 3
      If exist(baseDir$+filename$(i%))    !Loads Pi, e, and sqrt(2) from existing files.
        fixedNum$(i%) = @MyBLoad$(baseDir$+filename$(i%))
      Else     !Initialize Pi, e, and sqrt(2) to 10000 digits.
        Select i%
        Case 1
          fixedNum$(1) = @PiString$(10000)
        Case 2
          fixedNum$(2) = @EString$(10000)
        Case 3
          fixedNum$(3) = @SqrtString$(2,10000)
        endselect
        ret% = @myBSave%(baseDir$+filename$(i%), fixedNum$(i%))
      EndIf  
    Next i%
    ret = @CloseWaitScreen()
    Dim waitText$(3)
    
    'Start of main loop:
    Do
    startAgain:
      'Dialog for choosing number:
      '===========================
      txt$ = "Choose a number where to search:"
      btns$ = "Pi|e|sqrt(2)|sqrt(n)|EXIT"
      alert 0, txt$, 0, btns$, selection%
      Exit If selection%=5
      If selection%=4    !Arbitrary square root
                         !=====================
        txt$ = "Search square root of: "+Chr$(27)+squareNumber$
        alert 0, txt$, 0, "OK|Cancel", sele%, reTxt$
        If sele%=2
          GoTo startAgain
        EndIf
        squareNumber$ = word$(reTxt$,1,Chr$(13))
        sqN% = Val(Trim$(squareNumber$))
        If sqN%<0 
          sqN%=0
        EndIf
        filename$(4)="bn_sqrt"+Str$(sqN%)+".txt"
        numberName$(4)="Sqrt("+Str$(sqN%)+")"
        If exist(baseDir$+filename$(4))
          fixedNum$(4) = @MyBLoad$(baseDir$+filename$(4))
        Else     !Initialize sqrt(sqN%) to 10000 digits.
          WaitText$(0) = "Recalculating "+numberName$(selection%)
          WaitText$(1) = "to 10000 digits."
          WaitText$(2) = "Please wait."
          ret = @OpenWaitScreen(WaitText$())
          fixedNum$(4) = @SqrtString$(sqN%,10000)
          ret = @CloseWaitScreen()
          ret% = @myBSave%(baseDir$+filename$(4), fixedNum$(4))
        EndIf  
      EndIf
      'Search/Recalculate Dialog:
      '==========================
      digits% = Len(fixedNum$(selection%))-InStr(fixedNum$(selection%),".")
      txt$ = numberName$(selection%) + " is calculated with "
      txt$ = txt$+Chr$(27)+Str$(digits%)+"     |"
      txt$ = txt$+"digits. (Edit to recalculate.)|Input your phone number|or any other number|(without spaces):"+Chr$(27)+searchNumber$
      btns$ = "Search|Recalculate|Cancel"
      alert 0, txt$, 0, btns$, sele2%, reTxt$
          'Print selection%; " - "; sele2%; " |"; reTxt$; "|"
      newDigits% = Val(Trim$(word$(reTxt$, 1, Chr$(13))))
      searchNumber$ = word$(reTxt$, 2, Chr$(13))
      'Note: we should not TRIM searchNumber$ itself,
      ' because we use it as the new default text in the next ALERT,
      ' and ALERT does not allow to input a string longer than the default text.
      Select sele2%
      Case 1              !Search for a number
                          !===================
        posi% = InStr(fixedNum$(selection%), Trim$(searchNumber$))
        Color color_rgb(1,1,0.4)
        If posi%
          ltext 0,0, "Found "+Trim$(searchNumber$)+" in "+numberName$(selection%)
          ltext 0,textW*2, "at position "+Str$(posi%-2)+":"
          ret = @PrintFoundNumber(fixedNum$(selection%))
             'Print "Found at "; posi%-2
        Else
          ltext 0,0, Trim$(searchNumber$)+" was not found in "+Str$(Len(fixedNum$(selection%))-2)+" digits"
          ltext 0,textW*2, "of "+numberName$(selection%)+"."
              'Print "Not found"
        EndIf
        ltext 0,textW*16, "Press any key"
        ltext 0,textW*18, "or click mouse to continue."
        showpage
        x$ = @WaitMouseOrKey$()
        Color color_rgb(0,0,0)
        pbox 0,0, scrw%-1,scrh%-1
        showpage
      Case 2              !Recalculate a number
                          !====================
        If newDigits%<=digits%
          txt$ = "New number of digits is not greater|than the current one.|"
          txt$ = txt$ + "Recalculating makes no sense."
          alert 1, txt$, 1, "OK", s%
          GoTo startAgain
        EndIf
        digits% = newDigits%
        WaitText$(0) = "Recalculating "+numberName$(selection%)
        WaitText$(1) = "to "+Str$(digits%)+" digits."
        WaitText$(2) = "Please wait."
        ret = @OpenWaitScreen(WaitText$())
          tt = Timer
        Select selection%
        Case 1
          fixedNum$(1) = @PiString$(digits%)
        Case 2
          fixedNum$(2) = @EString$(digits%)
        Case 3
          fixedNum$(3) = @SqrtString$(2, digits%)
        Case 4
          fixedNum$(4) = @SqrtString$(sqN%, digits%)
        EndSelect
          Print "Calculation "; selection%; " = "; Timer-tt; " seconds."
        ret% = @myBSave%(baseDir$+filename$(selection%), fixedNum$(selection%))
        ret = @CloseWaitScreen()
      Case 3
        'Cancelled (nothing to do)
      endselect
    Loop
    End
    
    Function PrintFoundNumber(nString$)
      'Following is a workaround for printing text with background color,
      ' which does not yet work properly.
      Local startString%, startX%, 
      startString% = max(posi%-(lineLength%-Len(Trim$(searchNumber$)))/2, 1)
      startX% = ltextlen(Mid$(nString$, startString%, posi%-startString%))
      Color color_rgb(1,0,0)
      pbox startX%, textW*4, startX%+ltextlen(Trim$(searchNumber$)), textW*6
      Color color_rgb(1,1,0.4)
      ltext 0, textW*4, Mid$(nString$, startString%, lineLength%)
      Return 0
    EndFunction
    
    Function MyBLoad$(file$)
      'Workaround because of a bug in the Windows version.
      Local f%, temp$, bytes%
      If exist(file$)
        bytes% = size(file$)
        temp$ = Space$(bytes%)
        f%=FreeFile()
        Open "I", f%, file$
        bget f%, VarPtr(temp$), bytes%
        Close f%
        Return temp$
      Else
        Return ""
      EndIf 
    EndFunction
    
    Function MyBSave%(file$, s$)
      'Workaround because of a bug in the Windows version.
      Local f%, bytes%
      bytes% = Len(s$)
      f%=FreeFile()
      Open "O", f%, file$
      bput f%, VarPtr(s$), bytes%
      Close f%
      Return bytes%
    EndFunction
    
    Function PiString$(digits%)
      Local expon%, myPi&, denom&
      expon% = (digits%+26)/9    !Use more digits because of rounding errors.
      Locate 1,1
      Print "Preparing denominator..."
      denom& = @bn_power&(1000000000, expon%)
      Locate 1,1
      Print "Calculating Pi...       "
      myPi& = @bnx_pi&(denom&)
      ret = @BlinkWaitScreen(waitText$())
      Locate 1,1
      Print "Generating string...    "
      Return Left$(@bnx_radixPower$(myPi&, 9*expon%, 10), digits%+2)
    EndFunction
    
    Function EString$(digits%)
      Local expon%, denom&, e&
      expon% = (digits%+26)/9
      Locate 1,1
      Print "Preparing denominator..."
      denom& = @bn_power&(1000000000, expon%)
      Locate 1,1
      Print "Calculating e...        "
      e& = @bnx_e&(denom&)
      Locate 1,1
      Print "Generating string...    "
      Return Left$(@bnx_radixPower$(e&, 9*expon%, 10), digits%+2)
    EndFunction
    
    Function SqrtString$(n%, digits%)
      Local expon%, denom&, root&
      expon% = (digits%+17)/9
      denom& = @bn_power&(1000000000, expon%)
      Locate 1,1
      Print "Calculating square root..."
      root& = sqrt(n%*denom&*denom&)
      Locate 1,1
      Print "Generating string...      "
      Return Left$(@bnx_radixPower$(root&, 9*expon%, 10), digits%+4)
    EndFunction
    
    Function bnx_pi&(denom&)
        'Calculates Pi (Gauss-Legendre Algorithm), returns numerator.
        Local a&, olda&, b&, t&, x&
        a& = denom&
        b& = sqrt(denom&*denom& div 2)
        t& = denom& div 4
        x& = 1
        Repeat
            olda& = a&
            a& = (a&+b&) div 2
            ret = @BlinkWaitScreen(waitText$())
            b& = Sqrt(b&*olda&)
            Sub olda&, a&
            ret = @BlinkWaitScreen(waitText$())
            t& = t&-((x&*olda&*olda&) div denom&)
            Add x&, x&
            ret = @BlinkWaitScreen(waitText$())
        Until olda&=0
      Return (a&*a&) div t&
    EndFunction
    
    Function bnx_e&(denom&)
      'Return value / denom& = e.
      Local divisor&, diff&, num&
      num& = denom&
      diff& = denom&
      divisor& = 1
      repeat
        div diff&, divisor&
        inc divisor&
        Add num&, diff&
        ret = @BlinkWaitScreen(waitText$())
      Until diff&=0
      Return num&
    EndFunction
    
    Function bn_power&(a&, b%)
      ' (Returns 1 if b% negative - not quite logical...)
      Local c&, i%
      c& = 1
      For i% = 1 To b%
        c& = c&*a&
        ret = @BlinkWaitScreen(waitText$())
      Next i%
      Return c&
    EndFunction
    
    Function bn_radix$(a&,base%)
      'for arbitrary precision integers
      Local temp2$, sign$
    
      If base%>64 Or base%<2 
        Return ""
      EndIf
      sign$=""         !Initialize local variables
      temp2$ = ""
      If a&<0 Then
        sign$="-"
        a& = -a&
      EndIf
      repeat
        temp2$ = @bn_makedigit$(a& Mod base%) + temp2$
        div a&, base%
      Until a&=0
      Return sign$+temp2$
    EndFunction
    
    Function bnx_radix$(a&,b&,base%,maxlen%)
      'Returns a&/b& in base base%, max. maxlen% digits.
      'Is actually not used by this program since we can use the function
      ' bnx_radixPower$() below, which is about twice as fast.
      Local temp$, n&
    
      If b&=0
        Return ""
      EndIf
      temp$ = ""
      If Sgn(a&)<>Sgn(b&)
        temp$="-"
      EndIf
      a& = Abs(a&)
      b& = Abs(b&)
      n& = a& div b&      !Integer part
      temp$ = temp$ + @bn_radix$(n&,base%)+"."
      If temp$="."
        Return ""         !Invalid base%
      endif
      n& = a& Mod b&      !Residue
      repeat
        mul n&,base%
        temp$ = temp$+@bn_makedigit$(n& div b&)
        n& = n& Mod b&
        ret = @BlinkWaitScreen(waitText$())
      Until (Len(temp$)>=maxlen%) Or (n&=0)
      Return temp$
    EndFunction
    
    Function bnx_radixPower$(a&,b%,base%)
      'Returns a&/(base%^b%) in base base%, b% "decimal" digits.
      Local temp$, sign&, i%
    
      If base%>64 Or base%<2     !Invalid base
        Return ""
      EndIf
      sign$=""                   !Initialize local variables
      temp$=""
      If a&<0 Then
        sign$="-"
        a& = -a&
      EndIf
      For i% = 1 To b%
        temp$ = @bn_makedigit$(a& Mod base%) + temp$
        div a&, base%
        ret = @BlinkWaitScreen(waitText$())
      Next i%
      temp$ = "."+temp$
      repeat
        temp$ = @bn_makedigit$(a& Mod base%) + temp$
        div a&, base%
      Until a&=0
      Return sign$+temp$
    EndFunction
    
    Function bn_makedigit$(dig&)
    '"else..if" must be nested, because of a bug in the Windows version.
    'dig& could be a normal integer if the result of a big integer division
    ' or of big integer MOD could be assigned to a normal integer. 
    ' In the bytecode file this works, but the interpreter yields wrong results
    ' when doing this; so I decided to use a big integer here.
    ' However, in the Windows version, the interpreter now crashes when using this function!
      Local digi%
          'Print "Entered MakeDigit."
      digi% = dig&  
      If (digi%<10)
      Return Chr$(digi%+48)
      Else 
        If (digi%<36)
        Return Chr$(digi%+55)
        Else 
          If (digi%<62)
          Return Chr$(digi%+61)
          Else 
            If (digi%=62)
            Return "@"
            Else 
              If (digi%=63)
              Return "$"
              Else
                Return "!"
              EndIf
            EndIf
          EndIf
        EndIf
        EndIf
    EndFunction
    
    Function OpenWaitScreen(t$())
      Local maxLen%, i%, y%
      deftext 0, textW/100, textW/75, 0
      maxLen% = 0
      For i% = 0 To Dim?(t$())-1
        maxLen% = max(ltextlen(t$(i)), maxLen%)
      Next i%
      waitW% = maxLen%+2*textW
      waitH% = (Dim?(t$())+1)*textW*2
      waitX1% = (scrw%-waitW%)/2
      waitY1% = (scrh%-waitH%)/2
      Color color_rgb(0.3,0.3,0.3)
      pbox waitX1%, waitY1%, waitX1%+waitW%-1, waitY1%+waitH%-1
      Color color_rgb(1,1,0.4)
      y% = waitY1% + textW
      For i% = 0 To Dim?(t$())-1
        ltext waitX1%+textW, y%, t$(i%)
        Add y%, (textW*2)
      Next i%
      showpage
      waitT = Timer
      Return 0
    EndFunction
    
    Function BlinkWaitScreen(t$())
      Local i%, y%
      If Timer<waitT+1
        Return 0
      EndIf
      waitT = Timer
      blink% = blink% Xor 1
      If blink%
        Color color_rgb(0.3,0.7,0.7)
      Else
        Color color_rgb(0.3,0.3,0.3)
      EndIf
      pbox waitX1%, waitY1%, waitX1%+waitW%-1, waitY1%+waitH%-1
      Color color_rgb(1,1,0.4)
      y% = waitY1% + textW
      For i% = 0 To Dim?(t$())-1
        ltext waitX1%+textW, y%, t$(i%)
        Add y%, (textW*2)
      Next i%
      showpage
      Return 1
    EndFunction
    
    Function CloseWaitScreen()
      Color color_rgb(0,0,0)
      pbox waitX1%, waitY1%, waitX1%+waitW%-1, waitY1%+waitH%-1
      showpage
      Return 0
    EndFunction
    
    Function WaitMouseOrKey$()
      While InKey$<>""
      Wend
      repeat
        m$=InKey$
      Until (m$<>"") Or ((mousek And 7)<>0)
      Return m$
    EndFunction
    
     

    Last edit: Wanderer 2015-04-12
  • Wanderer

    Wanderer - 2015-04-12

    Here the program attached as a *.bas file.
    (Funny thing in this forum: There is no option for attaching a file to the first post of a thread, but the following posts support attachments...)

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.