Extended Integer Arithmetic code in PB

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Extended Integer Arithmetic code in PB

Post by davido »

I have been looking for some time for a high precision arithmetic environment without much success.

Looking in the Forums I noticed that there are an incomplete set of routines, notably divide missing.

So I decided to write my own, in PureBasic, of course and would like to share these with others.

There are two parts:

1. EngineXI.pbi which is the main code.
2. CalcXI which is a front end calculator I used for checking whilst writing the engine.

I code for fun and so it is rather crude and not very tidy. Please feel free to criticise if it will lead to improvements.

I have tested it on both 32 and 64 bit machines. In fact it was originally written for 64 bit windows 7 and I had to change the relevant .I's to .Q's - it is about 5:3 times faster on 64 bit as compared to 32 bit.

It appears to work on 64 bit Linux Mint 14, too.

Updated code dated: 20130217

Dave
Last edited by davido on Sun Feb 17, 2013 11:54 am, edited 3 times in total.
DE AA EB
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Extended Integer Arithmetic code in PB

Post by infratec »

Hi davido,

thanks for sharing :!:

I just played a bit with it.
If your number is to big for the array (8000) the program crashes.
It happens to me when I pressed the faculty button.

If a check slows down to much, an OnError handler could make sense.

When you need global variables inside an includefile, it makes sense to make the names 'save'.
I simply use the complete name of the file infront of each variable:
XIEngine_RA$

Because Ra$ could be used by the main application.

Bernd
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

Dear Bernd,

Thanks very much. I'll make the changes in due course.

Any comments are most welcome. We only learn when our mistakes are pointed out.

I have found a few errors myself since uploading.

At the moment I'm trying to improve the use of the calls to the procedures in the Engine from MultXI(@Num1.XI,@Num2.XI,@Num3.XI) to *Numx.XI = MultXI(@Num1.Xi,@Num2.XI)

Regards

Dave
DE AA EB
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Extended Integer Arithmetic code in PB

Post by STARGÅTE »

At the moment I'm trying to improve the use of the calls to the procedures in the Engine from MultXI(@Num1.XI,@Num2.XI,@Num3.XI) to *Numx.XI = MultXI(@Num1.Xi,@Num2.XI)
I find the procedure call good, you can define a variable with Name.XI.
If you change it to *Numx.XI = MultXI(@Num1.Xi,@Num2.XI), you have to free all XI-object with a procedure like FreeXI(*Num.XI), because the numbers then only pointers, and no longer be released itself (on end of a procedure).
If you forget that (or the user), you get very fast memory overflow.
________

The procedure MultXI():

Code: Select all

  For X=1 To *Num1\NumOfLimbs
    For Y=1 To *Num2\NumOfLimbs
      *Num3\Ra(X+Y-P)+ *Num1\Ra(X)* *Num2\Ra(Y)
    Next Y
  Next X
There, you've got the problem with the overflow.
If you insert a check (*Num3\Ra(X+Y-P) > Radix), you can solve this problem.
Then you can add a carry like in addition:

Pseudocode:

Code: Select all

For X
  For Y
    Result[X+Y-P] = Number1[X] * Number2[Y] + Carry
    Carry = Result[X+Y-P] / Radix
    Result[X+Y-P] % Radix
  Next Y
Next X
_____

One more comment:
Your code is fine and works (after some bug fixes), good work!
However, this variant (to work with large numbers) is not very fast.
So do not put too much work time into the code to make it faster.
Fast multi byte arithmetic is only possible with ASM-only.
I speak from experience, because I changed already to ASM for my unlimited integers include.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

Stargate thank you for your comments. :D

I had decided at the start to only use pb statements and avoid messing with memory as I do not fully understand the ramifications.
Thank you for pointing out the trap I was falling into. :oops: I think from your comments that I probably would not have been good enough to make it work anyway!

I thought that I had understood the problem with overflow in multiplication. That is why I chose 'limbs' upto 99,999,999, rather than the full 32 bits worth.
I reasoned that I could put off the overflow check for just about 900 iterations, which equates to about 7383 digits.
I did this to avoid using the very slow % operation, too often.
I checked this by using repunit 9's. It is easy to see from the pattern if any error occurs. Such numbers should be, I think, the first to cause overflow.
Are you still sure I need to make this change, which I'm trying to avoid?

I have a constantly running test that chooses 'random' numbers of a 100-7000 digits and divides that number by 50-2000 digits. It then checks by multiplying the divisor by answer then adds the residue and compares the result. So far no errors have occurred after more than 2,000,000,000 iterations. I do appreciate that this is not, by any means, proof.

I take your point about asm. I might attempt this but on modern machines its is much more difficult compared with the early 1980's when I happily used 6502 assembler on an Apple ][.


Best wishes and thank you for taking interest in my little project.

Dave
DE AA EB
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Extended Integer Arithmetic code in PB

Post by STARGÅTE »

right, the % operation is not fast.
But you can add an other way.
You use the school method for multiplication.
You can use for large numbers the Karatsuba algorithm.
As a result, large numbers clipped so that it can't come to overflow.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

Thanks Stargate looks very interesting! :D
DE AA EB
PMV
Enthusiast
Enthusiast
Posts: 727
Joined: Sat Feb 24, 2007 3:15 pm
Location: Germany

Re: Extended Integer Arithmetic code in PB

Post by PMV »

STARGÅTE wrote:right, the % operation is not fast.
Is it not been replaced by a fast single asm call? :x
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Extended Integer Arithmetic code in PB

Post by STARGÅTE »

i mean that it is not fast to work with: Number % 1000000
for a processor it is easier to work with % 2^n.
then you must not be divided, only clip the number with & 2^n-1

division is also slowly under ASM, compared to multiply, add, sub, and, or ...
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Extended Integer Arithmetic code in PB

Post by Michael Vogel »

Maybe you like to compare this code to VNF, there are some similarities, so some speed tests could be interesting as well (25000!)...
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

Hi Michael,

I suspect that my multiplication method may be a little faster than the traditional way, which I think you used. I had at first decided to use 9 digit limbs but they require a carry after each multiplication. The mod is very slow. I reasoned that if I used only 8 digit limbs the total size after multiplication would be a maximum of 16 digits and I could do 900+ additions without overflowing. Potentially saving lots of time.
Unfortunately addition and subtraction will suffer somewhat.

May I suggest you try only carrying in your multiply routine, just before overflow would occur, I think every 88 loops.

I could only check out one of your vnf multiplications of two 384 digit numbers which took 680ms for 10,000 on my machine. My routine with two 400 digit numbers too a little over 320ms. If you would like to check out yourself I have a speed statistics option in the help menu of the test calculator.

Thank you for pointing out 25000! - I need some error routines!
I had intended the initial limit of 7200 digits. Increasing the number of limbs allows 25,000! and 60,000! (260635 digits). Is there any other significance in the 25,000!

The Huge# thread is very large and it is difficult to locate your latest version of vnf; would you mind posting the latest version. Perhaps we could pick the best from both?

Regards

Dave
DE AA EB
Helle
Enthusiast
Enthusiast
Posts: 178
Joined: Wed Apr 12, 2006 7:59 pm
Location: Germany
Contact:

Re: Extended Integer Arithmetic code in PB

Post by Helle »

Oh, I like this things :D!
Divide (a nightmare !): Please test "123456789123456789 / 123456789".
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

Thanks Helle,

I was hoping that I had gotten rid of all the bugs in divide. I've tested thousands of millions of very large numbers with no errors so the algorithm must be generally sound.
The problem is finding the correct test divisor.

One other error I found a few minutes ago: Try dividing 77777777777777777/11111111111111111

To save time drag the repunit 7,17 into register 1, then the divisor into register 2 then press / =

Looks like I have a bit of work on.

Regards

Dave
DE AA EB
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

I have updated the Integer routines:

version 0.89

XIEngine
~ Kludged code for division to remove 4 bugs.
~ Re-wrote Division to tidy the code. Division now 5-8 times faster.
~ Removed bug in subtraction
~ Added some error codes
~ Removed 7200 digit limit on multipication
~ Now works with +ve and -ve integers; even division (might be controversial!)
~ Added GCD
~ Added square root. Just produces first 500 digits.
Later will give integer + residue.
~ Add Square and Cube
~ Added prime generation: 2-99999989 (1 limb)


XICalc
~ Updated to include all functions
Included as demo, but used by me for testing


~ Tested with PB 5.10 64bit, windows 7/64


XICalc:

Code: Select all

EnableExplicit

XIncludeFile "XIEngine.pbi"

Enumeration ;-Gadgets
  #RegABContainer
  #RegAText
  #RegisterA
  #RegBText
  #RegisterB
  #RegCContainer
  #RegCText
  #RegisterC
  #ResidueRegText
  #ResidueReg
  #Oper
  #Actions
  #AddToRegA
  #KeyFrame
  #But0
  #But1
  #But2
  #But3
  #But4
  #But5
  #But6
  #But7
  #But8
  #But9
  #ButPlus
  #ButMinus
  #ButMul
  #ButDiv
  #ButGCD
  #ButEquals
  #ButPlusMinus
  #ButDelChar
  #ButClearEntry
  #ButClearAll
  #Constants
  #Memories=#Constants+12
  #RandomNumber=#Memories+12
  #SquareRoot
  #Factorial
  #Powers
  #Repunit9
  #Functions
  #ExtraFunctions
  #MemoriesFrame
  #ConstantsFrame
  #Halfer
  #Doubler
  #ResTo1
  #RegisterTransfers
  #SwapReg
  #Squarer
  #Cuber
  #NthPrime
  #Regs2Memories
EndEnumeration

Enumeration ;Windows
  #WinMain
  #StatusBarWinMain
EndEnumeration

Enumeration ;MenuItems
  #WinMainMenu
  #Quit
  #About
  #Statistics
  #DivTest
  #CheckDivision
  #TestBed
  #EnterNums
  #CopyRegisters
  #MemoriesButtons
  #Accuracy
  #Copy
  #Paste
  #WinMainPopUpMenu
  #HideMCoords
EndEnumeration

Enumeration #PB_Compiler_EnumerationValue ;Shortcuts
  #Escape 
  #Return 
  #crtlC
  #crtlV
  #Key0
  #Key1
  #Key2
  #Key3
  #Key4
  #Key5
  #Key6
  #Key7
  #Key8
  #Key9
  #KeyPlus
  #KeyMinus
  #KeyMul
  #KeyDiv
  #KeyEquals
  #KeyDelChar
  #KeyClearEntry
  #KeyClearAll  
  #SpeedTextKey
  #AltD
  #AltC
EndEnumeration  ;Shortcuts

Enumeration ;Actions
  #DoAdd
  #DoSubtract
  #DoMultiply
  #DoDivide
  #DoFactorial
  #DoExp
  #DoGCD
EndEnumeration

Global EntryMode.I=1 ;// 1=Input Register1 (1st number) etc.
Global ActionMode.I ;// Add Multiply etc.
Global Number1$, Number2$, Number3$, Number4$ ;// Strings of each register
Global CurrentDirectory$ = GetCurrentDirectory()
Global AMessage$
Global Dim Consts$(11)
Global Dim Memories$(11)
Global HideMouseCoordinates.I=1

Declare ClearButton()
Declare CreateWinMainGadgets()
Declare CreateWinMainMenu()
Declare CreateWinMainPopupMenu()
Declare DivTesterXI(M.I)
Declare DoAdd()
Declare DoCube()
Declare DoDouble()
Declare DoDivide()
Declare DoMultiply()
Declare DoSquare()
Declare DoSubtract()
Declare.S DotStr(I.I)
Declare DrawActionCharacter(ActionChar$) 
Declare EventWinMain(EventID)
Declare HelpNumberEntry(HNum.I)
Declare InsertPrime(Primen.I,EMode.I)
Declare OpenWinMain()
Declare PutTextInRegister(RText$,RRegister.I)
Declare Setup()

Procedure About()
  Protected A$
  A$="CalcXI"+Chr(10)
  A$+"Dave Ward December 2012 - January 2013"+Chr(10)+"Written in PureBasic"
  MessageRequester("Extended Precision Integer Calculator",A$)
EndProcedure

Procedure Action_Add()
  If Number1$<>""
    ActionMode=#DoAdd
    DrawActionCharacter("+")
    SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
    SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$FF0000)
    EntryMode=2
  EndIf   
EndProcedure

Procedure HelpNumberEntry(HNum.I)
  Protected A$
  Select HNum
  Case 1
    A$="Entering Numbers:"+Chr(10)+Chr(10)
    A$+"Numbers can be entered via the 'on-screen keypad'"+Chr(10)
    A$+"One can drag numbers onto the relevant register. From other applications or internal registers"+Chr(10)
    
  Case 2
    A$="Memories - there are 12, 0-11:"+Chr(10)+Chr(10)
    A$+"To Save - Simply click on a register and drag to a memory button."+Chr(10)
    A$+"        - The r2m button saves registers to memories 8 - 11."+Chr(10)
    A$+"To Copy - click a memory button to copy its contents to the currently active register."
    
  Case 3
    A$="Copying registers"+Chr(10)+Chr(10)
    A$+"Numbers strings can be dragged or dropped between registers or external applications."+Chr(10)
    A$+"Clicking a register places the contents on the clipboard."
    
  Case 4
    A$="Accuracy"+Chr(10)+Chr(10)
    A$+"The array in the XI structure is set at 13000 which equates to 104,000 digits."
  EndSelect 
  MessageRequester("Help!",A$)
EndProcedure

Procedure ClearButton()
  ClearGadgetItems(#RegisterA)
  SetGadgetText(#RegAText,"1st number")
  SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$FF0000)
  Number1$="" 
  ClearGadgetItems(#RegisterB)
  SetGadgetText(#RegBText,"2nd number")
  SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$000000)
  Number2$=""
  ClearGadgetItems(#RegisterC)
  SetGadgetText(#RegCText,"Result")
  SetGadgetColor(#RegCText,#PB_Gadget_FrontColor,$000000)
  Number3$=""
  ClearGadgetItems(#ResidueReg)
  SetGadgetText(#ResidueRegText,"Residue")
  SetGadgetColor(#ResidueRegText,#PB_Gadget_FrontColor,$000000)
  Number4$=""
  EntryMode=1
  DrawActionCharacter(" ")
  StatusBarText(#StatusBarWinMain,0,"")
  StatusBarText(#StatusBarWinMain,1,"")
EndProcedure

Procedure CreateWinMainGadgets()
  Protected.L M, A$
  ContainerGadget(#RegABContainer,20,20,766,163,#PB_Container_Raised)
  TextGadget(#RegAText,5,0,350,17,"1st number (Register 1)",#PB_Text_Center)
  SetGadgetColor(#RegAText,#PB_Gadget_BackColor,$DEC4B0)
  SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$FF0000)
  ListViewGadget(#RegisterA,0,17,360,140)
  GadgetToolTip(#RegisterA,"Register 1")
  SetGadgetColor(#RegisterA,#PB_Gadget_BackColor,$87B8DE)
  
  TextGadget(#RegBText,405,0,350,17,"2nd number (Register 2)",#PB_Text_Center)
  SetGadgetColor(#RegBText,#PB_Gadget_BackColor,$DEC4B0)
  ListViewGadget(#RegisterB,400,17,360,140)
  GadgetToolTip(#RegisterB,"Register 2")
  SetGadgetColor(#RegisterB,#PB_Gadget_BackColor,$E6D8AD)
  
  CanvasGadget(#Actions,365,50,30,60)
  DrawActionCharacter(" ")
  CloseGadgetList()
  
  ContainerGadget(#RegCContainer,420,210,366,340,#PB_Container_Raised)
  TextGadget(#RegCText,5,0,350,17,"Result",#PB_Text_Center)
  SetGadgetColor(#RegCText,#PB_Gadget_BackColor,$DEC4B0)
  ListViewGadget(#RegisterC,0,17,360,140)
  GadgetToolTip(#RegisterC,"Register 3")
  
  TextGadget(#ResidueRegText,5,176,350,17,"Residue",#PB_Text_Center)
  SetGadgetColor(#ResidueRegText,#PB_Gadget_BackColor,$DEC4B0)
  ListViewGadget(#ResidueReg,0,193,360,140)
  SetGadgetColor(#ResidueReg,#PB_Gadget_BackColor,$CDFAFF)
  GadgetToolTip(#ResidueReg,"Register 4 - residue from division.")
  CloseGadgetList()
  
  Frame3DGadget(#KeyFrame,280,190,136,179,"Keypad")
  ButtonGadget(#But9,350,237,30,30,"9")
  ButtonGadget(#But8,318,237,30,30,"8")
  ButtonGadget(#But7,286,237,30,30,"7")
  ButtonGadget(#But6,350,269,30,30,"6")
  ButtonGadget(#But5,318,269,30,30,"5")
  ButtonGadget(#But4,286,269,30,30,"4")
  ButtonGadget(#But3,350,301,30,30,"3")
  ButtonGadget(#But2,318,301,30,30,"2")
  ButtonGadget(#But1,286,301,30,30,"1")
  ButtonGadget(#But0,286,333,30,30,"0")
  
  ButtonGadget(#ButClearAll,286,206,30,30,"AC")
  ButtonGadget(#ButClearEntry,318,206,30,30,"CE")
  ButtonGadget(#ButDelChar,350,206,30,30,"Del")
  
  ButtonGadget(#ButDiv,382,206,30,30,"/")
  ButtonGadget(#ButMul,382,237,30,30,"x")
  ButtonGadget(#ButMinus,382,269,30,30,"--")
  ButtonGadget(#ButPlus,382,301,30,30,"+")
  ButtonGadget(#ButEquals,382,333,30,30,"=")
  ButtonGadget(#ButPlusMinus,350,333,30,30,Chr(177))
  GadgetToolTip(#ButPlusMinus,"Changes the sign of the current register. Red indicates a negative number.")
  
  Frame3DGadget(#Functions,170,190,105,179,"Functions")
  ButtonGadget(#Squarer,176,206,30,30,"x"+Chr(178))
  GadgetToolTip(#Squarer,"Squares the 1st number (register 1)")
  ButtonGadget(#Cuber,208,206,30,30,"x"+Chr(179))
  GadgetToolTip(#Cuber,"Cubes the 1st number (register 1)")
  ButtonGadget(#Powers,240,206,30,30,"x^y")
  GadgetToolTip(#Powers,"Enter Base in Register1, click x^y button then enter a positive power in Register2, click = button.")
  ButtonGadget(#SquareRoot,176,238,30,30,"Sqr")
  GadgetToolTip(#SquareRoot,"Enter Number in Register 1 then press Sqr key")
  ButtonGadget(#Factorial,176,270,30,30,"!")
  GadgetToolTip(#Factorial,"Factorial of 1st number (register 1)")
  ButtonGadget(#Halfer,208,238,30,30,Chr(189))
  GadgetToolTip(#Halfer,"Halves the 1st number (register 1)")
  ButtonGadget(#Doubler,240,238,30,30,"x2")
  GadgetToolTip(#Doubler,"Doubles the 1st number (register 1)")
  ButtonGadget(#RandomNumber,176,302,30,30,"Rnd")
  GadgetToolTip(#RandomNumber,"Enter Number of digits in register, then press Random Number key!")

  ButtonGadget(#ButGCD,208,302,30,30,"GCD")
  A$ = "Greatest Common Divisor: Enter number in Register 1 then press GCD."
  GadgetToolTip(#ButGCD,A$ + " Place 2nd number in Register 2 and press =")
  ButtonGadget(#NthPrime,176,334,30,30,"P[n]")
  GadgetToolTip(#NthPrime,"Replaces the number [n] in register with Prime[n]")
  
  Frame3DGadget(#ExtraFunctions,20,375,255,75,"ExtraFunctions")
  
  ButtonGadget(#Repunit9,240,390,30,30,"Rep")
  A$ = "Enter Number in register. Then press 'Rep'. 150 is replaced by"
  GadgetToolTip(#Repunit9,A$+" a string of 50 1's - 675 by string of 75 6's.")
  
  ButtonGadget(#AddToRegA,5,740,80,18,"Enter Number")
  
  Frame3DGadget(#MemoriesFrame,20,450,395,50,"Memories")
  For M=0 To 11
    ButtonGadget(#Memories+M,27+32*M,467,28,28,Str(M))
    GadgetToolTip(#Memories+M,Memories$(M))
    EnableGadgetDrop(#Memories+M,#PB_Drop_Text,#PB_Drag_Copy)
  Next M
  
  Frame3DGadget(#ConstantsFrame,20,500,395,50,"Constants")
  For M=0 To 11
    ButtonGadget(#Constants+M,27+32*M,517,28,28,Chr(M+65))
    GadgetToolTip(#Constants+M,Consts$(M))
  Next M
  
  Frame3DGadget(#RegisterTransfers,280,375,136,75,"Register Transfers")
  ButtonGadget(#ResTo1,284,390,30,30,Chr(169))
  GadgetToolTip(#ResTo1,"Moves Result to Register 1 - Clears other registers.")
  ButtonGadget(#SwapReg,316,390,30,30,"1><2")
  GadgetToolTip(#SwapReg,"Swaps Register 1 with Register 2")
  ButtonGadget(#Regs2Memories,284,422,30,30,"r2m")
  GadgetToolTip(#Regs2Memories,"Moves Register 1-4 to Memories 8-11.")
  
  EnableGadgetDrop(#RegisterA,#PB_Drop_Text,#PB_Drag_Copy)
  EnableGadgetDrop(#RegisterB,#PB_Drop_Text,#PB_Drag_Copy)
  EnableGadgetDrop(#RegisterC,#PB_Drop_Text,#PB_Drag_Copy)
  EnableGadgetDrop(#ResidueReg,#PB_Drop_Text,#PB_Drag_Copy)
EndProcedure

Procedure CreateWinMainMenu()
  If CreateMenu(#WinMainMenu, WindowID(#WinMain))
    MenuTitle("File")
    MenuBar()
    MenuItem( #HideMCoords, "Toggle Hide Mouse coordinates")
    MenuBar()
    MenuItem( #Quit, "&Quit")
    
    MenuTitle("Edit")
    MenuItem(#Copy,"Copy from current register (red title)"+Chr(9)+"ctrl+C")
    MenuItem(#Paste,"Paste to current register (red title)"+Chr(9)+"ctrl+V")
    
    MenuTitle("Help")
    MenuItem(#About, "About")
    MenuItem(#Statistics, "Speed Statistics"+Chr(9)+"Alt+S")
    MenuBar()
    OpenSubMenu("Using CalcXI")
      MenuItem(#EnterNums,"Entering numbers")
      MenuItem(#MemoriesButtons,"Memory buttons")
      MenuItem(#CopyRegisters,"Copying Registers")
      MenuItem(#Accuracy,"Accuracy.")
    CloseSubMenu()
    OpenSubMenu("Testing CalcXI")
      MenuItem(#DivTest, "Divide Tester (100/50 random digits)"+Chr(9)+"Alt+D")
      MenuItem(#CheckDivision, "Check Division (Reg2 * Reg3 + Res)"+Chr(9)+"Alt+C")
      
      MenuItem(#TestBed, "Test Bed"+Chr(9)+"Alt+T")
    CloseSubMenu()
    
  EndIf
EndProcedure

Procedure CreateWinMainPopupMenu()
EndProcedure

Procedure DivTesterXI(M.I)
  Protected.L FlagError, Temp$, dt.I, MM.I, HH.I, SS.I, Iterations.I, DivisorDigits.I, DividendDigits.I
  Protected A$, DD$
  Protected.XI DNum1, DNum2, DNum3, DNum4, DNum5, DNum6, DNum7, DNum8
  dt=ElapsedMilliseconds()
  
  FlagError=0
  Iterations = 1000*1000*100
  DivisorDigits = 25
  DividendDigits = 125
  DD$ = " - "+Str(DividendDigits)+"/"+Str(DivisorDigits) + " digits"
  StatusBarText(#StatusBarWinMain,1,"Checking Division! " + DD$)
  If OpenFile(0,CurrentDirectory$+"DivTestResults.txt")
    FileSeek(0,Lof(0))
    WriteStringN(0,FormatDate("%dd/%mm/%yyyy %hh:%ii:%ss",Date())+" Starting run of "+DotStr(Iterations)+DD$)
    WriteStringN(0,"")
    For M=1 To Iterations
      If DivisorDigits = 25 And DividendDigits = 125
        A$=RSet("",25,Chr(Random(8)+49))+RSet("",25,Chr(Random(9)+48))
        A$+RandomExactNumberXI(25)+RSet("",25,Chr(Random(9)+48))+RSet("",25,Chr(Random(9)+48))
        ValXI(A$,@DNum1)
        A$=RSet("",20,Chr(Random(8)+49))
        A$+RandomExactNumberXI(10)+RSet("",20,Chr(Random(9)+48))
        ValXI(A$,@DNum2)
      Else
        ValXI(RandomExactNumberXI(DividendDigits),@DNum1)
        ValXI(RandomExactNumberXI(DivisorDigits),@DNum2)
      EndIf
      
      ZeroXI(@DNum3) : ZeroXI(@DNum4) : ZeroXI(@DNum5) : ZeroXI(@DNum6)
      DivXI(@DNum1,@DNum2,@DNum3,@DNum4)
      ZapLeadZeroesXI(@DNum4)
      MultXI(@DNum2,@DNum3,@DNum5)
      AddXI(@DNum4,@DNum5,@DNum6)
      ZapLeadZeroesXI(@DNum6)
      If EqualXI(@DNum1,@DNum6)=1
        
      Else 
        FlagError+1
        WriteStringN(0,"")
        WriteStringN(0,"Test: "+Str(M)+" Error.  "+FormatDate("%dd/%mm/%yyyy %hh:%ii:%ss",Date()))
        WriteStringN(0,"-----")
        WriteStringN(0,"Dividend: "+Str8XI(@DNum1))
        WriteStringN(0,"Divisor: "+Str8XI(@DNum2))
        WriteStringN(0,"Answer: "+Str8XI(@DNum3))
        WriteStringN(0,"Residue: "+Str8XI(@DNum4))
        WriteStringN(0,"")
        WriteStringN(0,"Dividend: "+Str8XI(@DNum1))
        WriteStringN(0,"Check No: "+Str8XI(@DNum6))
        WriteStringN(0,"")
      EndIf
      WindowEvent()
      If M%10000=0
        SS = (ElapsedMilliseconds()-dt)/1000
        MM = SS / 60
        SS = SS - MM * 60
        HH = MM / 60
        MM = MM - HH * 60
        StatusBarText(#StatusBarWinMain,0,Space(45))
        A$ = "Test: " + DotStr(M)+" - " + Right("00" + Str(HH),2) + ":"
        StatusBarText(#StatusBarWinMain,0,A$ + Right("00" + Str(MM),2) + ":" + Right("00" + Str(SS),2))
      EndIf
    Next M
    
    If FlagError=0
      StatusBarText(#StatusBarWinMain,0,"                                             ")
      StatusBarText(#StatusBarWinMain,0,"Test: "+ DotStr(M-1)+" No errors!")
      WriteStringN(0,"Test completed: No Errors found.  "+FormatDate("%dd/%mm/%yyyy %hh:%ii:%ss",Date()))
      WriteStringN(0,"")
    Else
      StatusBarText(#StatusBarWinMain,1,"Test: found "+Str(FlagError)+" errors!!")
    EndIf
  Else
    MessageRequester("ERROR!","Cannot read file: "+CurrentDirectory$)
  EndIf
  CloseFile(0)
  StatusBarText(#StatusBarWinMain,1,"")
EndProcedure

Procedure DoAdd()
  Protected.XI ANum, BNum, CNum
  ValXI(Number1$,@ANum.XI)
  ValXI(Number2$,@BNum.XI)
  AddXI(@ANum.XI,@BNum.XI,@CNum.XI)
  PutTextInRegister(StrXI(@CNum.XI),3)
EndProcedure

Procedure DoCube()
  Protected.XI ANum,CNum
  Protected PE.I
  ValXI(Number1$,@ANum)
  PE=3
  PowerXI(@ANum,PE,@CNum)
  Number3$=StrXI(@CNum)
  PutTextInRegister(Number3$,3)
EndProcedure

Procedure DoDouble()
  Protected.XI ANum,CNum
  EntryMode=2
  ValXI(Number1$,@ANum)
  DoubleXI(@ANum,@CNum)
  Number3$=StrXI(@CNum)
  PutTextInRegister(Number3$,3)      
  EntryMode=3
  SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
  SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$000000)
  SetGadgetColor(#RegCText,#PB_Gadget_FrontColor,$FF0000)
  SetGadgetColor(#ResidueRegText,#PB_Gadget_FrontColor,$000000)    
EndProcedure

Procedure DoDivide()
  Protected.XI ANum, BNum, CNum, Res
  ZeroXI(@CNum)
  ZeroXI(@Res)
  ValXI(Number1$,@ANum.XI)
  ValXI(Number2$,@BNum.XI)
  DivXI(@ANum.XI,@BNum.XI,@CNum.XI,@Res.XI)
  Number3$=StrXI(@CNum.XI)
  PutTextInRegister(Number3$,3)
  Number4$=StrXI(@Res.XI)
  PutTextInRegister(Number4$,4)  
EndProcedure

Procedure DoGCD()  
  Protected.XI ANum, BNum, CNum
  ValXI(Number1$,@ANum.XI)
  ValXI(Number2$,@BNum.XI)
  GCDXI(@ANum.XI,@BNum.XI,@CNum.XI)
  Number3$=StrXI(@CNum.XI)
  PutTextInRegister(Number3$,3) 
EndProcedure

Procedure DoHalf()
  Protected.XI ANum,CNum,Res
  EntryMode=2
  ValXI(Number1$,@ANum)
  HalveXI(@ANum,@CNum)
  Number3$=StrXI(@CNum)
  PutTextInRegister(Number3$,3)
  Number4$=Str(Residue64Bit)
  PutTextInRegister(Number4$,4)        
  EntryMode=3
  SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
  SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$000000)
  SetGadgetColor(#RegCText,#PB_Gadget_FrontColor,$FF0000)
  SetGadgetColor(#ResidueRegText,#PB_Gadget_FrontColor,$FF0000)    
EndProcedure

Procedure DoMultiply()  
  Protected.XI ANum, BNum, CNum
  ValXI(Number1$,@ANum.XI)
  ValXI(Number2$,@BNum.XI)
  MultXI(@ANum.XI,@BNum.XI,@CNum.XI)
  Number3$=StrXI(@CNum.XI)
  PutTextInRegister(Number3$,3) 
  
EndProcedure

Procedure DoSquare()
  Protected.XI ANum, CNum
  ValXI(Number1$,@ANum.XI)
  MultXI(@ANum.XI,@ANum.XI,@CNum.XI)
  Number3$=StrXI(@CNum.XI)
  PutTextInRegister(Number3$,3) 
  SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
  SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$000000)
  SetGadgetColor(#RegCText,#PB_Gadget_FrontColor,$FF0000)
EndProcedure

Procedure DoSubtract()  
  Protected.XI ANum, BNum, CNum
  ValXI(Number1$,@ANum.XI)
  ValXI(Number2$,@BNum.XI)
  SubXI(@ANum.XI,@BNum.XI,@CNum.XI)
  PutTextInRegister(StrXI(@CNum.XI),3) 
  
EndProcedure

Procedure.S DotStr(I.I) ;// I prefer commas
  ;// Based upon post by Michael Vogel, 2011
  Protected S.S = Str(Abs(I)), signi.S
  If I < 0
    Signi = "-"
  Else
    Signi = ""
  EndIf
  I=Len(S)
  While I > 3
    I - 3
    S=InsertString(s,",",I + 1)
  Wend
  ProcedureReturn Signi + S
EndProcedure

Procedure DrawActionCharacter(ActionChar$)
  If LoadFont(1, "Arial", 36): EndIf 
  StartDrawing(CanvasOutput(#Actions)) 
  DrawingMode(#PB_2DDrawing_Transparent)
  Box(0, 0, 30, 60, RGB(240, 240, 240))
  DrawingFont(FontID(1))
  FrontColor(0)
  DrawText(5,5,ActionChar$)
  StopDrawing()  
EndProcedure

Procedure EventWinMain(EventID) 
  Protected.I M, PE, Primen
  Protected A$, B$, Digit$
  Protected.XI ANum, CNum, Res
  Select EventID 
    CompilerIf #PB_Compiler_Version > = 510
    Case #EventPrimesBegun
      StatusBarText(#StatusBarWinMain,2,"Generating Primes")
      
    Case #EventPrimesDone
      StatusBarText(#StatusBarWinMain,2,"Got: 5,761,455 Primes: 2 - 99,999,989")
    CompilerEndIf
    
  Case #PB_Event_Menu 
    
    Select EventMenu()
      
    Case #Escape
      End
      
    Case #HideMCoords
      HideMouseCoordinates=1-HideMouseCoordinates
      StatusBarText(#StatusBarWinMain,3,"")
      
    Case #Quit  
      End
      
    Case #crtlV, #Paste
      If EntryMode=1
        PutTextInRegister(GetClipboardText(),1)
      ElseIf EntryMode=2
        PutTextInRegister(GetClipboardText(),2)
      EndIf 
      
      
    Case #crtlC, #Copy
      If EntryMode=1
        SetClipboardText(Number1$)
      ElseIf EntryMode=2
        SetClipboardText(Number2$)
      EndIf
      
    Case #About
      About()
      
    Case #DivTest, #AltD
      DivTesterXI(1)
      
    Case #CheckDivision, #AltC
      ValXI(Number1$,@RV1.XI)
      ValXI(Number2$,@RV2.XI)
      ValXI(Number3$,@RV3.XI)
      ValXI(Number4$,@RV4.XI)
      MultXI(@RV2.XI,@RV3.XI,@RV5.XI)
      ZapLeadZeroesXI(@RV4.XI)
      AddXI(@RV5.XI,@RV4.XI,@RV6.XI)
      ZapLeadZeroesXI(@RV6.XI)
      SubXI(@RV1.XI,@RV6.XI,@RV7)
      ZapLeadZeroesXI(@RV7.XI)
      If RV7\NumOfLimbs = 1 And RV7\Ra(1) = 0
        AMessage$ = "Ok - Zero result"
      Else
        AMessage$ = "Error!"
      EndIf
      MessageRequester("Division check",AMessage$)
      
    Case #Statistics, #SpeedTextKey
      StatusBarText(#StatusBarWinMain,0,"")
      StatusBarText(#StatusBarWinMain,1,"Speed Test!")
      SpeedStatistics()
      StatusBarText(#StatusBarWinMain,1,"")
      
    Case #TestBed
      Protected P.I, Carry.I, Result.I, X.I, Adjust.I, S.I, E.I
      RV10\NumOfLimbs = 1
      RV10\Ra(1) = 1
      
      For M=1 To 1000
        Carry = 0
        For X = 1 To RV10\NumOfLimbs
          Result = RV10\Ra(X) * Primes(M) + Carry
          Carry = Result / Radix
          RV10\Ra(X) = Result - Radix * Carry 
        Next X
        If Carry > 0
          RV10\NumOfLimbs + 1
          RV10\Ra(RV10\NumOfLimbs) = Carry
        EndIf
      Next M 
      S=1 : E = RV10\NumOfLimbs
      While E > S
        Swap RV10\Ra(E) , RV10\Ra(S)
        S + 1 : E - 1
      Wend
      
      Number1$ = StrXI(RV10)
      PutTextInRegister(Number1$,1)
      
    Case #EnterNums
      HelpNumberEntry(1)
      
    Case #MemoriesButtons
      HelpNumberEntry(2)
      
    Case #CopyRegisters
      HelpNumberEntry(3)
      
    Case #Accuracy
      HelpNumberEntry(4)
      
    Case #Key0 To #Key9
      If EntryMode=1
        Number1$+Str(EventGadget()-#Key0)
        PutTextInRegister(Number1$,1)
      ElseIf EntryMode=2
        Number2$+Str(EventGadget()-#Key0)
        PutTextInRegister(Number2$,2)
      EndIf
      
    Case #KeyPlus
      Debug "++++++"
      Action_Add()
    EndSelect 
    
    
    
  Case #PB_Event_Gadget 
    
    Select EventGadget() 
    Case #AddToRegA
      
    Case  #But0 To #But9
      If EntryMode=1
        Number1$+Str(EventGadget()-#But0)
        PutTextInRegister(Number1$,1)
      ElseIf EntryMode=2
        Number2$+Str(EventGadget()-#But0)
        PutTextInRegister(Number2$,2)
      EndIf
      
    Case #ButPlus
      Action_Add()
    Case #ButMinus
      If Number1$<>""
        ActionMode=#DoSubtract
        DrawActionCharacter("-")
        SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
        SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$FF0000)
        EntryMode=2
      EndIf
    Case #ButMul 
      If Number1$<>""
        ActionMode=#DoMultiply
        DrawActionCharacter("x")
        SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
        SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$FF0000)
        EntryMode=2
      EndIf
    Case #ButDiv
      ActionMode=#DoDivide
      DrawActionCharacter("/")
      SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$FF0000)
      EntryMode=2
      
    Case #Powers
      ActionMode=#DoExp
      DrawActionCharacter("^")
      SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$FF0000)
      EntryMode=2
      
    Case #ButGCD
      ActionMode=#DoGCD
      DrawActionCharacter("g")
      SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$FF0000)
      
      EntryMode=2
      
    Case #ButEquals
      SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegCText,#PB_Gadget_FrontColor,$FF0000)
      SetGadgetColor(#ResidueRegText,#PB_Gadget_FrontColor,$FF0000)
      EntryMode=3
      
      Select ActionMode
      Case #DoAdd
        DoAdd()
      Case #DoSubtract
        DoSubtract()
      Case #DoMultiply
        DoMultiply()
      Case #DoDivide
        DoDivide()
      Case #DoExp
        ValXI(Number1$,@ANum.XI)
        PE=Val(Number2$)
        PowerXI(@ANum.XI,PE,@CNum.XI)
        Number3$=StrXI(@CNum.XI)
        PutTextInRegister(Number3$,3)
      Case #DoGCD
        DoGCD()
      EndSelect
      
    Case #Squarer
      DoSquare()
      
    Case #Cuber
      DoCube()
      
    Case #Doubler
      DoDouble()
      
    Case #Halfer
      DoHalf()
      
      
    Case #Factorial
      FactorialXI(Val(Number1$),@CNum)
      PutTextInRegister(StrXI(@CNum.XI),3)
      SetGadgetColor(#RegAText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegBText,#PB_Gadget_FrontColor,$000000)
      SetGadgetColor(#RegCText,#PB_Gadget_FrontColor,$FF0000)
      DrawActionCharacter("!")
      
    Case #ResTo1
      A$=Number3$
      ClearButton()
      Number1$=A$
      PutTextInRegister(Number1$,1)
      
    Case #SwapReg
      A$=Number1$
      B$=Number2$
      ClearButton()
      Number1$=A$
      Number2$=B$
      Swap Number1$,Number2$
      PutTextInRegister(Number1$,1)
      PutTextInRegister(Number2$,2)
      
    Case #Regs2Memories
      Memories$(8) = Number1$
      Memories$(9) = Number2$
      Memories$(10) = Number3$
      Memories$(11) = Number4$
      
      For M=8 To 11
        A$=Memories$(M)
        If Len(A$)>100
          A$=Left(A$,50)+"....."
        EndIf
        GadgetToolTip(#Memories+M,A$)
      Next M
      
    Case #ButDelChar
      If EntryMode=1
        Number1$=Left(Number1$,Len(Number1$)-1)
        PutTextInRegister(Number1$,1)
      ElseIf EntryMode=2
        Number2$=Left(Number2$,Len(Number2$)-1)
        PutTextInRegister(Number2$,2)
      EndIf
      
    Case #ButClearEntry
      If EntryMode=1
        ClearGadgetItems(#RegisterA)
        Number1$=""
      ElseIf EntryMode=2
        ClearGadgetItems(#RegisterB)
        Number2$=""
      EndIf
      
    Case #ButClearAll
      ClearButton()
      
    Case #ButPlusMinus
      Select EntryMode
      Case 1
        If Left(Number1$,1)="-"
          Number1$=Mid(Number1$,2)
        Else
          Number1$="-"+Number1$
        EndIf
        PutTextInRegister(Number1$,1)
        
      Case 2
        If Left(Number2$,1)="-"
          Number2$=Mid(Number2$,2)
        Else
          Number2$="-"+Number2$
        EndIf
        PutTextInRegister(Number2$,2)
        
      EndSelect
      
    Case #RandomNumber
      If EntryMode=1
        MakeRandomstring(Val(Number1$),EntryMode)
      ElseIf EntryMode=2
        MakeRandomstring(Val(Number2$),EntryMode)
      EndIf
      
    Case #SquareRoot
      ValXI(Number1$,@ANum.XI)
      SqrXI(@ANum.XI,@CNum.XI)
      Number3$=StrXI(@CNum.XI)
      PutTextInRegister(Number3$,3) 
      
    Case #NthPrime
      If EntryMode=1
        Primen = Val(Number1$)
      ElseIf EntryMode=2
        Primen = Val(Number2$)
      EndIf
      If Primen < 5761456
         InsertPrime(Primen,EntryMode)
      Else
        MessageRequester("WARNING!","Max is 5,761,455")
      EndIf
      
    Case #Repunit9
      If EntryMode=1
        Digit$=Left(Number1$,1)
        Number1$=Mid(Number1$,2)
        MakeRepString(Digit$,Val(Number1$),EntryMode)
      ElseIf EntryMode=2
        Digit$=Left(Number2$,1)
        Number2$=Mid(Number2$,2)
        MakeRepString(Digit$,Val(Number2$),EntryMode)
      EndIf
      
      
      
    Case #RegisterA 
      SetClipboardText(Number1$)
      StatusBarText(#StatusBarWinMain,0,"1st Number on Clipboard")
      
    Case #RegisterB 
      SetClipboardText(Number2$)
      StatusBarText(#StatusBarWinMain,0,"2nd Number on Clipboard")
      
    Case #RegisterC
      SetClipboardText(Number3$)
      StatusBarText(#StatusBarWinMain,0,"Result on Clipboard")
      
    Case #ResidueReg
      SetClipboardText(Number4$)
      StatusBarText(#StatusBarWinMain,0,"Residue on Clipboard")
      
    Case #Memories To #Memories+11
      If EntryMode=1
        Number1$=Memories$(EventGadget()-#Memories)
        PutTextInRegister(Number1$,1)
      ElseIf EntryMode=2
        Number2$=Memories$(EventGadget()-#Memories)
        PutTextInRegister(Number2$,2)
      EndIf
      
    Case #Constants To #Constants+11
      If EntryMode=1
        Number1$=Consts$(EventGadget()-#Constants)
        PutTextInRegister(Number1$,1)
      ElseIf EntryMode=2
        Number2$=Consts$(EventGadget()-#Constants)
        PutTextInRegister(Number2$,2)
      EndIf
      
      
    EndSelect 
    
  Case  #PB_Event_GadgetDrop
    Select EventGadget()
    Case #RegisterA
      Number1$=EventDropText()
      PutTextInRegister(Number1$,1)
      
    Case #RegisterB
      Number2$=EventDropText()
      PutTextInRegister(Number2$,2)
      
    Case #Memories To #Memories+11
      Memories$(EventGadget()-#Memories)=EventDropText()
      A$=EventDropText()
      If Len(A$)>100
        A$=Left(A$,50)+"....."
      EndIf
      GadgetToolTip(EventGadget(),A$)
      
    EndSelect      
    
  Case #PB_Event_CloseWindow 
    If WindowID(#WinMain)
      End
    EndIf
  EndSelect 
  If EventID = #PB_Event_Gadget And EventType() = #PB_EventType_DragStart
    Select EventGadget()
    Case #RegisterA
      DragText(Number1$)
      
    Case #RegisterB
      DragText(Number2$)
      
    Case #RegisterC
      DragText(Number3$)
      
    Case #ResidueReg
      DragText(Number4$)
      
    EndSelect 
    
  EndIf
  
  If HideMouseCoordinates=0
    A$="X: "+Str(WindowMouseX(#WinMain))+" Y: "+Str(WindowMouseY(#WinMain))
    StatusBarText(#StatusBarWinMain,3,A$)
  EndIf
  
EndProcedure

Procedure InsertPrime(Primen.I,EMode.I)
  If EMode=1
    Number1$=Str(primes(Primen))
    PutTextInRegister(Number1$,EMode)
  ElseIf EMode=2
    Number2$=Str(primes(Primen))
    PutTextInRegister(Number2$,EMode)
  EndIf
  
EndProcedure

Procedure MakeRandomstring(NumOfDigits.I,EMode)
  Protected YNReply.I
  If NumOfDigits > 7200
    Repeat
      YNReply = MessageRequester("WARNING!!","Your are about to create a very large number."+Chr(10)+"Do you wish To proceed?",#PB_MessageRequester_YesNo)
      If YNReply = #PB_MessageRequester_No
        ProcedureReturn
      ElseIf YNReply = #PB_MessageRequester_Yes
        Break
      EndIf
    ForEver
  EndIf
  If EMode=1
    Number1$=RandomExactNumberXI(NumOfDigits)
    PutTextInRegister(Number1$,EMode)
  ElseIf EMode=2
    Number2$=RandomExactNumberXI(NumOfDigits)
    PutTextInRegister(Number2$,EMode)
  EndIf
EndProcedure

Procedure MakeRepString(Digit$,NumOfDigits.I,EMode)
  If EMode=1
    Number1$=RSet(Digit$,NumOfDigits,Digit$)
    PutTextInRegister(Number1$,EMode)
  ElseIf EMode=2
    Number2$=RSet(Digit$,NumOfDigits,Digit$)
    PutTextInRegister(Number2$,EMode)
  EndIf  
EndProcedure

Procedure OpenWinMain()
  Protected.D V
  Protected.L Xpos, Ypos, Width, Height, Flags
  Protected Title$
  
  Title$="CalcXI - Extended Precision Integer Calculator. Version: 0.88"
  Xpos=0
  Ypos=0
  Width=800
  Height=600
  Flags= #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget
  If OpenWindow(#WinMain,Xpos,Ypos,Width,Height,Title$,Flags)
    If LoadFont(0, "Arial", 8, #PB_Font_Bold)
      SetGadgetFont(#PB_Default, FontID(0))   ; Set the loaded Arial 16 font as new standard
    EndIf
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Escape, #Escape) 
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_C | #PB_Shortcut_Control, #crtlC)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_V | #PB_Shortcut_Control, #crtlV)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_S | #PB_Shortcut_Alt, #SpeedTextKey)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_D | #PB_Shortcut_Alt, #AltD)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_C | #PB_Shortcut_Alt, #AltC)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_0 , #Key0)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_1 , #Key1)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_2 , #Key2)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_3 , #Key3)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_4 , #Key4)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_5 , #Key5)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_6 , #Key6)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_7 , #Key7)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_8 , #Key8)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_9 , #Key9)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad0 , #Key0)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad1 , #Key1)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad2 , #Key2)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad3 , #Key3)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad4 , #Key4)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad5 , #Key5)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad6 , #Key6)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad7 , #Key7)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad8 , #Key8)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Pad9 , #Key9)
    AddKeyboardShortcut(#WinMain, #PB_Shortcut_Add, #KeyPlus)
    
    
    
    Setup()
    CreateWinMainMenu()
    CreateWinMainGadgets()
    CreateWinMainPopupMenu()
    CreateStatusBar(#StatusBarWinMain,WindowID(#WinMain))
    AddStatusBarField(250)
    AddStatusBarField(250)
    AddStatusBarField(200)
    AddStatusBarField(95)
    
  Else 
    MessageRequester("", "Error") 
    End 
  EndIf 
EndProcedure

Procedure PutTextInRegister(RText$,RRegister.I)
  Protected.L DigitsPerLine, DigitsPerGroup, RTextLen, M, N, SignAdjust, DPAdust
  Protected Line$, RChar$
  
  DigitsPerLine=50
  DigitsPerGroup=5
  RText$=ReplaceString(RText$," ","")
  RTextLen=Len(RText$)
  RChar$=Mid(RText$,M,1)
  SignAdjust=0
  DPAdust=0
  For M=1 To RTextLen
    If RChar$ ="+" Or RChar$ = "-"
      SignAdjust=1 
    ElseIf RChar$ = "."
      DPAdust=1 
    ElseIf RChar$ <"0" Or RChar$ >"9"  
      MessageRequester("ERROR!","Not a decimal integer!")
      ProcedureReturn
    EndIf
  Next M  
  
  Select RRegister
  Case 1
    ClearGadgetItems(#RegisterA)
    
    M=1 
    Repeat
      Line$=""
      N=1 
      Repeat
        Line$+Mid(Rtext$,N+M-1,DigitsPerGroup)+" "
        N+DigitsPerGroup
      Until DigitsPerLine<N
      AddGadgetItem(#RegisterA,-1,Line$)
      M+DigitsPerLine
    Until RTextLen<M 
    Number1$=RText$
    SetGadgetText(#RegAText,"1st number   "+Str(RTextLen-SignAdjust-DPAdust)+" digits. Lines: "+Str((RTextLen-1)/DigitsPerLine+1))
    
  Case 2
    ClearGadgetItems(#RegisterB)
    M=1 
    Repeat
      Line$=""
      N=1 
      Repeat
        Line$+Mid(Rtext$,N+M-1,DigitsPerGroup)+" "
        N+DigitsPerGroup
      Until DigitsPerLine<N
      AddGadgetItem(#RegisterB,-1,Line$)
      M+DigitsPerLine
    Until RTextLen<M 
    Number2$=RText$
    SetGadgetText(#RegBText,"2nd number   "+Str(RTextLen-SignAdjust-DPAdust)+" digits. Lines: "+Str((RTextLen-1)/DigitsPerLine+1))
    
    
  Case 3 ;// Answer
    SetGadgetText(#RegCText,"Result   "+Str(RTextLen-SignAdjust-DPAdust)+" digits. Lines: "+Str((RTextLen-1)/DigitsPerLine+1))
    ClearGadgetItems(#RegisterC)
    M=1 
    Repeat
      Line$=""
      N=1 
      Repeat
        Line$+Mid(Rtext$,N+M-1,DigitsPerGroup)+" "
        N+DigitsPerGroup
      Until DigitsPerLine<N
      AddGadgetItem(#RegisterC,-1,Line$)
      M+DigitsPerLine
    Until RTextLen<M
    Number3$=RText$
    
  Case 4 ;// Residue
    SetGadgetText(#ResidueRegText,"Residue   "+Str(RTextLen-SignAdjust-DPAdust)+" digits. Lines: "+Str((RTextLen-1)/DigitsPerLine+1))
    ClearGadgetItems(#ResidueReg)
    M=1 
    Repeat
      Line$=""
      N=1 
      Repeat
        Line$+Mid(Rtext$,N+M-1,DigitsPerGroup)+" "
        N+DigitsPerGroup
      Until DigitsPerLine<N
      AddGadgetItem(#ResidueReg,-1,Line$)
      M+DigitsPerLine
    Until RTextLen<M
    Number4$=RText$
  EndSelect  
EndProcedure

Procedure Setup()
  Protected.L M
  CreateThread(@PrimeGenerator(),120)
  Tens(0)=1
  For M=1 To 17
    Tens(M)=Tens(M-1)*10
  Next M
  Consts$(0)="11111111111111000000000000011111100";"30079336755242834368044586304632296300765"
  Consts$(1)="111111111111111111";"9872410987691906804355928752240"
  Consts$(2)="99999999999999000"
  Consts$(3)="11111111122222100"
  Consts$(4)="1243617733990094836481" ;// '5' cube + '6' cube = 9x '7' cube
  Consts$(5)="487267171714352336560"
  Consts$(6)="609623835676137297449"
  Consts$(8)="123456789123456789"
  Consts$(9)="123456789"
  Consts$(10)="83206617694944969819571878187315898937987043179"
  Consts$(11)="85133670478476560299637"
EndProcedure



Start: 

OpenWinMain()

Repeat
  Define.L EventID 
  EventID = WaitWindowEvent(20)
  If EventID 
    Select EventWindow() 
    Case #WinMain
      EventWinMain(EventID)
    EndSelect 
  EndIf 
ForEver 


End

DE AA EB
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extended Integer Arithmetic code in PB

Post by davido »

XIEngine:

Code: Select all


;// CalcXI (eXtended Integer) routines.
EnableExplicit

CompilerIf #PB_Compiler_Version > = 510
  Enumeration #PB_Event_FirstCustomValue
    #EventPrimesBegun
    #EventPrimesDone
  EndEnumeration  
CompilerEndIf

#MaxLimbsize = 25 * 1000
#MaxDigits = #MaxLimbsize * 8
#MaxPrime = 99999989

Enumeration ;// Errorcode
  #OverFlow
  #NumberTooLarge
  #DivideByZero
  #NegativePower
  #MaxPrimeExceeded
EndEnumeration

Structure XI
  Array Ra.Q(#MaxLimbsize)
  Sign$
  NumOfLimbs.I
  NumOfDigits.I
  EE.I  ;// May be used if decimals are supported.
EndStructure

Global Dim ResidueXI.Q(#MaxLimbsize)
Global Ra$,Rb$,Rc$ ;// Input numbers for processing
Global LimbSize.I=8, Zeroes$=RSet("0",LimbSize,"0")
Global Integer64bitOverflowThreshold.Q=9220000000000000000
Global Dim Tens.Q(17)
Global Radix.Q=Pow(10,LimbSize), Max12.Q=Pow(10,12)
Global Dim Primes(0)
Global RV1.XI, RV2.XI, RV3.XI, RV4.XI, RV5.XI  ;// Global extended integers for return results from procedures!
Global RV6.XI, RV7.XI, RV8.XI, RV9.XI, RV10.XI ;// 
Global Residue.XI ;// Special extended integer to hold 'mod' or residue from divisions and roots.
Global Residue64Bit.Q

;// Extended integers use the XI structure.
;// Initially Integers were used as this was written on a 64-bit machine. Quads allow its use on 32-bit machines.
;// The LimbSize is chosen as 8 rather than 9. This is compromise to 'increase' speed:
;// Using length 9 the normal method is to 'carrying' after each multiplication to 
;//  prevent overflow. This may be very time consuming; better if could be done at the end.
;// 
;//  If LimbSize 9 is chosen then numbers  longer than 89 will cause overflow. 
;//  If LimbSize 8 is chosen then the limit is 7383 digits.
;//  If LimbSize 7 is chosen then the limit is 32767 digits.
;// Timings (Multiplication) on a core i3 540 @3.07GHz, single thread; 400,000 iterations:
;//  9 digit limb: 620ms - 8 digit limb: 830ms a ~33% increase.
;//  7 digit limb: 890ms all for (89 digits, 400,000 iterations.)
;// Because all the carrying can be done at the end even the 7 digit strategy is a little better than 9 digits and incremental carrying


;- Declarations Functions
Declare AddXI(*Num1.XI,*Num2.XI,*Num3.XI)
Declare CopyXI(*Target.XI,*Source.XI)
Declare DecXI(*Num1)
Declare DivXI(*Num1.XI,*Dvisor.XI,*Num3.XI,*Res.XI)
Declare DoubleXI(*Num1.XI,*Num3.XI)
Declare.I EqualXI(*Num1.XI,*Num2.XI)
Declare GCDXI(*Num1.XI,*Num2.XI,*Num3.XI)
Declare.I GreaterThanXI(*Num1.XI,*Num2.XI)
Declare HalveXI(*Num1.XI,*Num3.XI)
Declare.I HalveInRegisterXI(*Num3.XI)
Declare IncXI(*Num1)
Declare.I LessThanXI(*Num1.XI,*Num2.XI)
Declare FactorialXI(FNum.I,*Num3)
Declare MakeRandomstring(NumOfDigits.I,EMode)
Declare MakeRepString(Digit$,NumOfDigits.I,EMode)
Declare PowerXI(*Num1.XI,PE.I,*Num3.XI)
Declare MultXI(*Num1.XI,*Num2.XI,*Num3.XI)
Declare PBVarToXI(PBInt.I,*Num1.XI)
Declare.S RandomExactNumberXI(NumOfDigits.I)
Declare RandomNumberXI(*Num1.XI,NumOfDigits.I)
Declare SquareXI(*Num1.XI,*Num3.XI)
Declare SqrXI(*Num1.XI,*Ans.XI)
Declare.S StrXI(*Num3.XI)
Declare SubXI(*Num1.XI,*Num2.XI,*Num3.XI)
Declare TestXI()
Declare ValXI(R$,*Num.XI)
Declare ZapLeadZeroesXI(*Num1.XI)
Declare ZeroXI(*Num1.XI)

; Declarations Others
Declare AddXICode(*Num1.XI,*Num2.XI,*Num3.XI)
Declare Errors(ErrorNum.I)
Declare NormaliseLimbs(*Nnum.XI)
Declare PrimeGenerator(*Value)
Declare SpeedStatistics()
Declare.S Str8XI(*Num8)
Declare SubXICode(*Num1.XI,*Num2.XI,*Num3.XI)


Procedure AddXI(*Num1.XI,*Num2.XI,*Num3.XI)
  Protected.L Swapped
  If GreaterThanXI(*Num1.XI,*Num2.XI)=#False
    Swap *Num1, *Num2
    Swapped=1
  EndIf
  *Num3\Sign$ = *Num1\Sign$
  If *Num1\Sign$ = *Num2\Sign$
    AddXICode(*Num1.XI,*Num2.XI,*Num3.XI)
  Else
    SubXICode(*Num1.XI,*Num2.XI,*Num3.XI)
  EndIf
  If Swapped=1
    Swap *Num1, *Num2
  EndIf
EndProcedure

Procedure AddXICode(*Num1.XI,*Num2.XI,*Num3.XI)
  Protected.Q Carry, Dif
  Protected.I X, Adjust
  Carry = 0
  If *Num1\Ra(1) + *Num2\Ra(1) >= Radix
    *Num3\NumOfLimbs = *Num1\NumOfLimbs + 1 : Adjust = 1
  Else
    *Num3\NumOfLimbs = *Num1\NumOfLimbs : Adjust = 0
  EndIf
  Dif = *Num1\NumOfLimbs - *Num2\NumOfLimbs
  
  For X = *Num1\NumOfLimbs To 1 Step -1
    If X - Dif > 0
      *Num3\Ra(X + Adjust)= *Num1\Ra(X) + *Num2\Ra(X - Dif) + Carry
    Else
      *Num3\Ra(X + Adjust) = *Num1\Ra(X) + Carry
    EndIf  
    If *Num3\Ra(X + Adjust) >= Radix
      *Num3\Ra(X + Adjust) - Radix
      Carry = 1
    Else
      Carry = 0
    EndIf
  Next X
  *Num3\Ra(1) + Carry
EndProcedure

Procedure CopyXI(*Target.XI,*Source.XI)
  Protected.L M, ShuffleUpALimb
  *Target\NumOfDigits = *Source\NumOfDigits
  *Target\NumOfLimbs = *Source\NumOfLimbs
  ;// When copying the result of a multiplication ignore a leading zero limb  
  ShuffleUpALimb = 0: If *Source\Ra(1)=0: ShuffleUpALimb=1: EndIf
  For M=1 To *Source\NumOfLimbs
    *Target\Ra(M) = *Source\Ra(M+ShuffleUpALimb)
  Next M
  *Target\Sign$ = *Source\Sign$
EndProcedure

Procedure DecXI(*Num1.XI)
  Protected.I Carry, M
  With *Num1
    M=\NumOfLimbs
    Carry=1
    Repeat
      \Ra(M)-Carry
      Carry=0
      If \Ra(M) < 0
        \Ra(M)+Radix
        Carry=1
      EndIf
    Until Carry=0
  EndWith
EndProcedure

Procedure DivXI(*Num1.XI,*Dvisor.XI,*Ans.XI,*Res.XI)
  Define.Q Carry, Divisor, Dividend
  Define.L M, Diff, TempS
  
  ;// First eliminate the simple case where the divisor is 1 limb
  Carry=0
  If *Dvisor\NumOfLimbs=1
    Divisor=*Dvisor\Ra(1)
    If Divisor = 0
      Errors(#DivideByZero)
      ProcedureReturn
    EndIf
    For M=1 To *Num1\NumOfLimbs
      Dividend=Carry*Radix+*Num1\Ra(M)
      If Dividend<Divisor
        *Ans\Ra(M)=0
        Carry=Dividend
      Else
        *Ans\Ra(M)=Dividend/Divisor
        Carry=Dividend - *Ans\Ra(M)*Divisor ;// This is almost free compared to the use of % (Mod)
      EndIf
    Next M
    *Res\Ra(1)=Carry
    *Res\NumOfLimbs=1
    *Res\NumOfDigits=Len(Str(*Res\Ra(1)))
    *Ans\NumOfLimbs=M-1
  Else
    ;// Covers the cases where the dividend and divisor both have two limbs
    If *Dvisor\NumOfLimbs=2 And *Num1\NumOfLimbs=2
      Dividend=(*Num1\Ra(1)*Radix + *Num1\Ra(2))
      Divisor=(*Dvisor\Ra(1)*Radix + *Dvisor\Ra(2))
      
      *Ans\Ra(2)=Dividend/Divisor
      *Ans\NumOfLimbs=2
      *Res\NumOfLimbs=2
      *Res\Ra(2) = Dividend - *Ans\Ra(2) * Divisor
      If *Res\Ra(2)>Radix
        *Res\Ra(1) = *Res\Ra(2) / Radix
        *Res\Ra(2) - *Res\Ra(1) * Radix
      EndIf
      
    Else ;// The general case where there are more than two limbs. Traditional long division
      Protected.Q  Multiplier, Result, TempR
      Protected.L DivisorLimb, ResLimb, DvisorTooBigFlag, M1, X, AnslimbAdjust, CurLimb, LoopEnd
      Protected.D TestDividend,TestDivisor, MPP
      Protected A$
      CopyXI(*Res.XI,*Num1.XI) ; Keeps the original numbers + free mod!
      
      *Ans\NumOfLimbs = *Res\NumOfLimbs - *Dvisor\NumOfLimbs + 1
      *Ans\NumOfDigits = 0
      *Ans\Ra(1)=0
      CurLimb = 0
      LoopEnd = 0
      
      TestDivisor = *Dvisor\Ra(1) * Radix + *Dvisor\Ra(2) 
      
      Repeat
        If CurLimb <> 0 ;// Division iterations loop start
          DvisorTooBigFlag = 0 ;// To ensure the multiplier is accurate in about 1:100,000,000 cases.
          M=1
          Repeat
            If *Dvisor\Ra(M) < *Res\Ra(CurLimb + M - 1)
              DvisorTooBigFlag = - 1
            ElseIf *Dvisor\Ra(M) > *Res\Ra(CurLimb + M - 1)
              DvisorTooBigFlag = 1
            EndIf
            M + 1
          Until M > *Dvisor\NumOfLimbs Or DvisorTooBigFlag <> 0
          
          ;// 1. Get the multiplier by dividing the 1st 3 limbs of *Res by *Dvisor in FP for more accuracy!
          TestDividend = *Res\Ra(CurLimb) * Radix + *Res\Ra(CurLimb + 1) + 0.5
          MPP = TestDividend / TestDivisor
          If MPP<1 Or DvisorTooBigFlag = 1     ;//  accurate except for the very rare exception.
            Multiplier = Int(MPP * Radix)
          Else
            Multiplier = Int(MPP)
          EndIf
          If Multiplier = Radix
            Multiplier - 1
          EndIf
          
          M=1 
          Repeat
            If *Dvisor\Ra(M) < *Res\Ra(CurLimb+M-1) ;// Aligns the limbs ready for subtraction
              Break
            ElseIf *Dvisor\Ra(M) > *Res\Ra(CurLimb+M-1)
              CurLimb+1
              Break
            EndIf
            M+1
          Until M > *Dvisor\NumOfLimbs  ;// Should never get here - could use forever! 
          
          ;// Multiply *Dvisor by Multiplier and subtract
          Carry = 0
          For M = *Dvisor\NumOfLimbs To 1 Step -1
            ResLimb = CurLimb + M -1
            TempR = *Dvisor\Ra(M) * Multiplier + Carry
            Carry = TempR / Radix 
            *Res\Ra(ResLimb) - (TempR - Carry * Radix)
            If *Res\Ra(ResLimb) < 0
              Carry + 1
              *Res\Ra(ResLimb) + Radix
            EndIf
          Next M
          *Res\Ra(ResLimb - 1) - Carry
          
          While *Res\Ra(ResLimb - 1) < 0 ;// Add Divisor back as Multiplier too big
            Carry = 0
            For M = *Dvisor\NumOfLimbs To 1 Step -1
              ResLimb = CurLimb + M -1
              *Res\Ra(ResLimb) + *Dvisor\Ra(M) + Carry
              If *Res\Ra(ResLimb) >= Radix
                *Res\Ra(ResLimb) - Radix
                Carry = 1
              Else 
                Carry =0
              EndIf
            Next M 
            *Res\Ra(ResLimb - 1) + Carry
            Multiplier - 1
          Wend
        EndIf ;// Division iterations loop end
        
        *Ans\Ra(CurLimb) + Multiplier
        
        While *Res\Ra(CurLimb) = 0 And CurLimb <= *Res\NumOfLimbs
          CurLimb + 1
        Wend
        DvisorTooBigFlag = 0 
        If *Dvisor\NumOfLimbs > *Res\NumOfLimbs - CurLimb +1
          DvisorTooBigFlag = 1
        ElseIf *Dvisor\NumOfLimbs < *Res\NumOfLimbs - CurLimb +1
          DvisorTooBigFlag = -1
        Else
          For M=1 To *Dvisor\NumOfLimbs
            If *Dvisor\Ra(M) > *Res\Ra(CurLimb + M - 1)
              DvisorTooBigFlag = 1
              Break
            ElseIf *Dvisor\Ra(M) < *Res\Ra(CurLimb + M - 1)
              DvisorTooBigFlag = -1
              Break
            Else
            EndIf
          Next M
        EndIf
        
        If DvisorTooBigFlag = 0
          ZeroXI(*Res)
          Multiplier = 1
          *Ans\Ra(*Ans\NumOfLimbs) = Multiplier
          DvisorTooBigFlag = 1
        EndIf
      Until DvisorTooBigFlag =1
    EndIf
  EndIf
  ZapLeadZeroesXI(*Res) 
  ZapLeadZeroesXI(*Ans)
  If *Num1\Sign$ = *Dvisor\Sign$
    *Res\Sign$ = *Dvisor\Sign$
  Else
    *Ans\Sign$ = "-"
    If *Res\Ra(1) > 0
      IncXI(*Ans)
      Carry = 0
      Diff = *Dvisor\NumOfLimbs - *Res\NumOfLimbs
      For X = *Dvisor\NumOfLimbs To 1 Step -1
        TempS = *Dvisor\Ra(X) - *Res\Ra(X - Diff) - Carry
        If TempS < 0
          *Res\Ra(X - Diff) = TempS + Radix
          Carry = 1
        Else
          *Res\Ra(X - Diff) = TempS 
          Carry = 0
        EndIf
      Next X
      *Res\Sign$ = *Dvisor\Sign$
    EndIf
  EndIf
EndProcedure

Procedure DoubleXI(*Num1.XI,*Num3.XI)
  Protected.I M, X, Carry
  CopyXI(*Num3.XI,*Num1.XI)
  With *Num3
    Carry = 0
    For M= \NumOfLimbs To 1 Step -1
      If \Ra(M) >49999999
        \Ra(M) <<1 - Radix + Carry
        Carry = 1
      Else
        \Ra(M) << 1 + Carry
        Carry = 0
      EndIf
    Next M
    If Carry = 1 ;// Extra limb required - so shuffle down and correct 1st limb!
      \NumOfLimbs + 1
      For M = \NumOfLimbs To 2 Step -1
        \Ra(M+1) = \Ra(M)
      Next M
      \Ra(1) = 1
    EndIf
  EndWith    
EndProcedure

Procedure Errors(ErrorNum.I)
  Protected A$
  Select ErrorNum
  Case #OverFlow
    A$ = "Result would overflow" + Chr(10) + "#MaxLimbsize set at: " + Str(#MaxLimbsize)
    A$ + "Limbs (" + Str(#MaxLimbsize * 8) + ": digits)"
  Case #NumberTooLarge
    A$ = "This number is too large and would cause overflow!"
    
  Case #DivideByZero
    A$ = "Attempt to divide by zero!"
    
  Case #NegativePower
    A$ = "Only positive powers allowed!"
    
  Case #MaxPrimeExceeded
    A$ = "Currently Max Prime is: " + Str(#MaxPrime)
    
  EndSelect
  MessageRequester("WARNING!!",A$)
EndProcedure

Procedure.I EqualXI(*Num1.XI,*Num2.XI)
  Protected.I M=1
  If *Num1\NumOfLimbs <> *Num2\NumOfLimbs
    ProcedureReturn 0
  EndIf
  Repeat
    If *Num1\Ra(M) <> *Num2\Ra(M)
      ProcedureReturn 0
    EndIf
    M+1
  Until M>*Num1\NumOfLimbs
  ProcedureReturn 1
EndProcedure

Procedure FactorialXI(FNum.I,*Num3.XI)
  Protected.I FCount, EndLimb, CurrentLimb,S,E
  Protected.Q Carry
  
  *Num3\NumOfLimbs = 1
  *Num3\Ra(1) = 1
  If FNum < 2
    ProcedureReturn
  EndIf
  
  FCount = 2
  Repeat
    EndLimb = *Num3\NumOfLimbs
    For CurrentLimb=1 To EndLimb
      *Num3\Ra(CurrentLimb) * FCount+Carry
      Carry=0
      If *Num3\Ra(CurrentLimb) > Radix
        Carry = *Num3\Ra(CurrentLimb) / Radix
        *Num3\Ra(CurrentLimb) - Carry * Radix
        If CurrentLimb = EndLimb
          *Num3\NumOfLimbs+1
          *Num3\Ra(EndLimb+1)=Carry
          Carry=0
        EndIf
      EndIf
      
    Next CurrentLimb
    FCount+1
    If *Num3\NumOfLimbs = #MaxLimbsize
      Errors(#OverFlow)
      ZeroXI(*Num3)
    EndIf
  Until FCount > FNum
  
  S = 1 : E = *Num3\NumOfLimbs
  
  While E-S > 0 ;// It was easier to make the calculations left to right
    Swap *Num3\Ra(S) , *Num3\Ra(E) ;// Therefore we have to turn the number the right way!
    S+1 : E-1
  Wend
EndProcedure

Procedure GCDXI(*Num1.XI,*Num2.XI,*Num3.XI)
Protected.I M, K, X, Carry, Diff
  Protected.Q GC1, GC2
  
  If LessThanXI(*Num1,*Num2) = 1
    Swap *Num1 , *Num2
  EndIf
  If *Num2\NumOfLimbs < 3
    If *Num1\NumOfLimbs < 3
      If *Num1\NumOfLimbs = 2
        GC1 = *Num1\Ra(1) * Radix + *Num1\Ra(2)
      Else
        GC1 = *Num1\Ra(1)
      EndIf
      If *Num2\NumOfLimbs = 2
        GC2 = *Num2\Ra(1) * Radix + *Num2\Ra(2)
      Else
        GC2 = *Num2\Ra(1)
      EndIf
    Else
      DivXI(*Num1,*Num2,@RV1,@Residue.XI) ;// Makes both 64 bit integers
      If *Num2\NumOfLimbs = 2
        GC2 = *Num2\Ra(1) * Radix + *Num2\Ra(2)
      Else
        GC2 = *Num2\Ra(1)
      EndIf
      If Residue\NumOfLimbs=2
        GC1 = Residue\Ra(1) * Radix + Residue\Ra(2)
      Else
        GC1 = Residue\Ra(1)
      EndIf
      If GC1 = 0 ;// If zero GCD is in *Num2. Otherwise falls through Integer GCD
        CopyXI(*Num3 , *Num2)
        ProcedureReturn *Num3 ;// odd!? Why is this necessary?
      EndIf
    EndIf
    K=1
    While GC1 & 1 =0 And GC2 & 1 =0
      GC1 >> 1 : GC2 >> 1
      K*2
    Wend
    While GC1 & 1 =0 
      GC1 >> 1
    Wend
    While GC2 & 1 =0
      GC2 >> 1
    Wend
    Repeat
      If GC1 < GC2
        Swap GC1 , GC2
      EndIf
      GC1 = GC1 - GC2
      While GC1 & 1 =0 
        GC1 >> 1
      Wend
    Until GC1 = GC2
    If GC1 > Radix
      *Num3\NumOfLimbs = 2
      *Num3\Ra(1) = GC1 / Radix
      *Num3\Ra(2) = GC1 - Radix * *Num3\Ra(1)
    Else
      *Num3\NumOfLimbs = 1
      *Num3\Ra(1) = GC1
    EndIf
    ;// General case XI numbers 
  Else
    K=1
    DivXI(*Num1,*Num2,@RV1,@Residue.XI) ;// Should save time particularly when 1 very much larger than 2
    *Num1 = @Residue

    While *Num1\Ra(*Num1\NumOfLimbs) & 1 = 0 And *Num2\Ra(*Num2\NumOfLimbs) & 1 = 0
      HalveInRegisterXI(*Num1)
      HalveInRegisterXI(*Num2)
      K*2 ;// Remove and save power of 2
    Wend
    While *Num1\Ra(*Num1\NumOfLimbs) & 1 = 0
      HalveInRegisterXI(*Num1)
    Wend
    While *Num2\Ra(*Num2\NumOfLimbs) & 1 = 0
      HalveInRegisterXI(*Num2)
    Wend
    ;// Both now odd

    Repeat
      If LessThanXI(*Num1,*Num2) = 1
        Swap *Num1 , *Num2
      EndIf
  
      ;// Subtract 2 from 1 in-place
      Carry = 0
      Diff = *Num1\NumOfLimbs - *Num2\NumOfLimbs
      For X = *Num1\NumOfLimbs To 1 Step -1
        If (X - Diff) > 0
          *Num1\Ra(X) - *Num2\Ra(X - Diff) - Carry
        Else
          *Num1\Ra(X) - Carry
        EndIf
        If *Num1\Ra(X) < 0
          *Num1\Ra(X) + Radix
          Carry = 1
        Else
          Carry = 0
        EndIf 
      Next X

    While *Num1\Ra(*Num1\NumOfLimbs) & 1 = 0
      HalveInRegisterXI(*Num1)
    Wend

Until EqualXI(*Num1,*Num2)
;//Multiply *Num1 * K --> *Num3
  PBVarToXI(K,*Num1)
  MultXI(*Num1,*Num2,*Num3)
EndIf
EndProcedure

Procedure.I GreaterThanXI(*Num1.XI,*Num2.XI)
  Protected.I GT
  Protected.I M
  GT=0
  If *Num1\NumOfLimbs > *Num2\NumOfLimbs
    ProcedureReturn 1
  ElseIf *Num1\NumOfLimbs < *Num2\NumOfLimbs
    ProcedureReturn 0
  Else
    M=1
    Repeat
      If *Num1\Ra(M) > *Num2\Ra(M)
        GT=1
      EndIf
      M+1
    Until M>*Num1\NumOfLimbs Or GT=1
  EndIf
  ProcedureReturn GT
EndProcedure

Procedure HalveXI(*Num1.XI,*Num3.XI)
  Protected.L M
  CopyXI(*Num3.XI,*Num1.XI)
  With *num3
    For M=1 To *Num1\NumOfLimbs-1
      If *Num3\Ra(M) & 1 =1
        *Num3\Ra(M+1)+Radix
      EndIf
      *Num3\Ra(M)= *Num3\Ra(M)>>1
    Next M
    If *Num3\Ra(M) & 1 =1
      Residue64Bit=1
    Else
      Residue64Bit=0
    EndIf
    *Num3\Ra(M)= *Num3\Ra(M)>>1
    If \Ra(1)=0 ;// Shuffle up
      For M=2 To \NumOfLimbs
        \Ra(M-1)=\Ra(M)
      Next M
      \NumOfLimbs-1
    EndIf
  EndWith
EndProcedure

Procedure.I HalveInRegisterXI(*Num3.XI)
  Protected.L M
  With *Num3
    For M=1 To \NumOfLimbs - 1
      If *Num3\Ra(M) & 1 = 1
        *Num3\Ra(M + 1) + Radix
      EndIf
      *Num3\Ra(M) = *Num3\Ra(M) >> 1
    Next M
    If *Num3\Ra(M) & 1 =1
      Residue64Bit = 1
    Else
      Residue64Bit = 0
    EndIf
    *Num3\Ra(M)= *Num3\Ra(M) >> 1
    If \Ra(1) = 0 ;// Shuffle up
      For M=2 To \NumOfLimbs
        \Ra(M - 1) = \Ra(M)
      Next M
      \NumOfLimbs - 1
    EndIf
  EndWith
  ProcedureReturn Residue64Bit
EndProcedure

Procedure IncXI(*Num1.XI)
  Protected.I Carry, M
  With *Num1
    M = \NumOfLimbs
    Carry = 1
    Repeat
      \Ra(M) + Carry
      Carry = 0
      If \Ra(M) > Radix
        \Ra(M) - Radix
        Carry = 1
      EndIf
      M-1
    Until Carry = 0 Or M = 0
    If Carry = 1
      For M = \NumOfLimbs To 1 Step -1
        \Ra(M + 1) = \Ra(M)
      Next M
      \Ra(1) = 1
      \NumOfLimbs + 1
    EndIf
  EndWith
EndProcedure

Procedure.I LessThanXI(*Num1.XI,*Num2.XI)
  Protected.I M
  If *Num1\NumOfLimbs < *Num2\NumOfLimbs
    ProcedureReturn 1
  ElseIf *Num1\NumOfLimbs > *Num2\NumOfLimbs
    ProcedureReturn 0
  Else
    M=1
    Repeat
      If *Num1\Ra(M) > *Num2\Ra(M)
        ProcedureReturn 0
      ElseIf *Num1\Ra(M) < *Num2\Ra(M)
        ProcedureReturn 1
      EndIf
      M+1
    Until M>*Num2\NumOfLimbs
  EndIf
  ProcedureReturn 1
EndProcedure

Procedure NormaliseLimbs(*Nnum.XI)
  Protected.Q AResult,ACarry,Count
  Protected.L M, Z
  
  AResult=0
  ACarry=0
  For Z=*Nnum\NumOfLimbs To 2 Step -1
    AResult = *Nnum\Ra(Z)
    ACarry = AResult / Radix
    *Nnum\Ra(Z - 1) + ACarry
    *Nnum\Ra(Z) - ACarry * Radix
  Next Z  
  Count = 1
  While *Nnum\Ra(Count) = 0
    Count + 1
  Wend
  If Count = 1 
    ProcedureReturn
  EndIf
  Count - 1
  For M=1 To *Nnum\NumOfLimbs-Count
    *Nnum\Ra(M) = *Nnum\Ra(M+Count)
  Next M
  *Nnum\NumOfLimbs - Count
EndProcedure

Procedure MultXI(*Num1.XI,*Num2.XI,*Num3.XI)
  Protected.L M, P, X, Y, CheckIfCloseToOverflow.I = 0
  *Num3\NumOfLimbs = *Num1\NumOfLimbs + *Num2\NumOfLimbs
  If *Num3\NumOfLimbs > #MaxLimbsize
    Errors(#OverFlow)
    ProcedureReturn
  EndIf
  If (*Num1\NumOfLimbs = 1 And *Num1\Ra(1) = 0) Or (*Num2\NumOfLimbs = 1 And *Num2\Ra(1) = 0)
    ProcedureReturn
  EndIf
  P=0
  If (*Num1\Ra(1)+1) * (*Num2\Ra(1) + 1) < Radix ;//Align the answer to start at limb 1
    *Num3\NumOfLimbs - 1
    P = 1
  EndIf
  If *Num1\Sign$ = *Num2\Sign$
    *Num3\Sign$ = ""
  Else
    *Num3\Sign$ = "-"
  EndIf
  For M=1 To *Num3\NumOfLimbs: *Num3\Ra(M) = 0: Next M
  For X=1 To *Num1\NumOfLimbs
    For Y=1 To *Num2\NumOfLimbs
      *Num3\Ra(X + Y - P) + *Num1\Ra(X) * (*Num2\Ra(Y))
    Next Y
    CheckIfCloseToOverflow + 1 ;// Far quicker than using % to check
    If CheckIfCloseToOverflow = 920 ;// Overflow getting close so:
      NormaliseLimbs(*Num3.XI)
      CheckIfCloseToOverflow = 0
    EndIf
  Next X
  NormaliseLimbs(*Num3.XI) ;// Just to be sure
EndProcedure

Procedure PBVarToXI(PBInt.I,*Num1.XI)
  With *Num1
    If PBInt<0
      \Sign$="-"
      PBInt = -PBInt
    EndIf
    \Ra(1) = PBInt
    \NumOfLimbs = 1
    \NumOfDigits = Len(Str(PBInt)) 
  EndWith
EndProcedure

Procedure PowerXI(*Num1.XI,PE.I,*Num3.XI)
  Protected.Q Carry
  Protected.I PCount, EndLimb, CurrentLimb,S,E,V
  If PE < 0
    Errors(#NegativePower)
    ProcedureReturn
  EndIf
  If *Num1\NumOfLimbs=1
    *Num3\NumOfLimbs = 1
    *Num3\Ra(1) = 1
    If PE < 2
      ProcedureReturn
    EndIf
    If *Num1\Sign$ = "-" And PE & 1 = 1
      *Num3\Sign$ = "-"
    EndIf
    
    V = *Num1\Ra(1)
    PCount = 1
    Repeat
      EndLimb = *Num3\NumOfLimbs
      For CurrentLimb=1 To EndLimb
        *Num3\Ra(CurrentLimb) * V +Carry
        Carry=0
        If *Num3\Ra(CurrentLimb) > Radix
          Carry = *Num3\Ra(CurrentLimb) / Radix
          *Num3\Ra(CurrentLimb) - Carry * Radix
          If CurrentLimb = EndLimb
            *Num3\NumOfLimbs+1
            *Num3\Ra(EndLimb+1)=Carry
            Carry=0
          EndIf
        EndIf
        
      Next CurrentLimb
      PCount+1
    Until PCount > PE
    
    S = 1 : E = *Num3\NumOfLimbs
    While E-S > 0 ;// As for Factorials it was easier to work from left to right.
      Swap *Num3\Ra(S) , *Num3\Ra(E) ;// It is necessary to turn the number round!
      S+1 : E-1
    Wend
  Else ;// General cases where the base has more than one limb
    Protected.I Count
    Protected NumS.XI
    Protected *NumS.XI
    *NumS.XI = @NumS.XI
    If PE=0
      *Num3\NumOfLimbs=1
      *Num3\Ra(1)=1
      ProcedureReturn
    EndIf
    If PE=1
      CopyXI(*Num3.XI,*Num1.XI)
      ProcedureReturn
    EndIf
    
    If PE & 1 =0
      MultXI(*Num1.XI,*Num1.XI,*Num3.XI) ;// Start the multiplications so that the last
      Swap *NumS.XI, *Num3.XI            ;// leaves the result in the correct register!
    Else
      MultXI(*Num1.XI,*Num1.XI,*NumS.XI)
    EndIf
    
    If PE=2
      ProcedureReturn
    EndIf
    
    Count=PE
    While Count>2
      MultXI(*Num1.XI,*NumS.XI,*Num3.XI) 
      Swap *NumS.XI, *Num3.XI
      Count-1
    Wend
  EndIf
EndProcedure

Procedure PrimeGenerator(*Value) ;// Places all primes less than 10^9 in the array Primes()
  Protected.I M,R,C,P,PMax
  CompilerIf #PB_Compiler_Version > = 510
    PostEvent(#EventPrimesBegun) 
  CompilerEndIf

  PMax=50000000
  Dim Odds(PMax)
  ReDim Primes(7000000)
  
  R=1
  Primes(R)=2
  For M=1 To PMax
    If Odds(M)=0
      P=M+M+1
      R+1
      Primes(R)=P
      C=M+P
      While C<PMax
        Odds(C)=1
        C+P
      Wend
    EndIf

  Next M
  ReDim Odds(0)
  CompilerIf #PB_Compiler_Version > = 510
  PostEvent(#EventPrimesDone)
  CompilerEndIf
EndProcedure

Procedure.S RandomExactNumberXI(MaxNumOfDigits.I)
  Protected R$
  Protected.L X
  R$=Str(Random(8)+1)
  X=MaxNumOfDigits/LimbSize+1
  Repeat
    R$+Right(Zeroes$+Str(Random(Radix-1)),LimbSize)
    X-1
  Until X=0
  ProcedureReturn Left(R$,MaxNumOfDigits)
EndProcedure

Procedure SqrXI(*Num1.XI,*Ans.XI)
  Protected.I  NumLimb, AnsLimb, Multiplier, M, X, MaxAnsLimb, TempR, Carry, LimbPtr
  Protected.D TestDividend, TestDivisor, MPP.D
  NumLimb = 1
  AnsLimb = 1
  Multiplier = Int(Sqr(*Num1\Ra(NumLimb) * Radix + *Num1\Ra(NumLimb + 1)))
  *Ans\NumOfLimbs = 1
  *Ans\Ra(AnsLimb) = Multiplier
  TempR = *Ans\Ra(AnsLimb) * Multiplier
  Carry = TempR / Radix
  *Num1\Ra(NumLimb+1) - (TempR - Carry * Radix)
  If *Num1\Ra(NumLimb+1) < 0
    Carry + 1
    *Num1\Ra(NumLimb+1) + Radix
  EndIf
  *Num1\Ra(NumLimb) - Carry
  *Ans\Ra(AnsLimb) + Multiplier ;// 
  
    Repeat
      While *Num1\Ra(NumLimb) = 0 And NumLimb < AnsLimb * 2
        NumLimb + 1
      Wend
      
      TestDivisor = *Ans\Ra(1) * Radix + *Ans\Ra(2)
      TestDividend = *Num1\Ra(NumLimb) * Radix + *Num1\Ra(NumLimb + 1)
      If AnsLimb = 1
        MPP = (TestDividend / TestDivisor) * Radix
        TestDivisor + MPP
      EndIf
      MPP = TestDividend / TestDivisor
      Multiplier = Int(MPP * Radix)
      *Ans\NumOfLimbs + 1
      AnsLimb + 1
      *Ans\Ra(AnsLimb) = Multiplier
      Carry = 0
      For M = *Ans\NumOfLimbs To 1 Step -1
        LimbPtr = NumLimb + M
        TempR = *Ans\Ra(M) * Multiplier + Carry
        Carry = TempR / Radix
        *Num1\Ra(LimbPtr) - (TempR - Carry * Radix)
        If *Num1\Ra(LimbPtr) < 0
          Carry + 1
          *Num1\Ra(LimbPtr) + Radix
        EndIf
      Next M
      *Num1\Ra(LimbPtr - 1) - Carry
      
      *Ans\Ra(AnsLimb) + Multiplier
    Until AnsLimb > 62
 
  HalveInRegisterXI(*Ans)
EndProcedure

Procedure.S StrXI(*Num3.XI)
  Protected.L M
  Protected R$
  M=1
  R$=""
  Repeat
    R$+ Right(Zeroes$+Str(*Num3\Ra(M)),LimbSize)
    M+1
  Until M>*Num3\NumOfLimbs
  ProcedureReturn *Num3\Sign$+LTrim(R$,"0")
EndProcedure

Procedure.S Str8XI(*Num8.XI)
  Protected.L M, OmitLeadZeroes
  Protected R$
  R$=""
  M=0 :OmitLeadZeroes=1
  While OmitLeadZeroes=0 And *Num8\Ra(M)=0 And M<= *Num8\NumOfLimbs
    M+1
  Wend
  OmitLeadZeroes=1
  Repeat
    ;R$+ "M= "+Str(M)+": "
    R$+Right(Zeroes$+Str(*Num8\Ra(M)),LimbSize)+" "
    M+1
  Until M>*Num8\NumOfLimbs
  ProcedureReturn *Num8\Sign$+R$;Trim(R$,"0")
  
EndProcedure

Procedure SpeedStatistics()
  Protected.D D3
  Protected.L Iter, M, N1, N2, N3, N4
  Protected AllString$, N$, Dt.I
  Protected.XI Num1, Num2, Num3, Res
  Iter=100000
  AllString$="All calculations using 100 digit numbers; 100,000 iterations."+Chr(10) 
  ValXI(RandomExactNumberXI(100),@Num1.XI)
  ValXI(RandomExactNumberXI(100),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    MultXI(@Num1.XI,@Num2.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"Multiplying: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  ValXI(RandomExactNumberXI(100),@Num1.XI)
  ValXI(RandomExactNumberXI(100),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    AddXI(@Num1.XI,@Num2.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"Adding: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  ValXI(RandomExactNumberXI(100),@Num1.XI)
  ValXI(RandomExactNumberXI(100),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    SubXI(@Num1.XI,@Num2.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"Subtracting: "+Str(ElapsedMilliseconds()-Dt)+"ms."

  ValXI(RandomExactNumberXI(100),@Num1.XI)
  ValXI(RandomExactNumberXI(100),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    GCDXI(@Num1.XI,@Num2.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"GCD: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    RandomExactNumberXI(100)
  Next M
  AllString$+Chr(10)+"Creating random numbers: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  N$=RandomExactNumberXI(100)
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    ValXI(N$,@Num1.XI) 
  Next M
  AllString$+Chr(10)+"Converting string to number: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  N$=RandomExactNumberXI(100)
  Dt=ElapsedMilliseconds()
  For M=1 To Iter
    CopyXI(@Num2,@Num1.XI) 
  Next M
  AllString$+Chr(10)+"Copying: "+Str(ElapsedMilliseconds()-Dt)+"ms." 
  
  ValXI(RandomExactNumberXI(100),@Num1.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To 100000
    HalveXI(@Num1.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"Halving: "+Str(ElapsedMilliseconds()-Dt)+"ms." 
  
  
  AllString$+Chr(10)+"=---------------------------------="+Chr(10)
  
  
  
  Dt=ElapsedMilliseconds()
  
  FactorialXI(2500,@Num3.XI)
  
  AllString$+Chr(10)+"First 2,500 Factorials: "+Str(ElapsedMilliseconds()-Dt)+"ms." 
  
  ValXI(RandomExactNumberXI(100),@Num1.XI)
  ValXI(RandomExactNumberXI(50),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To 10000
    DivXI(@Num1.XI,@Num2.XI,@Num3.XI,Res.XI)
  Next M
  AllString$+Chr(10)+"Dividing (100 by 50 digit numbers - 10,000 iterations): "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  ValXI(RandomExactNumberXI(100),@Num1.XI)
  ValXI(RandomExactNumberXI(50),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To 10000000
    GreaterThanXI(@Num1.XI,@Num2.XI)
  Next M
  AllString$+Chr(10)+"> (Two 100 digit numbers - 10,000,000 iterations): "+Str(ElapsedMilliseconds()-Dt)+"ms." 
  
  ValXI(RandomExactNumberXI(400),@Num1.XI)
  ValXI(RandomExactNumberXI(400),@Num2.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To 10000
    MultXI(@Num1.XI,@Num2.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"Multiplying (400 digit numbers - 10,000 iterations): "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  ValXI(RandomExactNumberXI(25000),@Num1.XI)
  ValXI(RandomExactNumberXI(25000),@Num2.XI)
  Dt=ElapsedMilliseconds()
  
  ValXI("17",@Num1.XI)
  Dt=ElapsedMilliseconds()
  For M=1 To 1000
    SqrXI(@Num1,@Num2.XI) 
  Next M
  AllString$+Chr(10)+"Square root 17 (500 places  10,000 iterations): "+Str(ElapsedMilliseconds()-Dt)+"ms." 
  
  For M=1 To 10
    MultXI(@Num1.XI,@Num2.XI,@Num3.XI)
  Next M
  AllString$+Chr(10)+"Multiplying (25000 digit numbers - 10 iterations): "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  AllString$+Chr(10)+"=---------------------------------="+Chr(10)
  
  N1=99999999
  N2=33333333
  Dt=ElapsedMilliseconds()
  For M=1 To 10000000
    N3=N1*N2
  Next M
  AllString$+Chr(10)+"Multiplying two 64bit integers - 10,000,000 iterations: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  N1=99999999
  N2=33333333
  Dt=ElapsedMilliseconds()
  For M=1 To 10000000
    D3=N1/N2
    N3=Int(D3)
  Next M
  AllString$+Chr(10)+"Int(Dividing two 64bit Floats) - 10,000,000 iterations: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  N1=99999999
  N2=33333333
  Dt=ElapsedMilliseconds()
  For M=1 To 10000000
    N3=N2/N1
  Next M
  AllString$+Chr(10)+"Dividing two 64bit integers - 10,000,000 iterations: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  N1=99999999
  N2=33333333
  Dt=ElapsedMilliseconds()
  For M=1 To 10000000
    N3=N2%N1
  Next M
  AllString$+Chr(10)+"Remainder (%) after dividing two 64bit integers - 10,000,000 iterations: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  N1=99999999
  N2=22222222
  N3=4
  Dt=ElapsedMilliseconds()
  For M=1 To 10000000
    N4=N1-N2*N3
  Next M
  AllString$+Chr(10)+"Remainder (-*) after dividing two 64bit integers - 10,000,000 iterations: "+Str(ElapsedMilliseconds()-Dt)+"ms."
  
  
  MessageRequester("CalcXI Speed Statistics",AllString$)
  
EndProcedure

Procedure SubXI(*Num1.XI,*Num2.XI,*Num3.XI)
  Protected.L  Swapped = 0
  If GreaterThanXI(*Num1.XI,*Num2.XI)=#False
    Swap *Num1, *Num2
    Swapped=1
  EndIf
  
  If *Num1\Sign$ = *Num2\Sign$
    If Swapped = 1
      If *Num2\Sign$ = "-"
        *Num3\Sign$ = ""
      Else
        *Num3\Sign$ = "-"
      EndIf
    Else
      *Num3\Sign$ = *Num1\Sign$
    EndIf
    SubXICode(*Num1.XI,*Num2.XI,*Num3.XI)
  Else
    If Swapped =1
      *Num3\Sign$ = *Num2\Sign$
    Else
      *Num3\Sign$ = *Num1\Sign$
    EndIf
    AddXICode(*Num1.XI,*Num2.XI,*Num3.XI)
  EndIf
  If Swapped=1
    Swap *Num1, *Num2
  EndIf
EndProcedure

Procedure SubXICode(*Num1.XI,*Num2.XI,*Num3.XI)
  Protected.L L1, L2, Carry, Diff, X, TempS
  Carry = 0
      *Num3\NumOfLimbs = *Num1\NumOfLimbs
      Diff = *Num1\NumOfLimbs - *Num2\NumOfLimbs
      For X = *Num1\NumOfLimbs To 1 Step -1
        If (X - Diff) > 0
          TempS = *Num1\Ra(X) - *Num2\Ra(X - Diff) - Carry
        Else
          TempS = *Num1\Ra(X) - Carry
        EndIf
        If TempS < 0
          *Num3\Ra(X) = TempS + Radix
          Carry = 1
        Else
          *Num3\Ra(X) = TempS
          Carry = 0
        EndIf
      Next X
  If *Num3\Ra(1)=0
    ZapLeadZeroesXI(*Num3)
  EndIf
  If *Num3\Ra(1)=0
    *Num3\Sign$ = ""
  EndIf
EndProcedure

Procedure ValXI(R$,*Num.XI)
  Protected.L LenOfLimb1, M, Sign
  If Len(R$) > #MaxLimbsize * 8
    Errors(#NumberTooLarge)
    ProcedureReturn
  EndIf
  With *Num
    \EE = FindString(R$,".")
    If \EE > 0
      \EE - Len(R$)
      R$=StringField(R$,1,".")+StringField(R$,2,".")
    EndIf
    If Left(R$,1)="-"
      \Sign$="-"
      Sign=1
      R$=Mid(R$,2)
    Else
      \Sign$=""
      Sign=0
    EndIf
    
    \NumOfDigits=Len(R$)
    LenOfLimb1=\NumOfDigits%LimbSize
    If LenOfLimb1=0
      LenOfLimb1=LimbSize
    EndIf
    
    \NumOfLimbs=1
    \Ra(\NumOfLimbs)=Val(Left(R$,LenOfLimb1))
    
    M=LenOfLimb1+1
    While M<\NumOfDigits
      \NumOfLimbs+1
      \Ra(\NumOfLimbs)=Val(Mid(R$,M,LimbSize))
      M+LimbSize
    Wend
  EndWith  
EndProcedure

Procedure ZapLeadZeroesXI(*Num1.XI)
  Protected.I M,X
  With  *Num1
    M=1
    While \Ra(M) = 0 And M <= \NumOfLimbs
      M+1
    Wend
    If M > \NumOfLimbs
      \NumOfLimbs = 1
    Else
      For X=M To \NumOfLimbs
        \Ra(X-M+1) = \Ra(X)
      Next X
      \NumOfLimbs - M +1
      \NumOfDigits = \NumOfLimbs * 8 - 8 + Len(Str(\Ra(1)))
    EndIf
  EndWith
EndProcedure

Procedure ZeroXI(*Num1.XI)
  Protected.I M
  With *Num1
    \EE = 0
    \Sign$=""
    \NumOfDigits=0
    For M=1 To \NumOfLimbs
      \Ra(M)=0
    Next M
    \NumOfLimbs=0
  EndWith
EndProcedure

DE AA EB
Post Reply