Extended Integer Arithmetic code in PB
Extended Integer Arithmetic code in PB
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
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
Re: Extended Integer Arithmetic code in PB
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
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
Re: Extended Integer Arithmetic code in PB
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
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
Re: Extended Integer Arithmetic code in PB
I find the procedure call good, you can define a variable with Name.XI.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)
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
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 more ― Typeface - Sprite-based font include/module
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
Re: Extended Integer Arithmetic code in PB
Stargate thank you for your comments.
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. 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
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. 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
Re: Extended Integer Arithmetic code in PB
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.
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 more ― Typeface - Sprite-based font include/module
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
Re: Extended Integer Arithmetic code in PB
Is it not been replaced by a fast single asm call?STARGÅTE wrote:right, the % operation is not fast.
Re: Extended Integer Arithmetic code in PB
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 ...
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 more ― Typeface - Sprite-based font include/module
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
- Michael Vogel
- Addict
- Posts: 2678
- Joined: Thu Feb 09, 2006 11:27 pm
- Contact:
Re: Extended Integer Arithmetic code in PB
Maybe you like to compare this code to VNF, there are some similarities, so some speed tests could be interesting as well (25000!)...
Re: Extended Integer Arithmetic code in PB
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
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
Re: Extended Integer Arithmetic code in PB
Oh, I like this things !
Divide (a nightmare !): Please test "123456789123456789 / 123456789".
Divide (a nightmare !): Please test "123456789123456789 / 123456789".
Re: Extended Integer Arithmetic code in PB
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
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
Re: Extended Integer Arithmetic code in PB
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:
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
Re: Extended Integer Arithmetic code in PB
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