MathgeomGLS
11234 строки · 324.2 Кб
1{----------------------------------------------------------------------------}
2{ }
3{ File: Velthuis.BigIntegers.pas }
4{ Function: A big integer implementation, with critical parts written in }
5{ Win32 or Win64 assembler, or "Pure Pascal" for other }
6{ platforms, or if explicitly specified. }
7{ Language: Delphi version XE2 or later }
8{ Author: Rudy Velthuis }
9{ Copyright: (c) 2015,2016,2017 Rudy Velthuis }
10{ }
11{ For tests, see BigIntegerDevelopmentTests.dproj. The data }
12{ for these tests are generated by a C# program, in the }
13{ DataGenerators\BigIntegers\BigIntegerTestGenerator }
14{ subdirectory, or by a Java program, in the }
15{ DataGenerators\BigIntegers\Java\BigIntegerTestDataGenerator }
16{ subdirectory. }
17{ }
18{ Credits: Thanks to Peter Cordes, Nils Pipenbrinck and Johan Bontes for }
19{ their help on StackOverflow: }
20{ - http://stackoverflow.com/a/32298732/95954 }
21{ - http://stackoverflow.com/a/32087095/95954 }
22{ - http://stackoverflow.com/a/32084357/95954 }
23{ }
24{ Thanks to Agner Fog for his excellent optimization guides. }
25{ }
26{ Literature: 1. Donald Knuth, "The Art Of Computer Programming", 2nd ed. }
27{ Vol I-III. }
28{ 2. Karl Hasselström, }
29{ "Fast Division of Large Integers - A Comparison of }
30{ Algorithms" }
31{ bioinfo.ict.ac.cn/~dbu/AlgorithmCourses/ }
32{ Lectures/Hasselstrom2003.pdf }
33{ 3. Richard P. Brent and Paul Zimmermann, }
34{ "Modern Computer Arithmetic" }
35{ http://arxiv.org/pdf/1004.4710v1.pdf }
36{ https://members.loria.fr/PZimmermann/mca/mca-cup-0.5.9.pdf }
37{ 4. Christoph Burnikel, Joachim Ziegler }
38{ "Fast Recursive Division" }
39{ cr.yp.to/bib/1998/burnikel.ps }
40{ 5. Hacker's Delight, e.g. }
41{ http://www.hackersdelight.org/basics2.pdf }
42{ 6. Wikipedia }
43{ https://en.wikipedia.org }
44{ 7. Rosetta Code }
45{ http://rosettacode.org/wiki/Rosetta_Code }
46{ 8. Michael Malenkov, Christopher J. Dutra, Marco T. Morazán }
47{ "A New Bignum Multiplication Algorithm" }
48{ http://prolangs.cs.vt.edu/rutgers/meetings/ }
49{ masplas06/papers/2_Malenkov.pdf }
50{ }
51{ -------------------------------------------------------------------------- }
52{ }
53{ License: Redistribution and use in source and binary forms, with or }
54{ without modification, are permitted provided that the }
55{ following conditions are met: }
56{ }
57{ * Redistributions of source code must retain the above }
58{ copyright notices, this list of conditions and the }
59{ following disclaimer. }
60{ * Redistributions in binary form must reproduce the above }
61{ copyright notice, this list of conditions and the following }
62{ disclaimer in the documentation and/or other materials }
63{ provided with the distribution. }
64{ }
65{ Disclaimer: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" }
66{ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT }
67{ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND }
68{ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO }
69{ EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE }
70{ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, }
71{ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
72{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, }
73{ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED }
74{ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT }
75{ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) }
76{ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF }
77{ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. }
78{ }
79{----------------------------------------------------------------------------}
80
81{----------------------------------------------------------------------------}
82{ Corrections: }
83{ }
84{ 2015-11-28: Changed calls to System.@GetMem in System.AllocMem. }
85{ GetMem does not clear memory. This caused occasional bad }
86{ results. }
87{ }
88{ 2015-11-29: Changed all references to CPUX86 and CPUX64 to CPU32BITS }
89{ and CPU64BITS, respectively. The former are not defined }
90{ outside MS Windows. }
91{ }
92{ 2016-01-24: Changed internals of DivMod. There could be problems if the }
93{ dividend and the quotient were identical. }
94{ }
95{ 2016-01-24: Changed ToString(Base). The old slow method simply }
96{ divided the entire BigInteger by base and added the }
97{ remainder as digit, digit by digit. }
98{ This is still available as ToStringClassic, for debugging }
99{ purposes. ToStringClassic will be removed later on, if new }
100{ methods are secured. }
101{ The newer methods do either shift (base 2, 4, 16), convert }
102{ entire limbs or, for large values, use a divide-and-conquer }
103{ algorithm. }
104{ }
105{ 2016-02-02: Changed PUREPASCAL version of InternalDivMod, removing an }
106{ obscure, seldom bug that was caused by an uncaught overflow. }
107{ }
108{ 2016-02-03: Added operator Explicit(BigInteger): string, which simply }
109{ calls ToString to generate the result. }
110{ }
111{ 2016-08-23: Changed Remainder(... UInt32) and Remainder(... UInt16). }
112{ InternalDivMod32 did not return True, and on False, }
113{ Remainder gave wrong error message. }
114{ }
115{ 2016-12-27: Added ModInverse. }
116{ Optimized and renamed MakeLength to AllocNewMagnitude. }
117{ }
118{ 2016-12-29: Changed return type of Compare() to Integer. Using }
119{ TValueSign made inlining several comparison operators }
120{ harder, since that required System.Math. }
121{ }
122{ 2016-12-30: Changed implementation of ModInverse to 20% faster version. }
123{ }
124{ 2017-01-08: Updated Pow to remove trailing bits before exponentiation }
125{ and putting them back afterward. }
126{ }
127{ 2017-01-13: DivModKnuth now eliminates common trailing zero limbs }
128{ before attempting the division. }
129{ So $123400000000000000000 div $4560000000000000000 is }
130{ performed as $1234 div $456. The mod result is of course }
131{ corrected. }
132{ }
133{ 2017-01-15: Reworked SetBit, ClearBit and FlipBit. }
134{ }
135{ 2017-07-14: Changed all manual aligns to use .ALIGN 16 }
136{ }
137{ 2017-08-17: Removed buffer overflow bugs in InternalMultiplyAndAdd16 and }
138{ UncheckedDivModKnuth. Also removed bug from }
139{ InternalShiftLeft. All after using FastMM4 in full debug }
140{ mode (which adds extra footer bytes to every allocation). }
141{ }
142{ 2017-08-18: Some more buffer overruns (badly dimensioned magnitudes) }
143{ removed. }
144{ }
145{ 2017-08-22: Improved speed of Win32 InternalAddPurePascal by using 16 }
146{ bit additions, removing need for costly carry emulation. }
147{ }
148{----------------------------------------------------------------------------}
149{ Newer changes can be read from the GitHub repository: }
150{ https://github.com/rvelthuis/DelphiBigNumbers }
151{----------------------------------------------------------------------------}
152
153unit Velthuis.BigIntegers;
154
155{ TODO: modular arithmetic. Modular division and multiplication. Barrett, Montgomery, etc. }
156{ TODO: Better parsing. Recursive parsing (more or less the reverse of recursive routine for ToString) for normal
157bases, shifting for bases 2, 4 and 16. This means that normal bases are parsed BaseInfo.MaxDigits at a time. }
158{ TODO: InternalMultiply (basecase) Win32: use MMX instead of plain registers? Also remove trailing loop, make 4
159completely separate loop+trail parts? }
160{ TODO: InternalMultiply: consider algorithm by Malenkov et al. In short, this adds columns first, instead of rows. }
161{ TODO: Check if old NthRoot (binary search) is perhaps better for the relatively small values tested here. It seems
162to have become slower. }
163
164interface
165
166uses
167CompilerAndRTLVersions, Velthuis.RandomNumbers, System.SysUtils, System.Math;
168
169// --- User settings ---
170
171//------------------------------------------------------------------------------------------------------------------//
172// Setting PUREPASCAL forces the use of plain Object Pascal for all routines, i.e. no assembler is used. //
173//------------------------------------------------------------------------------------------------------------------//
174
175{ $DEFINE PUREPASCAL}
176
177
178//------------------------------------------------------------------------------------------------------------------//
179// Setting RESETSIZE forces the Compact routine to shrink the dynamic array when that makes sense. //
180// This can slow down code a little. //
181//------------------------------------------------------------------------------------------------------------------//
182
183{ $DEFINE RESETSIZE}
184
185
186//------------------------------------------------------------------------------------------------------------------//
187// If set, none of the public methods modifies the instance it is called upon. //
188// If necessary, a new instance is returned. //
189//------------------------------------------------------------------------------------------------------------------//
190
191{$DEFINE BIGINTEGERIMMUTABLE}
192
193
194//------------------------------------------------------------------------------------------------------------------//
195// EXPERIMENTAL is set for code that tries something new without deleting the original code yet. //
196// Undefine it to get the original code. //
197//------------------------------------------------------------------------------------------------------------------//
198
199{ $DEFINE EXPERIMENTAL}
200
201
202{$IFDEF BIGINTEGERIMMUTABLE}
203{$UNDEF RESETSIZE}
204{$ENDIF}
205
206// --- Permanent settings ---
207
208{$OPTIMIZATION ON}
209{$STACKFRAMES OFF}
210{$INLINE ON}
211
212{$IF CompilerVersion >= CompilerVersionDelphiXE3}
213{$LEGACYIFEND ON}
214{$IFEND}
215
216{$IF CompilerVersion >= CompilerVersionDelphiXE}
217{$CODEALIGN 16}
218{$ALIGN 16}
219{$IFEND}
220
221{$IF CompilerVersion < CompilerVersionDelphiXE8}
222{$IF (DEFINED(WIN32) OR DEFINED(CPUX86)) AND NOT DEFINED(CPU32BITS)}
223{$DEFINE CPU32BITS}
224{$IFEND}
225{$IF (DEFINED(WIN64) OR DEFINED(CPUX64)) AND NOT DEFINED(CPU64BITS)}
226{$DEFINE CPU64BITS}
227{$IFEND}
228{$IFEND}
229
230{$IF SizeOf(Extended) > SizeOf(Double)}
231{$DEFINE HasExtended}
232{$IFEND}
233
234{$IF NOT DECLARED(PAnsiChar)}
235{$DEFINE NoAnsi}
236{$IFEND}
237
238// Assembler is only supplied for Windows targets. For other targets, PUREPASCAL must be defined.
239{$IF not defined(PUREPASCAL) and not defined(MSWINDOWS)}
240{$DEFINE PUREPASCAL}
241{$IFEND}
242
243const
244{$IFDEF PUREPASCAL}
245PurePascal = True;
246{$ELSE}
247PurePascal = False;
248{$ENDIF}
249
250{$IFDEF EXPERIMENTAL}
251ExperimentalCode = True;
252{$ELSE}
253ExperimentalCode = False;
254{$ENDIF}
255
256// This assumes an unroll factor of 4. Unrolling more (e.g. 8) does not improve performance anymore.
257// That was tested and removed again.
258CUnrollShift = 2;
259CUnrollIncrement = 1 shl CUnrollShift;
260CUnrollMask = CUnrollIncrement - 1;
261
262type
263TNumberBase = 2..36; // Number base or radix.
264
265{$IF not declared(TRandom32Proc)}
266TRandom32Proc = function: UInt32;
267TRandomizeProc = procedure(NewSeed: UInt64);
268{$IFEND}
269
270PLimb = ^TLimb; // Knuth calls them "limbs".
271TLimb = type UInt32; // FWIW, I also like the recently spotted term "bigit".
272TMagnitude = TArray<TLimb>; // These BigIntegers use sign-magnitude format, hence the name.
273
274// BigInteger uses a sign-magnitude representation, i.e. the magnitude is always interpreted as an
275// unsigned big integer, while the sign bit represents the sign. Currently, the sign bit is stored as the
276// top bit of the FSize member.
277
278PBigInteger = ^BigInteger;
279BigInteger = record
280public
281{$REGION 'public constants, types and variables'}
282type
283/// <summary>TRoundingMode governs which rounding mode is used to convert from Double to BigInteger.</summary>
284/// <param name="rmTruncate">Truncates any fraction</param>
285/// <param name="rmSchool">Rounds any fraction >= 0.5 away from zero</param>
286/// <param name="rmRound">Rounds any fraction > 0.5 away from zero</param>
287TRoundingMode = (rmTruncate, rmSchool, rmRound);
288
289TNumberBaseInfo = record
290MaxPower: NativeUInt;
291MaxDigits: Integer;
292PowerOfTwo: Boolean;
293MaxFactor: UInt32;
294end;
295
296class var
297MinusOne: BigInteger;
298Zero: BigInteger;
299One: BigInteger;
300Ten: BigInteger;
301
302const
303{$IFDEF BIGINTEGERIMMUTABLE}
304Immutable = True;
305{$ELSE}
306Immutable = False;
307{$ENDIF}
308
309CapacityMask = High(Integer) - 3; // Mask ensuring that FData lengths are a multiple of 4, e.g. $7FFFFFFC
310SizeMask = High(Integer); // Mask to extract size part of FSize member, e.g. $7FFFFFFF
311SignMask = Low(Integer); // Mask to extract sign bit of FSize member, e.g. $80000000
312
313{$IFDEF PUREPASCAL}
314{$IFDEF CPU64BITS} // 64PP = 64 bit, Pure Pascal
315KaratsubaThreshold = 80; // Checked
316ToomCook3Threshold = 272; // Checked
317BurnikelZieglerThreshold = 91; // Checked
318BurnikelZieglerOffsetThreshold = 5; // Unchecked
319KaratsubaSqrThreshold = 48; // Unchecked
320{$ELSE CPU32BITS} // 32PP = 32 bit, Pure Pascal
321KaratsubaThreshold = 40; // Checked
322ToomCook3Threshold = 144; // Checked
323BurnikelZieglerThreshold = 91; // Checked
324BurnikelZieglerOffsetThreshold = 5; // Unchecked
325KaratsubaSqrThreshold = 48; // Unchecked
326{$ENDIF CPU64BITS}
327{$ELSE !PUREPASCAL}
328{$IFDEF CPU64BITS} // 64A = 64 bit, Assembler
329KaratsubaThreshold = 128; // Checked
330ToomCook3Threshold = 1024; // Checked
331BurnikelZieglerThreshold = 160; // Checked
332BurnikelZieglerOffsetThreshold = 80; // Unchecked
333KaratsubaSqrThreshold = 256; // Unchecked
334{$ELSE CPU32BITS} // 32A = 32 bit, Assembler
335KaratsubaThreshold = 64; // Checked
336ToomCook3Threshold = 256; // Checked
337BurnikelZieglerThreshold = 80; // Checked
338BurnikelZieglerOffsetThreshold = 40; // Unchecked
339KaratsubaSqrThreshold = 128; // Unchecked
340{$ENDIF CPU64BITS}
341{$ENDIF PUREPASCAL}
342
343RecursiveToStringThreshold = 4; // Checked
344ToomCook3SqrThreshold = 216; // Unchecked
345{$ENDREGION}
346
347{$REGION 'public methods'}
348
349// -- Constructors --
350
351/// <summary>Initializes class variables before first use.</summary>
352class constructor Initialize;
353
354/// <summary>Creates a new BigInteger from the data in limbs and the sign specified in Negative.</summary>
355/// <param name="Limbs">data for the magnitude of the BigInteger. The data is interpreted as unsigned,
356/// and comes low limb first.</param>
357/// <param name="Negative">Indicates if the BigInteger is negative.</param>
358constructor Create(const Limbs: array of TLimb; Negative: Boolean); overload;
359
360/// <summary>Creates a new BigInteger from the data in limbs and the sign specified in Negative.</summary>
361/// <param name="Magnitude">data for the magnitude of the BigInteger. The data is interpreted as unsigned,
362/// and comes low limb first.</param>
363/// <param name="Negative">Indicates if the BigInteger is negative.</param>
364constructor Create(const Magnitude: TMagnitude; Negative: Boolean); overload;
365
366/// <summary>Creates a new BigInteger with the same value as the specified BigInteger.</summary>
367constructor Create(const Value: BigInteger); overload;
368
369/// <summary>Creates a new BigInteger with the value of the specified Integer.<summary>
370constructor Create(const Value: Int32); overload;
371
372/// <summary>Creates a new BigInteger with the value of the specified Cardinal.<summary>
373constructor Create(const Value: UInt32); overload;
374
375/// <summary>Creates a new BigInteger with the value of the specified 64 bit integer.<summary>
376constructor Create(const Value: Int64); overload;
377
378/// <summary>Creates a new BigInteger with the value of the specified Integer.<summary>
379constructor Create(const Value: UInt64); overload;
380
381/// <summary>Creates a new BigInteger with the integer value of the specified Double.</summary>
382constructor Create(const Value: Double); overload;
383
384{$IFNDEF NoAnsi}
385/// <summary>Creates a new BigInteger with the value of the specified string.</summary>
386constructor Create(const Value: PAnsiChar); overload;
387{$ENDIF}
388
389/// <summary>Creates a new BigInteger with the value of the specified string.</summary>
390constructor Create(const Value: PWideChar); overload;
391
392/// <summary>Creates a new BigInteger from the value in the byte array.
393/// The byte array is considered to be in two's complement.</summary>
394/// <remarks>This is the complementary function of ToByteArray</remarks>
395constructor Create(const Bytes: array of Byte); overload;
396
397/// <summary>Creates a new random BigInteger of the given size. Uses the given IRandom to
398/// generate the random value.</summary>
399constructor Create(NumBits: Integer; const Random: IRandom); overload;
400
401/// <summary>Creates a new random BigInteger of the given size. Uses the given Random32Proc function to
402/// generate the random value.</summary>
403constructor Create(NumBits: Integer; Random: TRandom32Proc); overload;
404
405
406// -- Global numeric base related functions --
407
408/// <summary>Sets the global numeric base for big integers to 10.</summary>
409/// <remarks>The global numeric base is used for input or output if there is no override in the input string or
410/// the output function.</remarks>
411class procedure Decimal; static;
412
413/// <summary>Sets the global numeric base for big integers to 16.</summary>
414/// <remarks>The global numeric base is used for input or output if there is no override in the input string or
415/// the output function.</remarks>
416class procedure Hexadecimal; static;
417
418/// <summary>Sets the global numeric base for big integers to 16.</summary>
419/// <remarks>The global numeric base is used for input or output if there is no override in the input string or
420/// the output function.</remarks>
421class procedure Hex; static;
422
423/// <summary>Sets the global numeric base for big integers to 2.</summary>
424/// <remarks>The global numeric base is used for input or output if there is no override in the input string or
425/// the output function.</remarks>
426class procedure Binary; static;
427
428/// <summary>Sets the global numeric base for big integers to 8.</summary>
429/// <remarks>The global numeric base is used for input or output if there is no override in the input string or
430/// the output function.</remarks>
431class procedure Octal; static;
432
433
434// -- String input functions --
435
436/// <summary>Tries to parse the specified string into a valid BigInteger value in the specified numeric base.
437/// Returns False if this failed.</summary>
438/// <param name="S">The string that represents a big integer value in the specified numeric base.</param>
439/// <param name="ABase">The numeric base that is assumed when parsing the string. Valid values are 2..36.</param>
440/// <param name="AValue">The resulting BigInteger, if the parsing succeeds. AValue is undefined if the
441/// parsing fails.</param>
442/// <returns>Returns True if S could be parsed into a valid BigInteger in AVaLue. Returns False on failure.</returns>
443class function TryParse(const S: string; ABase: TNumberBase; var AValue: BigInteger): Boolean; overload; static;
444
445// -------------------------------------------------------------------------------------------------------------//
446// Note: most of the parse format for BigIntegers was taken from or inspired by Common Lisp (e.g. '%nnR' or //
447// '_'), some was inspired by other languages, including Delphi (e.g. the '$ 'for hex values), some was //
448// something I prefer (e.g. '0k' additional to '0o' for octal format). It should be usable in Delphi as well //
449// as in C++Builder, as it contains the default formats for integer values in these languages too. //
450// -- Rudy Velthuis. //
451//--------------------------------------------------------------------------------------------------------------//
452
453/// <summary>Tries to parse the specified string into a valid BigInteger value in the default BigInteger
454/// numeric base.</summary>
455/// <param name="S">The string that represents a big integer value in the default numeric base, unless
456/// specified otherwise. See <see cref="BigInteger.Base" /></param>
457/// <param name="Value">The resulting BigInteger, if the parsing succeeds. Value is undefined if the parsing
458/// fails.</param>
459/// <returns>Returns True if S could be parsed into a valid BigInteger in Res. Returns False on failure.</returns>
460/// <remarks>
461/// <para>To make it easier to increase the legibility of large numbers, any '_' in the numeric string
462/// will completely be ignored, so '1_000_000_000' is exactly equivalent to '1000000000'.</para>
463/// <para>The string to be parsed is considered case insensitive, so '$ABC' and '$abc' represent exactly
464/// the same value.</para>
465/// <para>The format of a string to be parsed is as follows:</para>
466/// <para><c>[sign][base override]digits</c></para>
467/// <para>
468/// <param name="sign">This can either be '-' or '+'. It will make the BigInteger negative or
469/// positive, respectively. If no sign is specified, a positive BigInteger is generated.</param>
470/// <param name="base override">There are several ways to override the default numeric base.
471/// <para>Specifying '0x' or '$' here will cause the string to be interpreted as representing a
472/// hexadecimal (base 16) value.</para><para>Specifying '0b' will cause it to be interpreted as
473/// binary (base 2).</para><para>Specifying '0d' will cause it to be interpreted as
474/// decimal (base 10).</para>
475/// <para>Specifying '0o' or '0k' will cause it to be interpreted as octal (base 8).</para>
476/// <para>Finally, to specify any base,
477/// using an override in the format '%nnR' (R for radix) will cause the number to be interpreted to be
478/// in base 'nn', where 'nn' represent one or two decimal digits. So '%36rRudyVelthuis' is a valid
479/// BigInteger value with base 36.</para>
480/// </param>
481/// </para>
482/// </remarks>
483class function TryParse(const S: string; var Value: BigInteger): Boolean; overload; static;
484
485/// <summary>Parses the specified string into a BigInteger, using the default numeric base.</summary>
486class function Parse(const S: string): BigInteger; static;
487
488
489// -- Sign related functions --
490
491/// <summary>Returns True if the BigInteger is zero.</summary>
492function IsZero: Boolean; inline;
493
494/// <summary>Returns True if the BigInteger is negative (< 0).</summary>
495function IsNegative: Boolean; inline;
496
497/// <summary>Returns True if the BigInteger is positive (> 0).</summary>
498function IsPositive: Boolean; inline;
499
500/// <summary>Returns True if the BigInteger is even (0 is considered even too).</summary>
501function IsEven: Boolean; inline;
502
503/// <summary>Returns True if the magnitude of the BigInteger value is exactly a power of two.</summary>
504function IsPowerOfTwo: Boolean;
505
506/// <summary>Returns True if the BigInteger represents a value of 1.</summary>
507function IsOne: Boolean;
508
509
510// -- Bit fiddling --
511
512/// <summary>Tests if the bit at the given bit index is set.</summary>
513/// <remarks>If the index is outside the magnitude, the bit value is calculated: if the BigInteger is
514/// negative, it is assumed to be set, otherwise it is assumed to be clear.</remarks>
515function TestBit(Index: Integer): Boolean;
516
517/// <summary>Returns a new BigInteger which is a copy of the current one, but with the bit at the given index
518/// set. If necessary, the new BigInteger is expanded.</summary>
519function SetBit(Index: Integer): BigInteger;
520
521/// <summary>Returns a new BigInteger which is a copy of the current one, but with the bit at the given index
522/// cleared. If necessary, the new BigInteger is expanded.</summary>
523function ClearBit(Index: Integer): BigInteger;
524
525/// <summary>Returns a new BigInteger which is a copy of the current one, but with the bit at the given index
526/// toggled. If necessary, the new BigInteger is expanded.</summary>
527function FlipBit(Index: Integer): BigInteger;
528
529
530// -- String output functions --
531
532/// <summary>Returns the string interpretation of the specified BigInteger in the default numeric base,
533/// see <see cref="BigInteger.Base" />.
534/// </summary>
535function ToString: string; overload;
536
537/// <summary>Returns the string interpretation of the specified BigInteger in the specified numeric base.</summary>
538function ToString(Base: Integer): string; overload;
539
540/// <summary>Old, slow, but secure routine.</summary>
541/// <remarks>This should only be used for debugging purposes. May be removed anytime.
542/// For regular code, use <c>ToString(Base)</c>.</remarks>
543function ToStringClassic(Base: Integer): string;
544
545/// <summary>Returns the string interpretation of the specified BigInteger in numeric base 10. Equivalent
546/// to ToString(10).</summary>
547function ToDecimalString: string;
548
549/// <summary>Returns the string interpretation of the specified BigInteger in numeric base 16. Equivalent
550/// to ToString(16).</summary>
551function ToHexString: string;
552
553/// <summary>Returns the string interpretation of the specified BigInteger in numeric base 2. Equivalent
554/// to ToString(2).</summary>
555function ToBinaryString: string;
556
557/// <summary>Returns the string interpretation of the specified BigInteger in numeric base 8. Equivalent
558/// to ToString(8).</summary>
559function ToOctalString: string;
560
561
562// -- Arithmetic operators --
563
564/// <summary>Adds two BigIntegers.</summary>
565class operator Add(const Left, Right: BigInteger): BigInteger;
566
567/// <summary>Subtracts the second BigInteger from the first.</summary>
568class operator Subtract(const Left, Right: BigInteger): BigInteger;
569
570/// <summary>Multiplies two BigIntegers.</summary>
571class operator Multiply(const Left, Right: BigInteger): BigInteger;
572
573/// <summary>Multiplies the specified BigInteger with the specified Word value.</summary>
574class operator Multiply(const Left: BigInteger; Right: Word): BigInteger;
575
576/// <summary>multiplies the specified Wirdvalue with the specified BigInteger.</summary>
577class operator Multiply(Left: Word; const Right: BigInteger): BigInteger;
578
579/// <summary>Performs an integer divide of the first BigInteger by the second.
580class operator IntDivide(const Left, Right: BigInteger): BigInteger;
581
582/// <summary>Performs an integer divide of the first BigInteger by the second.
583class operator IntDivide(const Left: BigInteger; Right: UInt16): BigInteger;
584
585/// <summary>Performs an integer divide of the first BigInteger by the second.
586class operator IntDivide(const Left: BigInteger; Right: UInt32): BigInteger;
587
588/// <summary>Returns the remainder of an integer divide of the first BigInteger by the second.</summary>
589class operator Modulus(const Left, Right: BigInteger): BigInteger;
590
591/// <summary>Returns the remainder of an integer divide of the first BigInteger by the second.</summary>
592class operator Modulus(const Left: BigInteger; Right: UInt32): BigInteger;
593
594/// <summary>Returns the remainder of an integer divide of the first BigInteger by the second.</summary>
595class operator Modulus(const Left: BigInteger; Right: UInt16): BigInteger;
596
597/// <summary>Unary minus. Negates the value of the specified BigInteger.</summary>
598class operator Negative(const Value: BigInteger): BigInteger;
599
600{$IFDEF BIGINTEGERIMMUTABLE}
601private
602{$ENDIF}
603/// <summary>Increment. Adds 1 to the value of the specified BigInteger very fast.</summary>
604class operator Inc(const Value: BigInteger): BigInteger;
605
606/// <summary>Decrement. Subtracts 1 from the value of the specified BigInteger very fast.</summary>
607class operator Dec(const Value: BigInteger): BigInteger;
608{$IFDEF BIGINTEGERIMMUTABLE}
609public
610{$ENDIF}
611
612// -- Logical and bitwise operators --
613
614/// <summary>Returns the result of the bitwise AND operation on its BigInteger operands. The result
615/// has two's complement semantics, e.g. '-1 and 7' returns '7'.</summary>
616class operator BitwiseAnd(const Left, Right: BigInteger): BigInteger;
617
618/// <summary>Returns the result of the bitwise OR operation on its BigInteger operands. The result
619/// has two's complement semantics, e.g. '-1 or 7' returns '-1'.</summary>
620class operator BitwiseOr(const Left, Right: BigInteger): BigInteger;
621
622/// <summary>Returns the result of the bitwise XOR operation on its BigIntegers operands. The result
623/// has two's complement semantics, e.g. '-1 xor 7' returns '-8'.</summary>
624class operator BitwiseXor(const Left, Right: BigInteger): BigInteger;
625
626/// <summary>Returns the result of the bitwise NOT operation on its BigInteger operand. The result
627/// has two's complement semantics, e.g. 'not 1' returns '-2'.</summary>
628class operator LogicalNot(const Value: BigInteger): BigInteger;
629
630
631// -- Shift operators --
632
633/// <summary>Shifts the specified BigInteger value the specified number of bits to the left (away from 0).
634/// The size of the BigInteger is adjusted accordingly.</summary>
635/// <remarks>Note that this is an arithmetic shift, i.e. the sign is preserved. This is unlike normal
636/// integer shifts in Delphi.</remarks>
637class operator LeftShift(const Value: BigInteger; Shift: Integer): BigInteger;
638
639/// <summary>Shifts the specified BigInteger value the specified number of bits to the right (toward 0).
640/// The size of the BigInteger is adjusted accordingly.</summary>
641/// <remarks>Note that this is an arithmetic shift, i.e. the sign is preserved. This is unlike normal
642/// integer shifts in Delphi. This means that negative values do not finally end up as 0, but
643/// as -1, since the sign bit is always shifted in.</remarks>
644class operator RightShift(const Value: BigInteger; Shift: Integer): BigInteger;
645
646
647// -- Comparison operators --
648
649/// <summary>Returns True if the specified BigIntegers have the same value.</summary>
650class operator Equal(const Left, Right: BigInteger): Boolean;
651
652/// <summary>Returns True if the specified BigInteger do not have the same value.</summary>
653class operator NotEqual(const Left, Right: BigInteger): Boolean;
654
655/// <summary>Returns true if the value of Left is mathematically greater than the value of Right.</summary>
656class operator GreaterThan(const Left, Right: BigInteger): Boolean;
657
658/// <summary>Returns true if the value of Left is mathematically greater than or equal to the value
659/// of Right.</summary>
660class operator GreaterThanOrEqual(const Left, Right: BigInteger): Boolean;
661
662/// <summary>Returns true if the value of Left is mathematically less than the value of Right.</summary>
663class operator LessThan(const Left, Right: BigInteger): Boolean;
664
665/// <summary>Returns true if the value of Left is mathematically less than or equal to the
666/// value of Right.</summary>
667class operator LessThanOrEqual(const Left, Right: BigInteger): Boolean;
668
669
670// -- Implicit conversion operators --
671
672/// <summary>Implicitly (i.e. without a cast) converts the specified Integer to a BigInteger.</summary>
673class operator Implicit(const Value: Int32): BigInteger;
674
675/// <summary>Implicitly (i.e. without a cast) converts the specified Cardinal to a BigInteger.</summary>
676class operator Implicit(const Value: UInt32): BigInteger;
677
678/// <summary>Implicitly (i.e. without a cast) converts the specified Int64 to a BigInteger.</summary>
679class operator Implicit(const Value: Int64): BigInteger;
680
681/// <summary>Implicitly (i.e. without a cast) converts the specified UInt64 to a BigInteger.</summary>
682class operator Implicit(const Value: UInt64): BigInteger;
683
684/// <summary>Implicitly (i.e. without a cast) converts the specified string to a BigInteger. The BigInteger
685/// is the result of a call to Parse(Value).</summary>
686class operator Implicit(const Value: string): BigInteger;
687
688{$IFNDEF NoAnsi}
689/// <summary>Implicitly (i.e. without a cast) converts the specified string to a BigInteger. The BigInteger
690/// is the result of a call to Parse(Value).</summary>
691/// <remark>Added for compatibility with C++Builder.</remark>
692class operator Implicit(const Value: PAnsiChar): BigInteger;
693{$ENDIF}
694
695/// <summary>Implicitly (i.e. without a cast) converts the specified string to a BigInteger. The BigInteger
696/// is the result of a call to Parse(Value).</summary>
697/// <remark>Added for compatibility with C++Builder.</remark>
698class operator Implicit(const Value: PWideChar): BigInteger;
699
700
701// -- Explicit conversion operators --
702
703/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to an Integer. If necessary, the
704/// value of the BigInteger is truncated or sign-extended to fit in the result.</summary>
705class operator Explicit(const Value: BigInteger): Int32;
706
707/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to a Cardinal. If necessary, the
708/// value of the BigInteger is truncated to fit in the result.</summary>
709class operator Explicit(const Value: BigInteger): UInt32;
710
711/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to an Int64. If necessary, the
712/// value of the BigInteger is truncated or sign-extended to fit in the result.</summary>
713class operator Explicit(const Value: BigInteger): Int64;
714
715/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to an UInt64. If necessary, the
716/// value of the BigInteger is truncated to fit in the result.</summary>
717class operator Explicit(const Value: BigInteger): UInt64;
718
719{$IFDEF HasExtended}
720/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to an Extended.</summary>
721class operator Explicit(const Value: BigInteger): Extended;
722{$ENDIF}
723
724/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to a Double.</summary>
725class operator Explicit(const Value: BigInteger): Double;
726
727/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to a Single.</summary>
728class operator Explicit(const Value: BigInteger): Single;
729
730/// <summary>Explicitly (i.e. with a cast) converts the specified Double to a BigInteger.</summary>
731class operator Explicit(const Value: Double): BigInteger;
732
733/// <summary>Explicitly (i.e. with a cast) converts the specified BigInteger to a string.</summary>
734/// <remarks>Calls Value.ToString to generate the result.</remarks>
735class operator Explicit(const Value: BigInteger): string;
736
737
738// -- Conversion functions --
739
740/// <summary>Converts the specified BigInteger to a Single, if this is possible. Returns an infinity if the
741/// value of the BigInteger is too large.</summary>
742function AsSingle: Single;
743
744/// <summary>Converts the specified BigInteger to a Double, if this is possible. Returns an infinity if the
745/// value of the BigInteger is too large.</summary>
746function AsDouble: Double;
747
748{$IFDEF HasExtended}
749/// <summary>Converts the specified BigInteger to an Extended, if this is possible. Returns an infinity if the
750/// value of the BigInteger is too large.</summary>
751function AsExtended: Extended;
752{$ENDIF}
753
754/// <summary>Converts the specified BigInteger to an Integer, if this is possible. Returns an exception if the
755/// value of the BigInteger is too large.</summary>
756function AsInteger: Integer;
757
758/// <summary>Converts the specified BigInteger to a Cardinal, if this is possible. Returns an exception if the
759/// value of the BigInteger is too large or is negative.</summary>
760function AsCardinal: Cardinal;
761
762/// <summary>Converts the specified BigInteger to an Int64, if this is possible. Returns an exception if the
763/// value of the BigInteger is too large.</summary>
764function AsInt64: Int64;
765
766/// <summary>Converts the specified BigInteger to a UInt64, if this is possible. Returns an exception if the
767/// value of the BigInteger is too large or is negative.</summary>
768function AsUInt64: UInt64;
769
770
771// -- Operators as functions --
772
773/// <summary>The function equivalent to the operator '+'.</summary>
774class function Add(const Left, Right: BigInteger): BigInteger; overload; static;
775class procedure Add(const Left, Right: BigInteger; var Result: BigInteger); overload; static;
776
777/// <summary>The function equivalent to the operator '-'.</summary>
778class function Subtract(const Left, Right: BigInteger): BigInteger; overload; static;
779class procedure Subtract(const Left, Right: BigInteger; var Result: BigInteger); overload; static;
780
781/// <summary>The function equivalent to the operator '*'.</summary>
782class function Multiply(const Left, Right: BigInteger): BigInteger; overload; static;
783class procedure Multiply(const Left, Right: BigInteger; var Result: BigInteger); overload; static;
784
785/// <summary>Function performing "schoolbook" multiplication.</summary>
786class procedure MultiplyBaseCase(const Left, Right: BigInteger; var Result: BigInteger); static;
787
788/// <summary>Function performing multiplcation using Karatsuba algorithm. Has more overhead, so only
789/// applied to large BigIntegers.</summary>
790class procedure MultiplyKaratsuba(const Left, Right: BigInteger; var Result: BigInteger); static;
791
792/// <summary>Function performing multiplication using Toom-Cook 3-way algorithm. Faster than Karatsuba, but,
793/// due to its overhead, only for very large BigIntegers.</summary>
794class function MultiplyToomCook3(const Left, Right: BigInteger): BigInteger; static;
795
796/// <summary>The function equivalent to the operators 'div' and 'mod'. Since calculation of the quotient
797/// automatically leaves a remainder, this function allows you to get both for more or less the "price"
798/// (performance-wise) of one.</summary>
799class procedure DivMod(const Dividend, Divisor: BigInteger; var Quotient, Remainder: BigInteger); static;
800
801/// <summary>Simple "schoolbook" division according to Knuth, with limb-size digits.</summary>
802class procedure DivModKnuth(const Left, Right: BigInteger; var Quotient, Remainder: BigInteger); static;
803
804/// <summary>Recursive "schoolbook" division, as described by Burnikel and Ziegler. Faster than
805/// <see cref="DivModKnuth" />, but with more overhead, so should only be applied for
806/// larger BigIntegers.</summary>
807/// <remark>For smaller BigIntegers, this routine falls back to DivModKnuth.
808class procedure DivModBurnikelZiegler(const Left, Right: BigInteger; var Quotient, Remainder: BigInteger); static;
809
810/// <summary>The function equivalent to the operator 'div'.</summary>
811class function Divide(const Left, Right: BigInteger): BigInteger; overload; static;
812
813/// <summary>The function equivalent to the operator 'div'.</summary>
814class function Divide(const Left: BigInteger; Right: UInt16): BigInteger; overload; static;
815
816/// <summary>The function equivalent to the operator 'div'.</summary>
817class function Divide(const Left:BigInteger; Right: UInt32): BigInteger; overload; static;
818
819/// <summary>The function equivalent to the operator 'mod'. Like for integers, the remainder gets
820/// the sign - if any - of the dividend (i.e. of Left).</summary>
821class function Remainder(const Left, Right: BigInteger): BigInteger; overload; static;
822
823/// <summary>The function equivalent to the operator 'mod'. Like for integers, the remainder gets
824/// the sign - if any - of the dividend (i.e. of Left).</summary>
825class function Remainder(const Left: BigInteger; Right: UInt32): BigInteger; overload; static;
826
827/// <summary>The function equivalent to the operator 'mod'. Like for integers, the remainder gets
828/// the sign - if any - of the dividend (i.e. of Left).</summary>
829class function Remainder(const Left: BigInteger; Right: UInt16): BigInteger; overload; static;
830
831class function SqrKaratsuba(const Value: BigInteger): BigInteger; static;
832
833/// <summary>Returns the negation of Value.</summary>
834class function Negate(const Value: BigInteger): BigInteger; static;
835
836/// <summary>The procedural equivalent of the operator 'shl'.</summary>
837class procedure ShiftLeft(const Value: BigInteger; Shift: Integer; var Result: BigInteger); overload; static;
838/// <summary>The function equivalent of the operator 'shl'.</summary>
839class function ShiftLeft(const Value: BigInteger; Shift: Integer): BigInteger; overload; static;
840
841/// <summary>The procedural equivalent of the operator 'shr'.</summary>
842class procedure ShiftRight(const Value: BigInteger; Shift: Integer; var Result: BigInteger); overload; static;
843/// <summary>The function equivalent of the operator 'shr'.</summary>
844class function ShiftRight(const Value: BigInteger; Shift: Integer): BigInteger; overload; static;
845
846// -- Self-referential operator functions --
847
848{$IFNDEF BIGINTEGERIMMUTABLE}
849/// <summary>
850/// <para>The functional equivalent to</para>
851/// <code> A := A + Other;</code>
852/// <para>This can be chained, as the function returns a pointer to itself:</para>
853/// <code> A.Add(First).Add(Second);</code></summary>
854/// <remarks><para>This was added in the hope to gain speed by avoiding some allocations.
855/// This is not so, although a longer chain seems to improve performance, compared to normal addition
856/// using operators, a bit.</para></remarks>
857function Add(const Other: BigInteger): PBigInteger; overload;
858
859/// <summary>The functional equivalent to Self := Self + Other;</summary>
860function Subtract(const Other: BigInteger): PBigInteger; overload;
861
862/// <summary>The functional equivalent to Self := Self div Other;</summary>
863function Divide(const Other: BigInteger): PBigInteger; overload;
864
865/// <summary>The functional equivalent to Self := Self mod Other;</summary>
866function Remainder(const Other: BigInteger): PBigInteger; overload;
867
868/// <summar>The functional equivalent to Self := Self * Other;</summary>
869function Multiply(const Other: BigInteger): PBigInteger; overload;
870{$ENDIF}
871
872
873// -- Math functions --
874
875/// <summary>Returns the absolute value of the value in the BigInteger.</summary>
876class function Abs(const Value: BigInteger): BigInteger; overload; static;
877
878/// <summary>Returns the absolute value of the current BigInteger.<summary>
879function Abs: BigInteger; overload;
880
881/// <summary>Returns the predecessor of the current BigInteger, i.e. its value minus one.</summary>
882function Pred: BigInteger; overload;
883
884// <summary>Returns the successor of the current BigInteger, i.e. its value plus one.</summary>
885function Succ: BigInteger; overload;
886
887/// <summary>Returns the bit length, the minimum number of bits needed to represent the value, excluding
888/// the sign bit.</summary>
889function BitLength: Integer;
890
891/// <summary>Returns the number of all bits that are set, assuming two's complement. The sign bit is
892/// included in the count.</summary>
893function BitCount: Integer;
894
895/// <summary>Returns the index of the rightmost (lowest) bit set. The lowest bit has index 0. Returns -1 if
896/// this BigInteger is zero. </summary>
897function LowestSetBit: Integer;
898
899/// <summary>Returns a copy of the current BigInteger, with a unique copy of the data.</summary>
900function Clone: BigInteger;
901
902/// <summary>Returns +1 if the value in Left is greater than the value in Right, 0 if they are equal and
903/// 1 if it is lesser.</summary>
904class function Compare(const Left, Right: BigInteger): Integer; static;
905
906/// <summary>Returns N!, i.e. N * (N - 1) * (N - 2) * ... * 2 as BigInteger.
907class function Factorial(N: Integer): BigInteger; static;
908
909/// <summary>Returns a single Fibonacci number; 0 --> 0; 1 --> 1; N --> F(N-1) + F(N-2)</summary>
910class function Fibonacci(N: Integer): BigInteger; static;
911
912/// <summary>Returns the (positive) greatest common divisor of the specified BigInteger values.</summary>
913class function GreatestCommonDivisor(const Left, Right: BigInteger): BigInteger; static;
914
915/// <summary>Returns the natural logarithm of the BigInteger value.</summary>
916class function Ln(const Value: BigInteger): Double; overload; static;
917
918/// <summary>Returns the natural logarithm of the current BigInteger.</summary>
919function Ln: Double; overload;
920
921/// <summary>Returns the logarithm to the specified base of the BigInteger value.</summary>
922class function Log(const Value: BigInteger; Base: Double): Double; overload; static;
923
924/// <summary>Returns the logarithm to the specified base of the current BigInteger.</summary>
925function Log(Base: Double): Double; overload;
926
927/// <summary>Returns the logarithm to base 2 of the BigInteger value.</summary>
928class function Log2(const Value: BigInteger): Double; overload; static;
929
930/// <summary>Returns the logarithm to base 2 of the current BigInteger.</summary>
931function Log2: Double; overload;
932
933/// <summary>Returns the logarithm to base 10 of the BigInteger value.</summary>
934class function Log10(const Value: BigInteger): Double; overload; static;
935
936/// <summary>Returns the logarithm to base 10 of the current BigInteger.</summary>
937function Log10: Double; overload;
938
939/// <summary>The reverse of BigInteger.Ln. Returns e^Value, for very large Value, as BigInteger
940class function Exp(const b: Double): BigInteger; static;
941
942/// <summary>Returns the larger of two specified values.</summary>
943class function Max(const Left, Right: BigInteger): BigInteger; static;
944
945/// <summary>Returns the smaller of two specified values.</summary>
946class function Min(const Left, Right: BigInteger): BigInteger; static;
947
948/// <summary>Returns the modular inverse of Value mod Modulus.</summary>
949/// <exception>Returns an exception if there is no modular inverse.</exception>
950class function ModInverse(const Value, Modulus: BigInteger): BigInteger; static;
951
952/// <summary>Returns the specified modulus value of the specified value raised to the specified power.</summary>
953class function ModPow(const ABase, AExponent, AModulus: BigInteger): BigInteger; static;
954
955/// <summary>Returns the specified value raised to the specified power.</summary>
956class function Pow(const ABase: BigInteger; AExponent: Integer): BigInteger; overload; static;
957
958/// <summary>Returns the specified value raised to the spefied power in Result,</summary>
959class procedure Pow(const ABase: BigInteger; AExponent: Integer; var Result: BigInteger); overload; static;
960
961/// <summary>Returns the nth root R of a BigInteger such that R^index <= Radicand < (R+1)^index.</summary>
962class function NthRoot(const Radicand: BigInteger; Index: Integer): BigInteger; static;
963
964/// <summary>If R is the nth root of Radicand, returns Radicand - R^index.</summary>
965class procedure NthRootRemainder(const Radicand: BigInteger; Index: Integer;
966var Root, Remainder: BigInteger); static;
967
968/// <summary>Returns the square root R of Radicand, such that R^2 < Radicand < (R+1)^2</summary>
969class function BaseCaseSqrt(const Radicand: BigInteger): BigInteger; static;
970
971/// <summary>If R is the square root of Radicand, returns Radicand - R^2.</summary>
972class procedure BaseCaseSqrtRemainder(const Radicand: BigInteger; var Root, Remainder: BigInteger); static;
973
974/// <summary>Returns the square root R of the radicand, such that R^2 < radicand < (R+1)^2.</summary>
975class function Sqrt(const Radicand: BigInteger): BigInteger; static;
976
977/// <summary>Returns square root and remainder of the radicand.</summary>
978class procedure SqrtRemainder(const Radicand: BigInteger; var Root, Remainder: BigInteger); static;
979
980/// <summary>Returns the square of Value, i.e. Value*Value</summary>
981class function Sqr(const Value: BigInteger): BigInteger; static;
982
983
984// -- Utility functions --
985
986/// <summary>Sets whether partial-flags stall must be avoided with modified routines.</summary>
987/// <remarks>
988/// <para><b>USING THE WRONG SETTING MAY AFFECT THE TIMING OF CERTAIN ROUTINES CONSIDERABLY, SO USE
989/// THIS WITH EXTREME CARE!</b></para>
990/// <para>The unit is usually able to determine the right settings automatically.</para>
991/// </remarks>
992class procedure AvoidPartialFlagsStall(Value: Boolean); static;
993
994// -- Array function(s) --
995
996/// <summary>Converts a BigInteger value to a byte array.</summary>
997/// <returns><para>A TArray<Byte>, see remarks.</para></returns>
998/// <remarks>
999/// <para>The individual bytes in the array returned by this method appear in little-endian order.</para>
1000/// <para>Negative values are written to the array using two's complement representation in the most compact
1001/// form possible. For example, -1 is represented as a single byte whose value is $FF instead of as an array
1002/// with multiple elements, such as $FF, $FF or $FF, $FF, $FF, $FF.</para>
1003/// <para>Because two's complement representation always interprets the highest-order bit of the last byte in
1004/// the array (the byte at position High(Array)) as the sign bit, the method returns a byte array with
1005/// an extra element whose value is zero to disambiguate positive values that could otherwise be interpreted
1006/// as having their sign bits set. For example, the value 120 or $78 is represented as a single-byte array:
1007/// $78. However, 129, or $81, is represented as a two-byte array: $81, $00. Something similar applies to
1008/// negative values: -179 (or -$B3) must be represented as $4D, $FF.</para>
1009/// </remarks>
1010function ToByteArray: TArray<Byte>;
1011
1012// -- Information functions --
1013
1014/// <summary>Returns the number of allocated limbs for the current BigInteger.</summary>
1015function GetAllocated: Integer;
1016
1017/// <summary>Returns the number of used limbs for the current BigInteger.</summary>
1018function GetSize: Integer; inline;
1019
1020/// <summary>Returns a pointer to the first limb of the magnitude.</summary>
1021function Data: PLimb; inline;
1022
1023/// <summary>Returns the sign for the current BigInteger: -1 for negative values, 0 for zero and 1 for
1024/// positive values.</summary>
1025function GetSign: Integer; inline;
1026
1027/// <summary>Sets the sign of the current BigInteger: -1 for negative values, 0 for zero and 1 for
1028/// positive values.</summary>
1029procedure SetSign(Value: Integer); inline;
1030{$ENDREGION}
1031
1032private
1033{$REGION 'private constants, types and variables'}
1034type
1035TErrorCode = (ecParse, ecDivByZero, ecConversion, ecInvalidBase, ecOverflow, ecInvalidArg, ecInvalidArgFloat, ecNoInverse,
1036ecNegativeExponent, ecNegativeRadicand);
1037TBinaryOperator = procedure(Left, Right, Result: PLimb; LSize, RSize: Integer);
1038var
1039// The limbs of the magnitude, least significant limb at lowest address.
1040FData: TMagnitude;
1041// The top bit is the sign bit. Other bits form the unsigned number of valid limbs of the magnitude.
1042FSize: Integer;
1043class var
1044// The currently actual (global) number base.
1045FBase: TNumberBase;
1046// Flag indicating need to test for partial flag stall.
1047FAvoidStall: Boolean;
1048// The current rounding mode.
1049FRoundingMode: TRoundingMode;
1050
1051// The internal functions used to add and subtract. These differ depending on the need to avoid
1052// a partial flag stall.
1053FInternalAdd: TBinaryOperator;
1054FInternalSubtract: TBinaryOperator;
1055FLog2: Double;
1056{$ENDREGION}
1057
1058{$REGION 'private functions'}
1059{$IFNDEF PUREPASCAL}
1060// Function detecting of current CPU could suffer from partial flag stall.
1061class procedure DetectPartialFlagsStall; static;
1062
1063// Internal function adding two magnitudes. Contains code to avoid a partial flag stall.
1064class procedure InternalAddModified(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1065// Internal function adding two magnitudes. Does not contain code to avoid partial flag stall.
1066class procedure InternalAddPlain(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1067// Internal function subtracting two magnitudes. Contains code to avoid a partial flag stall.
1068class procedure InternalSubtractModified(Larger, Smaller, Result: PLimb; LSize, SSize: Integer); static;
1069// Internal func9tion subtracting two magnitudes. Does not contain code to avoid a partial flag stall.
1070class procedure InternalSubtractPlain(Larger, Smaller, Result: PLimb; LSize, SSize: Integer); static;
1071// Internal perfect division by 3 (guaranteed that there is no remainder).
1072class procedure InternalDivideBy3(Value, Result: PLimb; ASize: Integer); static;
1073// Internal function dividing magnitude by 100, in-place. Leaves quotient in place, returns remainder.
1074class function InternalDivMod100(var X: NativeUInt): NativeUInt; static;
1075// Function performing int to string conversion, writing to WritePtr.
1076class procedure InternalIntToStrDecimal(const Value: NativeUInt; var WritePtr: PChar; MaxDigits: Integer); static;
1077// Function calculating floating point components out of a BigInteger.
1078{$ELSE}
1079// Internal function adding two magnitudes. Pure Pascal (non-assembler) implementation.
1080class procedure InternalAddPurePascal(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1081// Internal function subtracting two magnitudes. Pure Pascal (non-assembler) implementation.
1082class procedure InternalSubtractPurePascal(Larger, Smaller, Result: PLimb; LSize, SSize: Integer); static;
1083{$ENDIF}
1084class procedure ConvertToFloatComponents(const Value: BigInteger; SignificandSize: Integer;
1085var Sign: Integer; var Significand: UInt64; var Exponent: Integer); static;
1086// Internal function comparing two magnitudes.
1087class function InternalCompare(Left, Right: PLimb; LSize, RSize: Integer): Integer; static; {$IFDEF PUREPASCAL} inline; {$ENDIF}
1088// Internal function and-ing two magnitudes.
1089class procedure InternalAnd(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1090// Internal function or-ing two magnitudes.
1091class procedure InternalOr(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1092// Internal funciton xor-ing two magnitudes.
1093class procedure InternalXor(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1094// Internal function and-not-ing two magnitudes (Left^ and not Right^).
1095class procedure InternalAndNot(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1096// Internal function not-and-ing two magnitudes (not Left^ and Right^).
1097class procedure InternalNotAnd(Left, Right, Result: PLimb; LSize, RSize: Integer); static; inline;
1098// Internal function performing bitwise operations. The bitwise operations share similar code.
1099class procedure InternalBitwise(const Left, Right: BigInteger; var Result: BigInteger;
1100PlainOp, OppositeOp, InversionOp: TBinaryOperator); static;
1101// Internal function icrementing a magnitude by one, in-place.
1102class procedure InternalIncrement(Limbs: PLimb; Size: Integer); static;
1103// Internal function decrementing a magnitude by one, in-place.
1104class procedure InternalDecrement(Limbs: PLimb; Size: Integer); static;
1105// Internal function parsing a decimal string into a BigInteger. Returns False if string not valid.
1106class function InternalParseDecimal(P: PChar; var Value: BigInteger): Boolean; static;
1107// Internal function parsing a hex string into a BigInteger. Returns False if string not valid.
1108class function InternalParseHex(P: PChar; var Value: BigInteger): Boolean; static;
1109// Internal function shifting a magnitude left into a new magnitude.
1110class procedure InternalShiftLeft(Source, Dest: PLimb; Shift, Size: Integer); static;
1111// Internal function shifting a magnitude right into a new magnitude.
1112class procedure InternalShiftRight(Source, Dest: PLimb; Shift, Size: Integer); static;
1113// Internal function performing int to string function for given numeric base.
1114class procedure InternalIntToStrBase(const Value: NativeUInt; Base: Cardinal;
1115var WritePtr: PChar; MaxDigits: Integer); static;
1116// Internal function performing int to string conversion for bases 2, 4, and 16, doing simple shifts.
1117class procedure InternalShiftedToString(const Value: BigInteger; Base: Integer; var WritePtr: PChar); static;
1118// Internal function performing int to string conversion, repeatedly dividing by 10 (simple algorithm).
1119class procedure InternalPlainToString(const Value: BigInteger; Base: Integer; const BaseInfo: TNumberBaseInfo;
1120var WritePtr: PChar; SectionCount: Integer); static;
1121// Internal function performing int to string conversion, using recursive divide-and-conquer algorithm.
1122class procedure InternalRecursiveToString(const Value: BigInteger; Base: Integer; const BaseInfo: TNumberBaseInfo;
1123var WritePtr: PChar; SectionCount: Integer); static;
1124// Internal function performing division of two magnitudes, returning quotient and remainder.
1125class function InternalDivMod(Dividend, Divisor, Quotient, Remainder: PLimb;
1126LSize, RSize: Integer): Boolean; static;
1127// Internal function performing division of magnitude by 32 bit integer.
1128class function InternalDivMod32(Dividend: PLimb; Divisor: UInt32; Quotient, Remainder: PLimb;
1129LSize: Integer): Boolean; static;
1130// Internal function performing division of magnitude by 16 bit integer (needed for Pure Pascal division).
1131class function InternalDivMod16(Dividend: PLimb; Divisor: UInt16; Quotient, Remainder: PLimb;
1132LSize: Integer): Boolean; static;
1133// performs a Knuth divmod. Does not compare magnitudes. Called by DivModKnuth.
1134class procedure UncheckedDivModKnuth(const Left, Right: BigInteger; var Quotient, Remainder: BigInteger); static;
1135// Internal function multiplying two magnitudes.
1136class procedure InternalMultiply(Left, Right, Result: PLimb; LSize, RSize: Integer); static;
1137// Internal function dividing magnitude by given base value. Leaves quotient in place, returns remainder.
1138class function InternalDivideByBase(Mag: PLimb; Base: Integer; var Size: Integer): UInt32; static;
1139// Internal function multiplying by 16 bit integer and then adding 16 bit value. Used by parser.
1140class procedure InternalMultiply16(const Left: TMagnitude; var Result: TMagnitude; LSize: Integer; Right: Word); static;
1141// Internal function multiplying by a base and adding a digit. Condition: ADigit < ABase. Size is updated if necessary.
1142// Cf. code of TryParse on how to set up Value.
1143class procedure InternalMultiplyAndAdd16(Value: PLimb; ABase, ADigit: Word; var Size: Integer); static;
1144
1145// Internal function negating magnitude (treating it as two's complement).
1146class procedure InternalNegate(Source, Dest: PLimb; Size: Integer); static;
1147
1148// Burnikel-Ziegler and helper functions.
1149// Divides two magnitudes using Burnikel-Ziegler algorithm.
1150class procedure InternalDivModBurnikelZiegler(const Left, Right: BigInteger;
1151var Quotient, Remainder: BigInteger); static;
1152// Divides a BigInteger by 3 exactly. BigInteger is guaranteed to be a positive multiple of 3.
1153class function DivideBy3Exactly(const A: BigInteger): BigInteger; static;
1154// Helper function for Burnikel-Ziegler division. See explanation in implementation section.
1155class procedure DivThreeHalvesByTwo(const LeftUpperMid, LeftLower, Right, RightUpper: BigInteger;
1156const RightLower: BigInteger;
1157N: Integer; var Quotient, Remainder: BigInteger); static;
1158// Helper function for Burnikel-Ziegler division.
1159class procedure DivTwoDigitsByOne(const Left, Right: BigInteger; N: Integer;
1160var Quotient, Remainder: BigInteger); static;
1161
1162// Karatsuba and Toom-Cook helper function
1163// Split BigInteger into smaller BigIntegers of size BlockSize.
1164function Split(BlockSize, BlockCount: Integer): TArray<BigInteger>;
1165
1166// Sets global numeric base.
1167class procedure SetBase(const Value: TNumberBase); static;
1168// Raises exceptions depending on given error code.
1169class procedure Error(ErrorCode: TErrorCode; const ErrorInfo: array of const); static;
1170
1171class procedure Compact(var Data: TMagnitude; var Size: Integer); overload; static;
1172// Resets size thus that there are no leading zero limbs.
1173procedure Compact; overload; inline;
1174// Reallocates magnitude to ensure a given size.
1175procedure EnsureSize(RequiredSize: Integer);
1176// Creates a new magnitude.
1177procedure MakeSize(RequiredSize: Integer);
1178{$ENDREGION}
1179
1180public
1181{$REGION 'public properties'}
1182/// <summary>Number of valid limbs in the magnitude</summary>
1183property Size: Integer read GetSize;
1184/// <summary>Number of allocated limbs in the mangitude</summary>
1185property Allocated: Integer read GetAllocated;
1186/// <summary>Indicates whether BigInteger is negative</summary>
1187property Negative: Boolean read IsNegative;
1188/// <summary>The sign of the BigInteger: -1, 0 or 1</summary>
1189property Sign: Integer read GetSign write SetSign;
1190/// <summary>Magnitude, dynamic array of TLimb, containing the (unsigned) value of the BigInteger</summary>
1191property Magnitude: TMagnitude read FData;
1192
1193/// <summary>Global numeric base for BigIntegers</summary>
1194class property Base: TNumberBase read FBase write SetBase;
1195/// <summary>A pure alias for Base</summary>
1196class property Radix: TNumberBase read FBase write SetBase;
1197/// <summary>Global rounding mode used for conversion to floating point</summary>
1198class property RoundingMode: TRoundingMode read FRoundingMode write FRoundingMode;
1199/// <summary>Global flag indicating if partial flag stall is avoided</summary>
1200class property StallAvoided: Boolean read FAvoidStall;
1201{$ENDREGION}
1202
1203end;
1204
1205/// <summary>Returns sign bit (top bit) of an integer.</summary>
1206function SignBitOf(Value: Integer): Integer; inline;
1207
1208var
1209// Set this to True if you want to generate debug output.
1210DoDebug: Boolean = True;
1211
1212{$HPPEMIT END '#include "Velthuis.BigIntegers.operators.hpp"'}
1213
1214implementation
1215
1216// To switch PUREPASCAL for debugging purposes, $UNDEF PUREPASCAL before the routine and $DEFINE PUREPASCAL
1217// after the routine, if PP was defined.
1218{$IFDEF PUREPASCAL}
1219{$DEFINE PP}
1220{$ENDIF}
1221
1222// Copy the following around the routine for which you want to switch off PUREPASCAL
1223
1224{$UNDEF PUREPASCAL}
1225// Routine here.
1226{$IFDEF PP}
1227{$DEFINE PUREPASCAL}
1228{$UNDEF PP}
1229{$ENDIF}
1230
1231uses
1232{$IFDEF DEBUG}
1233{$IFDEF MSWINDOWS}
1234Winapi.Windows,
1235{$ENDIF}
1236{$ENDIF}
1237Velthuis.Sizes, Velthuis.Numerics, Velthuis.FloatUtils, Velthuis.StrConsts;
1238
1239{$POINTERMATH ON}
1240
1241const
1242KZero: NativeUInt = 0;
1243
1244{$REGION 'Debug related tools -- can eventually be removed'}
1245{$IFDEF DEBUG}
1246function Join(const Delimiter: string; const Values: array of string): string;
1247var
1248I: Integer;
1249begin
1250if Length(Values) > 0 then
1251begin
1252Result := Values[0];
1253for I := 1 to High(Values) do
1254Result := Delimiter + Result;
1255end;
1256end;
1257
1258function DumpPLimb(P: PLimb; Size: Integer): string;
1259var
1260SL: TArray<string>;
1261I: Integer;
1262begin
1263Result := '';
1264SetLength(SL, Size);
1265for I := 0 to Size - 1 do
1266SL[I] := Format('%.8x', [P[Size - I - 1]]);
1267Result := Result + Join(' ', SL);
1268end;
1269
1270procedure Debug(const Msg: string; const Params: array of const); overload;
1271begin
1272if not DoDebug then
1273Exit;
1274
1275if IsConsole then
1276// Write to console.
1277Writeln(System.ErrOutput, Format(Msg, Params))
1278{$IFDEF MSWINDOWS}
1279else
1280
1281// Inside the IDE, this will be displayed in the Event Log.
1282OutputDebugString(PChar(Format(Msg, Params)));
1283{$ELSE}
1284;
1285{$ENDIF}
1286
1287end;
1288
1289procedure Debug(const Msg: string); overload;
1290begin
1291Debug(Msg, []);
1292end;
1293{$ELSE}
1294procedure Debug(const Msg: string; const Params: array of const);
1295begin
1296end;
1297{$ENDIF}
1298{$ENDREGION}
1299
1300{$REGION 'Partial flag stall avoidance code'}
1301const
1302CTimingLoops = $40000;
1303
1304{$IFNDEF PUREPASCAL}
1305procedure Timing(var T1, T2, T3: UInt64); stdcall;
1306{$IFDEF WIN32}
1307asm
1308RDTSC
1309MOV ECX,T1
1310MOV DWORD PTR [ECX],EAX
1311MOV DWORD PTR [ECX+4],EDX
1312XOR EAX,EAX
1313MOV EDX,CTimingLoops
1314
1315@ADCLoop:
1316
1317ADC EAX,[ECX] // Partial-flags stall on some "older" processors causes a measurable
1318DEC EDX // timing difference. DEC only changes one flag, not entire flags register,
1319JNE @ADCLoop // causing a stall when ADC reads flag register.
1320
1321RDTSC
1322MOV ECX,T2
1323MOV [ECX],EAX
1324MOV [ECX+4],EDX
1325XOR EAX,EAX
1326MOV EDX,CTimingLoops
1327
1328.ALIGN 16
1329
1330@ADDLoop:
1331
1332ADD EAX,[ECX] // ADD does not read carry flag, so no partial-flags stall.
1333DEC EDX
1334JNE @ADDLoop
1335
1336RDTSC
1337MOV ECX,T3
1338MOV [ECX],EAX
1339MOV [ECX+4],EDX
1340end;
1341{$ELSE}
1342asm
1343MOV R9,RDX
1344RDTSC
1345MOV [RCX],EAX
1346MOV [RCX+4],EDX
1347XOR EAX,EAX
1348MOV EDX,CTimingLoops
1349
1350.ALIGN 16
1351
1352@ADCLoop:
1353
1354ADC EAX,[RCX]
1355DEC EDX
1356JNE @ADCLoop
1357
1358RDTSC
1359MOV [R9],EAX
1360MOV [R9+4],EDX
1361XOR EAX,EAX
1362MOV EDX,CTimingLoops
1363
1364.ALIGN 16
1365
1366@ADDLoop:
1367
1368ADD EAX,[RCX]
1369DEC EDX
1370JNE @ADDLoop
1371
1372RDTSC
1373MOV [R8],EAX
1374MOV [R8+4],EDX
1375end;
1376{$ENDIF}
1377
1378class procedure BigInteger.DetectPartialFlagsStall;
1379var
1380T1, T2, T3: UInt64;
1381I1, I2: UInt64;
1382begin
1383repeat
1384Timing(T1, T2, T3);
1385I1 := T2 - T1;
1386I2 := T3 - T2;
1387// Debug('Timing: %d / %d = %.2f', [I1, I2, I1 / I2]);
1388
1389// Make sure timings are far enough apart. Repeat if in "grey area" inbetween.
1390if I1 / I2 > 4.0 then
1391begin
1392AvoidPartialFlagsStall(True);
1393Exit;
1394end
1395else if I1 / I2 < 2.0 then
1396begin
1397AvoidPartialFlagsStall(False);
1398Exit;
1399end;
1400until False;
1401end;
1402{$ENDIF !PUREPASCAL}
1403{$ENDREGION}
1404
1405{$RANGECHECKS OFF}
1406{$OVERFLOWCHECKS OFF}
1407{$POINTERMATH ON}
1408{$STACKFRAMES OFF}
1409
1410{$DEFINE LIBDIVIDE}
1411
1412type
1413TUInt64 = record
1414Lo, Hi: UInt32;
1415end;
1416
1417const
1418// Size of a single limb, used in e.g. asm blocks.
1419CLimbSize = SizeOf(TLimb);
1420
1421// Double limb, for 64 bit access
1422DLimbSize = 2 * CLimbSize;
1423
1424// Array mapping a digit in a specified base to its textual representation.
1425CBaseChars: array[0..35] of Char = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
1426CNumBase = Ord('0');
1427CAlphaBase = Ord('A');
1428
1429// Array mapping a specified base to the maximum number of digits required to represent one limb in that base.
1430// They map a specified base to Ceil(32 / Log2(base)).
1431CStringMaxLengths: array[TNumberBase] of Integer =
1432(
143332, 21, 16, 14, 13, 12, 11,
143411, 10, 10, 9, 9, 9, 9,
14358, 8, 8, 8, 8, 8, 8,
14368, 7, 7, 7, 7, 7, 7,
14377, 7, 7, 7, 7, 7, 7
1438);
1439
1440CStringMinLengths: array[TNumberBase] of Integer =
1441(
144232, 20, 16, 13, 12, 11, 10,
144310, 9, 9, 8, 8, 8, 8,
14448, 7, 7, 7, 7, 7, 7,
14457, 6, 6, 6, 6, 6, 6,
14466, 6, 6, 6, 6, 6, 6
1447);
1448
1449// Various useful sizes and bitcounts.
1450CLimbBits = CByteBits * CLimbSize;
1451CLimbWords = CLimbSize div SizeOf(Word);
1452CUInt64Limbs = SizeOf(UInt64) div CLimbSize;
1453CInt64Limbs = SizeOf(Int64) div CLimbSize;
1454
1455{$INCLUDE 'bases.inc'}
1456
1457var
1458CBasePowers: array[TNumberBase] of TArray<BigInteger>;
1459
1460ValueCache: array[-15..15] of BigInteger;
1461
1462type
1463PDynArrayRec = ^TDynArrayRec;
1464TDynArrayRec = packed record
1465{$IFDEF CPU64BITS}
1466_Padding: Integer; // Make 16 byte align for payload.
1467{$ENDIF}
1468RefCnt: Integer;
1469Length: NativeInt;
1470end;
1471
1472procedure SwapIntegers(var L, R: Integer); inline;
1473var
1474Temp: Integer;
1475begin
1476Temp := L;
1477L := R;
1478R := Temp;
1479end;
1480
1481procedure SwapPLimbs(var L, R: PLimb); inline;
1482var
1483Temp: PLimb;
1484begin
1485Temp := L;
1486L := R;
1487R := Temp;
1488end;
1489
1490function ActualSize(Limb: PLimb; Size: Integer): Integer;
1491{$IFDEF PUREPASCAL}
1492begin
1493while (Size > 0) and (Limb[Size - 1] = 0) do
1494Dec(Size);
1495Result := Size;
1496end;
1497{$ELSE}
1498{$IFDEF WIN32}
1499asm
1500
1501LEA EAX,[EAX + EDX * CLimbSize - CLimbSize]
1502XOR ECX,ECX
1503
1504@Loop:
1505
1506CMP [EAX],ECX
1507JNE @Exit
1508LEA EAX,[EAX - CLimbSize]
1509DEC EDX
1510JNE @Loop
1511
1512@Exit:
1513
1514MOV EAX,EDX
1515
1516end;
1517{$ELSE !WIN32}
1518asm
1519
1520LEA RAX,[RCX + RDX * CLimbSize - CLimbSize]
1521XOR ECX,ECX
1522
1523@Loop:
1524
1525CMP [RAX],ECX
1526JNE @Exit
1527LEA RAX,[RAX - CLimbSize]
1528DEC EDX
1529JNE @Loop
1530
1531@Exit:
1532
1533MOV EAX,EDX
1534
1535end;
1536{$ENDIF !WIN32}
1537{$ENDIF}
1538
1539function IntMax(Left, Right: UInt32): UInt32;
1540{$IFNDEF PUREPASCAL}
1541{$IFDEF WIN32}
1542asm
1543CMP EAX,EDX
1544CMOVB EAX,EDX
1545end;
1546{$ELSE WIN64}
1547asm
1548MOV EAX,ECX
1549CMP EAX,EDX
1550CMOVB EAX,EDX
1551end;
1552{$ENDIF}
1553{$ELSE}
1554begin
1555Result := Left;
1556if Left < Right then
1557Result := Right;
1558end;
1559{$ENDIF}
1560
1561function IntMin(Left, Right: UInt32): UInt32;
1562{$IFNDEF PUREPASCAL}
1563{$IFDEF WIN32}
1564asm
1565CMP EAX,EDX
1566CMOVA EAX,EDX
1567end;
1568{$ELSE WIN64}
1569asm
1570MOV EAX,ECX
1571CMP EAX,EDX
1572CMOVA EAX,EDX
1573end;
1574{$ENDIF}
1575{$ELSE}
1576begin
1577Result := Left;
1578if Left > Right then
1579Result := Right;
1580end;
1581{$ENDIF}
1582
1583function ShouldUseBurnikelZiegler(LSize, RSize: Integer): Boolean; inline;
1584begin
1585// http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-November/023493.html
1586Result := (RSize >= BigInteger.BurnikelZieglerThreshold) and
1587((LSize - RSize) >= BigInteger.BurnikelZieglerOffsetThreshold);
1588end;
1589
1590function SizeBitsOf(Value: Integer): Integer; inline;
1591begin
1592Result := Value and BigInteger.SizeMask;
1593end;
1594
1595function SignBitOf(Value: Integer): Integer; inline;
1596begin
1597Result := Value and BigInteger.SignMask;
1598end;
1599
1600function Min(const A, B: BigInteger): BigInteger; inline;
1601begin
1602Result := BigInteger.Min(A, B);
1603end;
1604
1605function Max(const A, B: BigInteger): BigInteger; inline;
1606begin
1607Result := BigInteger.Max(A, B);
1608end;
1609
1610function AllocLimbs(Size: Integer): PLimb; inline;
1611begin
1612GetMem(Result, Size * CLimbSize);
1613end;
1614
1615procedure CopyLimbs(Src, Dest: PLimb; Count: Integer); inline;
1616begin
1617Move(Src^, Dest^, Count * CLimbSize);
1618end;
1619
1620// Replacement for SetLength() only for TMagnitudes, i.e. dynamic arrays of TLimb.
1621procedure AllocNewMagnitude(var AData: TMagnitude; RequiredSize: Integer);
1622var
1623NewData: PByte;
1624NewSize: Integer;
1625begin
1626NewSize := (RequiredSize + 3) and BigInteger.CapacityMask;
1627NewData := AllocMem(NewSize * CLimbSize + SizeOf(TDynArrayRec));
1628PDynArrayRec(NewData).RefCnt := 1;
1629PDynArrayRec(NewData).Length := NewSize;
1630PByte(AData) := NewData + SizeOf(TDynArrayRec);
1631end;
1632
1633{ BigInteger }
1634
1635procedure ShallowCopy(const Value: BigInteger; var Result: BigInteger); inline;
1636begin
1637Result.FSize := Value.FSize;
1638Result.FData := Value.FData;
1639end;
1640
1641procedure DeepCopy(const Value: BigInteger; var Result: BigInteger); inline;
1642begin
1643Result.FSize := Value.FSize;
1644Result.FData := Copy(Value.FData);
1645end;
1646
1647function BigInteger.Abs: BigInteger;
1648begin
1649ShallowCopy(Self, Result);
1650Result.FSize := Result.FSize and SizeMask;
1651end;
1652
1653class function BigInteger.Abs(const Value: BigInteger): BigInteger;
1654begin
1655ShallowCopy(Value, Result);
1656Result.SetSign(0);
1657end;
1658
1659function BigInteger.Pred: BigInteger;
1660begin
1661ShallowCopy(Self, Result);
1662Dec(Result);
1663end;
1664
1665function BigInteger.Succ: BigInteger;
1666begin
1667ShallowCopy(Self, Result);
1668Inc(Result);
1669end;
1670
1671class function BigInteger.Add(const Left, Right: BigInteger): BigInteger;
1672begin
1673Add(Left, Right, Result);
1674end;
1675
1676class procedure BigInteger.Add(const Left, Right: BigInteger; var Result: BigInteger);
1677var
1678LSize, RSize, ResSize: Integer;
1679LSign, RSign, ResSign: Integer;
1680NewSize: Integer;
1681Comparison: Integer;
1682ResData: TMagnitude;
1683begin
1684if not Assigned(Left.FData) then
1685begin
1686Result.FSize := Right.FSize;
1687Result.FData := Right.FData;
1688Exit;
1689end
1690else if not Assigned(Right.FData) then
1691begin
1692Result.FSize := Left.FSize;
1693Result.FData := Left.FData;
1694Exit;
1695end;
1696
1697LSize := Left.FSize and SizeMask;
1698RSize := Right.FSize and SizeMask;
1699LSign := Left.FSize and SignMask;
1700RSign := Right.FSize and SignMask;
1701ResSize := IntMax(LSize, RSize) + 1;
1702AllocNewMagnitude(ResData, ResSize);
1703
1704if LSign = RSign then
1705begin
1706// Same sign: add both magnitudes and transfer sign.
1707FInternalAdd(PLimb(Left.FData), PLimb(Right.FData), PLimb(ResData), LSize, RSize);
1708ResSign := LSign;
1709end
1710else
1711begin
1712Comparison := InternalCompare(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize);
1713
1714if Comparison = 0 then
1715begin
1716Result.FSize := 0;
1717Result.FData := nil;
1718Exit;
1719end;
1720
1721if Comparison < 0 then
1722begin
1723FInternalSubtract(PLimb(Right.FData), PLimb(Left.FData), PLimb(ResData), RSize, LSize);
1724ResSign := RSign;
1725end
1726else
1727begin
1728FInternalSubtract(PLimb(Left.FData), PLimb(Right.FData), PLimb(ResData), LSize, RSize);
1729ResSign := LSign;
1730end;
1731end;
1732
1733NewSize := ActualSize(PLimb(ResData), ResSize);
1734if NewSize = 0 then
1735begin
1736Result.FSize := 0;
1737Result.FData := nil;
1738end
1739else
1740begin
1741{$IFDEF RESETSIZE}
1742if NewSize < (2 * ResSize div 3) then
1743SetLength(ResData, NewSize);
1744{$ENDIF}
1745Result.FSize := NewSize or ResSign;
1746Result.FData := ResData;
1747end;
1748end;
1749
1750class operator BigInteger.Add(const Left, Right: BigInteger): BigInteger;
1751begin
1752Add(Left, Right, Result);
1753end;
1754
1755class procedure BigInteger.Binary;
1756begin
1757FBase := 2;
1758end;
1759
1760class procedure BigInteger.InternalAnd(Left, Right, Result: PLimb; LSize, RSize: Integer);
1761{$IFDEF PUREPASCAL}
1762var
1763I: Integer;
1764begin
1765if RSize > LSize then
1766RSize := LSize;
1767for I := 0 to RSize - 1 do
1768Result[I] := Left[I] and Right[I];
1769end;
1770{$ELSE !PUREPASCAL}
1771{$IFDEF WIN32}
1772asm
1773PUSH ESI
1774PUSH EDI
1775PUSH EBX
1776
1777MOV EBX,RSize
1778MOV EDI,LSize
1779
1780CMP EDI,EBX
1781JAE @SkipSwap
1782XCHG EBX,EDI
1783XCHG EAX,EDX
1784
1785@SkipSwap:
1786
1787MOV EDI,EBX
1788AND EDI,CUnrollMask
1789SHR EBX,CUnrollShift
1790JE @MainTail
1791
1792@MainLoop:
1793
1794MOV ESI,[EAX]
1795AND ESI,[EDX]
1796MOV [ECX],ESI
1797
1798MOV ESI,[EAX + CLimbSize]
1799AND ESI,[EDX + CLimbSize]
1800MOV [ECX + CLimbSize],ESI
1801
1802MOV ESI,[EAX + 2*CLimbSize]
1803AND ESI,[EDX + 2*CLimbSize]
1804MOV [ECX + 2*CLimbSize],ESI
1805
1806MOV ESI,[EAX + 3*CLimbSize]
1807AND ESI,[EDX + 3*CLimbSize]
1808MOV [ECX + 3*CLimbSize],ESI
1809
1810LEA EAX,[EAX + 4*CLimbSize]
1811LEA EDX,[EDX + 4*CLimbSize]
1812LEA ECX,[ECX + 4*CLimbSize]
1813DEC EBX
1814JNE @MainLoop
1815
1816@MainTail:
1817
1818LEA EAX,[EAX + EDI*CLimbSize]
1819LEA EDX,[EDX + EDI*CLimbSize]
1820LEA ECX,[ECX + EDI*CLimbSize]
1821LEA EBX,[@JumpsMain]
1822JMP [EBX + EDI*TYPE Pointer]
1823
1824.ALIGN 16
1825
1826@JumpsMain:
1827
1828DD @Exit
1829DD @Main1
1830DD @Main2
1831DD @Main3
1832
1833@Main3:
1834
1835MOV ESI,[EAX - 3*CLimbSize]
1836AND ESI,[EDX - 3*CLimbSize]
1837MOV [ECX - 3*CLimbSize],ESI
1838
1839@Main2:
1840
1841MOV ESI,[EAX - 2*CLimbSize]
1842AND ESI,[EDX - 2*CLimbSize]
1843MOV [ECX - 2*CLimbSize],ESI
1844
1845@Main1:
1846
1847MOV ESI,[EAX - CLimbSize]
1848AND ESI,[EDX - CLimbSize]
1849MOV [ECX - CLimbSize],ESI
1850
1851@Exit:
1852
1853POP EBX
1854POP EDI
1855POP ESI
1856end;
1857{$ELSE WIN64}
1858asm
1859MOV R10D,RSize
1860
1861CMP R9D,R10D
1862JAE @SkipSwap
1863XCHG R10D,R9D
1864XCHG RCX,RDX
1865
1866@SkipSwap:
1867
1868MOV R9D,R10D
1869AND R9D,CUnrollMask
1870SHR R10D,CUnrollShift
1871JE @MainTail
1872
1873@MainLoop:
1874
1875MOV RAX,[RCX]
1876AND RAX,[RDX]
1877MOV [R8],RAX
1878MOV RAX,[RCX + DLimbSize]
1879AND RAX,[RDX + DLimbSize]
1880MOV [R8 + DLimbSize],RAX
1881LEA RCX,[RCX + 2*DLimbSize]
1882LEA RDX,[RDX + 2*DLimbSize]
1883LEA R8,[R8 + 2*DLimbSize]
1884DEC R10D
1885JNE @MainLoop
1886
1887@MainTail:
1888
1889LEA RCX,[RCX + R9*CLimbSize]
1890LEA RDX,[RDX + R9*CLimbSize]
1891LEA R8,[R8 + R9*CLimbSize]
1892LEA R10,[@JumpsMain]
1893JMP [R10 + R9*TYPE Pointer]
1894
1895.ALIGN 16
1896
1897@JumpsMain:
1898
1899DQ @Exit
1900DQ @Main1
1901DQ @Main2
1902DQ @Main3
1903
1904@Main3:
1905
1906MOV EAX,[RCX - 3*CLimbSize]
1907AND EAX,[RDX - 3*CLimbSize]
1908MOV [R8 - 3*CLimbSize],EAX
1909
1910@Main2:
1911
1912MOV EAX,[RCX - 2*CLimbSize]
1913AND EAX,[RDX - 2*CLimbSize]
1914MOV [R8 - 2*CLimbSize],EAX
1915
1916@Main1:
1917
1918MOV EAX,[RCX - CLimbSize]
1919AND EAX,[RDX - CLimbSize]
1920MOV [R8 - CLimbSize],EAX
1921
1922@Exit:
1923
1924end;
1925{$ENDIF WIN64}
1926{$ENDIF !PUREPASCAL}
1927
1928class procedure BigInteger.InternalXor(Left, Right, Result: PLimb; LSize, RSize: Integer);
1929{$IFDEF PUREPASCAL}
1930var
1931I: Integer;
1932begin
1933if LSize < RSize then
1934begin
1935SwapIntegers(LSize, RSize);
1936SwapPLimbs(Left, Right);
1937end;
1938for I := 0 to RSize - 1 do
1939Result[I] := Left[I] xor Right[I];
1940for I := RSize to LSize - 1 do
1941Result[I] := Left[I];
1942end;
1943{$ELSE !PUREPASCAL}
1944{$IFDEF WIN32}
1945asm
1946PUSH ESI
1947PUSH EDI
1948PUSH EBX
1949
1950MOV EBX,RSize
1951MOV EDI,LSize
1952
1953CMP EDI,EBX
1954JAE @SkipSwap
1955XCHG EBX,EDI
1956XCHG EAX,EDX
1957
1958@SkipSwap:
1959
1960SUB EDI,EBX
1961PUSH EDI // Number of "tail" loops
1962MOV EDI,EBX
1963AND EDI,CUnrollMask
1964SHR EBX,CUnrollShift
1965JE @MainTail
1966
1967@MainLoop:
1968
1969MOV ESI,[EAX]
1970XOR ESI,[EDX]
1971MOV [ECX],ESI
1972
1973MOV ESI,[EAX + CLimbSize]
1974XOR ESI,[EDX + CLimbSize]
1975MOV [ECX + CLimbSize],ESI
1976
1977MOV ESI,[EAX + 2*CLimbSize]
1978XOR ESI,[EDX + 2*CLimbSize]
1979MOV [ECX + 2*CLimbSize],ESI
1980
1981MOV ESI,[EAX + 3*CLimbSize]
1982XOR ESI,[EDX + 3*CLimbSize]
1983MOV [ECX + 3*CLimbSize],ESI
1984
1985LEA EAX,[EAX + 4*CLimbSize]
1986LEA EDX,[EDX + 4*CLimbSize]
1987LEA ECX,[ECX + 4*CLimbSize]
1988DEC EBX
1989JNE @MainLoop
1990
1991@MainTail:
1992
1993LEA EAX,[EAX + EDI*CLimbSize]
1994LEA EDX,[EDX + EDI*CLimbSize]
1995LEA ECX,[ECX + EDI*CLimbSize]
1996LEA EBX,[@JumpsMain]
1997JMP [EBX + EDI*TYPE Pointer]
1998
1999.ALIGN 16
2000
2001@JumpsMain:
2002
2003DD @DoRestLoop
2004DD @Main1
2005DD @Main2
2006DD @Main3
2007
2008@Main3:
2009
2010MOV ESI,[EAX - 3*CLimbSize]
2011XOR ESI,[EDX - 3*CLimbSize]
2012MOV [ECX - 3*CLimbSize],ESI
2013
2014@Main2:
2015
2016MOV ESI,[EAX - 2*CLimbSize]
2017XOR ESI,[EDX - 2*CLimbSize]
2018MOV [ECX - 2*CLimbSize],ESI
2019
2020@Main1:
2021
2022MOV ESI,[EAX - CLimbSize]
2023XOR ESI,[EDX - CLimbSize]
2024MOV [ECX - CLimbSize],ESI
2025
2026@DoRestLoop:
2027
2028XOR EDX,EDX
2029POP EBX
2030MOV EDI,EBX
2031AND EDI,CUnrollMask
2032SHR EBX,CunrollShift
2033JE @RestLast3
2034
2035@RestLoop:
2036
2037MOV EDX,[EAX]
2038MOV [ECX],EDX
2039
2040MOV EDX,[EAX + CLimbSize]
2041MOV [ECX + CLimbSize],EDX
2042
2043MOV EDX,[EAX + 2*CLimbSize]
2044MOV [ECX + 2*CLimbSize],EDX
2045
2046MOV EDX,[EAX + 3*CLimbSize]
2047MOV [ECX + 3*CLimbSize],EDX
2048
2049LEA EAX,[EAX + 4*CLimbSize]
2050LEA ECX,[ECX + 4*CLimbSize]
2051DEC EBX
2052JNE @RestLoop
2053
2054@RestLast3:
2055
2056LEA EAX,[EAX + EDI*CLimbSize]
2057LEA ECX,[ECX + EDI*CLimbSize]
2058LEA EBX,[@RestJumps]
2059JMP [EBX + EDI*TYPE Pointer]
2060
2061.ALIGN 16
2062
2063@RestJumps:
2064
2065DD @Exit
2066DD @Rest1
2067DD @Rest2
2068DD @Rest3
2069
2070@Rest3:
2071
2072MOV EDX,[EAX - 3*CLimbSize]
2073MOV [ECX - 3*CLimbSize],EDX
2074
2075@Rest2:
2076
2077MOV EDX,[EAX - 2*CLimbSize]
2078MOV [ECX - 2*CLimbSize],EDX
2079
2080@Rest1:
2081
2082MOV EDX,[EAX - CLimbSize]
2083MOV [ECX - CLimbSize],EDX
2084
2085@Exit:
2086
2087POP EBX
2088POP EDI
2089POP ESI
2090end;
2091{$ELSE WIN64}
2092asm
2093MOV R10D,RSize
2094
2095CMP R9D,R10D
2096JAE @SkipSwap
2097XCHG R10D,R9D
2098XCHG RCX,RDX
2099
2100@SkipSwap:
2101
2102SUB R9D,R10D
2103PUSH R9
2104MOV R9D,R10D
2105AND R9D,CUnrollMask
2106SHR R10D,CUnrollShift
2107JE @MainTail
2108
2109@MainLoop:
2110
2111MOV RAX,[RCX]
2112XOR RAX,[RDX]
2113MOV [R8],RAX
2114
2115MOV RAX,[RCX + DLimbSize]
2116XOR RAX,[RDX + DLimbSize]
2117MOV [R8 + DLimbSize],RAX
2118
2119LEA RCX,[RCX + 2*DLimbSize]
2120LEA RDX,[RDX + 2*DLimbSize]
2121LEA R8,[R8 + 2*DLimbSize]
2122DEC R10D
2123JNE @MainLoop
2124
2125@MainTail:
2126
2127LEA RCX,[RCX + R9*CLimbSize]
2128LEA RDX,[RDX + R9*CLimbSize]
2129LEA R8,[R8 + R9*CLimbSize]
2130LEA R10,[@JumpsMain]
2131JMP [R10 + R9*TYPE Pointer]
2132
2133@JumpsMain:
2134
2135DQ @DoRestLoop
2136DQ @Main1
2137DQ @Main2
2138DQ @Main3
2139
2140@Main3:
2141
2142MOV EAX,[RCX - 3*CLimbSize]
2143XOR EAX,[RDX - 3*CLimbSize]
2144MOV [R8 - 3*CLimbSize],EAX
2145
2146@Main2:
2147
2148MOV EAX,[RCX - 2*CLimbSize]
2149XOR EAX,[RDX - 2*CLimbSize]
2150MOV [R8 - 2*CLimbSize],EAX
2151
2152@Main1:
2153
2154MOV EAX,[RCX - CLimbSize]
2155XOR EAX,[RDX - CLimbSize]
2156MOV [R8 - CLimbSize],EAX
2157
2158@DoRestLoop:
2159
2160POP R10
2161TEST R10D,R10D
2162JE @Exit
2163MOV R9D,R10D
2164AND R9D,CUnrollMask
2165SHR R10D,CUnrollShift
2166JE @RestLast3
2167
2168@RestLoop:
2169
2170MOV RAX,[RCX]
2171MOV [R8],RAX
2172
2173MOV RAX,[RCX + DLimbSize]
2174MOV [R8 + DLimbSize],RAX
2175
2176LEA RCX,[RCX + 2*DLimbSize]
2177LEA R8,[R8 + 2*DLimbSize]
2178DEC R10D
2179JNE @RestLoop
2180
2181@RestLast3:
2182
2183LEA RCX,[RCX + R9*CLimbSize]
2184LEA R8,[R8 + R9*CLimbSize]
2185LEA R10,[@RestJumps]
2186JMP [R10 + R9*TYPE Pointer]
2187
2188@RestJumps:
2189
2190DQ @Exit
2191DQ @Rest1
2192DQ @Rest2
2193DQ @Rest3
2194
2195@Rest3:
2196
2197MOV EAX,[RCX - 3*CLimbSize]
2198MOV [R8 - 3*CLimbSize],EAX
2199
2200@Rest2:
2201
2202MOV EAX,[RCX - 2*CLimbSize]
2203MOV [R8 - 2*CLimbSize],EAX
2204
2205@Rest1:
2206
2207MOV EAX,[RCX - CLimbSize]
2208MOV [R8 - CLimbSize],EAX
2209
2210@Exit:
2211
2212end;
2213{$ENDIF WIN64}
2214{$ENDIF !PUREPASCAL}
2215
2216class procedure BigInteger.InternalOr(Left, Right, Result: PLimb; LSize, RSize: Integer);
2217{$IFDEF PUREPASCAL}
2218var
2219I: Integer;
2220begin
2221// Ensure Left/LSize belong to largest BigInteger.
2222if LSize < RSize then
2223begin
2224SwapIntegers(LSize, RSize);
2225SwapPLimbs(Left, Right);
2226end;
2227for I := 0 to RSize - 1 do
2228Result[I] := Left[I] or Right[I];
2229for I := RSize to LSize - 1 do
2230Result[I] := Left[I];
2231end;
2232{$ELSE !PUREPASCAL}
2233{$IFDEF WIN32}
2234asm
2235PUSH ESI
2236PUSH EDI
2237PUSH EBX
2238
2239MOV EBX,RSize
2240MOV EDI,LSize
2241
2242CMP EDI,EBX
2243JAE @SkipSwap
2244XCHG EBX,EDI
2245XCHG EAX,EDX
2246
2247@SkipSwap:
2248
2249SUB EDI,EBX
2250PUSH EDI // Number of "rest" loops
2251MOV EDI,EBX
2252AND EDI,CUnrollMask
2253SHR EBX,CUnrollShift
2254JE @MainTail
2255
2256@MainLoop:
2257
2258MOV ESI,[EAX]
2259OR ESI,[EDX]
2260MOV [ECX],ESI
2261
2262MOV ESI,[EAX + CLimbSize]
2263OR ESI,[EDX + CLimbSize]
2264MOV [ECX + CLimbSize],ESI
2265
2266MOV ESI,[EAX + 2*CLimbSize]
2267OR ESI,[EDX + 2*CLimbSize]
2268MOV [ECX + 2*CLimbSize],ESI
2269
2270MOV ESI,[EAX + 3*CLimbSize]
2271OR ESI,[EDX + 3*CLimbSize]
2272MOV [ECX + 3*CLimbSize],ESI
2273
2274LEA EAX,[EAX + 4*CLimbSize]
2275LEA EDX,[EDX + 4*CLimbSize]
2276LEA ECX,[ECX + 4*CLimbSize]
2277DEC EBX
2278JNE @MainLoop
2279
2280@MainTail:
2281
2282LEA EAX,[EAX + EDI*CLimbSize]
2283LEA EDX,[EDX + EDI*CLimbSize]
2284LEA ECX,[ECX + EDI*CLimbSize]
2285LEA EBX,[@JumpsMain]
2286JMP [EBX + EDI*TYPE Pointer]
2287
2288.ALIGN 16
2289
2290@JumpsMain:
2291
2292DD @DoRestLoop
2293DD @Main1
2294DD @Main2
2295DD @Main3
2296
2297@Main3:
2298
2299MOV ESI,[EAX - 3*CLimbSize]
2300OR ESI,[EDX - 3*CLimbSize]
2301MOV [ECX - 3*CLimbSize],ESI
2302
2303@Main2:
2304
2305MOV ESI,[EAX - 2*CLimbSize]
2306OR ESI,[EDX - 2*CLimbSize]
2307MOV [ECX - 2*CLimbSize],ESI
2308
2309@Main1:
2310
2311MOV ESI,[EAX - CLimbSize]
2312OR ESI,[EDX - CLimbSize]
2313MOV [ECX - CLimbSize],ESI
2314
2315@DoRestLoop:
2316
2317XOR EDX,EDX
2318POP EBX
2319MOV EDI,EBX
2320AND EDI,CUnrollMask
2321SHR EBX,CUnrollShift
2322JE @RestLast3
2323
2324@RestLoop:
2325
2326MOV EDX,[EAX]
2327MOV [ECX],EDX
2328
2329MOV EDX,[EAX + CLimbSize]
2330MOV [ECX + CLimbSize],EDX
2331
2332MOV EDX,[EAX + 2*CLimbSize]
2333MOV [ECX + 2*CLimbSize],EDX
2334
2335MOV EDX,[EAX + 3*CLimbSize]
2336MOV [ECX + 3*CLimbSize],EDX
2337
2338LEA EAX,[EAX + 4*CLimbSize]
2339LEA ECX,[ECX + 4*CLimbSize]
2340DEC EBX
2341JNE @RestLoop
2342
2343@RestLast3:
2344
2345LEA EAX,[EAX + EDI*CLimbSize]
2346LEA ECX,[ECX + EDI*CLimbSize]
2347LEA EBX,[@RestJumps]
2348JMP [EBX + EDI*TYPE Pointer]
2349
2350.ALIGN 16
2351
2352@RestJumps:
2353
2354DD @Exit
2355DD @Rest1
2356DD @Rest2
2357DD @Rest3
2358
2359@Rest3:
2360
2361MOV EDX,[EAX - 3*CLimbSize]
2362MOV [ECX - 3*CLimbSize],EDX
2363
2364@Rest2:
2365
2366MOV EDX,[EAX - 2*CLimbSize]
2367MOV [ECX - 2*CLimbSize],EDX
2368
2369@Rest1:
2370
2371MOV EDX,[EAX - CLimbSize]
2372MOV [ECX - CLimbSize],EDX
2373
2374@Exit:
2375
2376POP EBX
2377POP EDI
2378POP ESI
2379end;
2380{$ELSE WIN64}
2381asm
2382MOV R10D,RSize
2383
2384CMP R9D,R10D
2385JAE @SkipSwap
2386XCHG R10D,R9D
2387XCHG RCX,RDX
2388
2389@SkipSwap:
2390
2391SUB R9D,R10D
2392PUSH R9
2393MOV R9D,R10D
2394AND R9D,CUnrollMask
2395SHR R10D,CUnrollShift
2396JE @MainTail
2397
2398@MainLoop:
2399
2400MOV RAX,[RCX]
2401OR RAX,[RDX]
2402MOV [R8],RAX
2403
2404MOV RAX,[RCX + DLimbSize]
2405OR RAX,[RDX + DLimbSize]
2406MOV [R8 + DLimbSize],RAX
2407
2408LEA RCX,[RCX + 2*DLimbSize]
2409LEA RDX,[RDX + 2*DLimbSize]
2410LEA R8,[R8 + 2*DLimbSize]
2411DEC R10D
2412JNE @MainLoop
2413
2414@MainTail:
2415
2416LEA RCX,[RCX + R9*CLimbSize]
2417LEA RDX,[RDX + R9*CLimbSize]
2418LEA R8,[R8 + R9*CLimbSize]
2419LEA R10,[@JumpsMain]
2420JMP [R10 + R9*TYPE Pointer]
2421
2422// Align jump table manually, with NOPs.
2423
2424DB $90,$90,$90,$90,$90,$90
2425
2426@JumpsMain:
2427
2428DQ @DoRestLoop
2429DQ @Main1
2430DQ @Main2
2431DQ @Main3
2432
2433@Main3:
2434
2435MOV EAX,[RCX - 3*CLimbSize]
2436OR EAX,[RDX - 3*CLimbSize]
2437MOV [R8 - 3*CLimbSize],EAX
2438
2439@Main2:
2440
2441MOV EAX,[RCX - 2*CLimbSize]
2442OR EAX,[RDX - 2*CLimbSize]
2443MOV [R8 - 2*CLimbSize],EAX
2444
2445@Main1:
2446
2447MOV EAX,[RCX - CLimbSize]
2448OR EAX,[RDX - CLimbSize]
2449MOV [R8 - CLimbSize],EAX
2450
2451@DoRestLoop:
2452
2453POP R10
2454TEST R10D,R10D
2455JE @Exit
2456MOV R9D,R10D
2457AND R9D,CUnrollMask
2458SHR R10D,CUnrollShift
2459JE @RestLast3
2460
2461@RestLoop:
2462
2463MOV RAX,[RCX]
2464MOV [R8],RAX
2465
2466MOV RAX,[RCX + DLimbSize]
2467MOV [R8 + DLimbSize],RAX
2468
2469LEA RCX,[RCX + 2*DLimbSize]
2470LEA R8,[R8 + 2*DLimbSize]
2471DEC R10D
2472JNE @RestLoop
2473
2474@RestLast3:
2475
2476LEA RCX,[RCX + R9*CLimbSize]
2477LEA R8,[R8 + R9*CLimbSize]
2478LEA R10,[@RestJumps]
2479JMP [R10 + R9*TYPE Pointer]
2480
2481// Align jump table manually, with NOPs.
2482
2483// -- Aligned.
2484
2485@RestJumps:
2486
2487DQ @Exit
2488DQ @Rest1
2489DQ @Rest2
2490DQ @Rest3
2491
2492@Rest3:
2493
2494MOV EAX,[RCX - 3*CLimbSize]
2495MOV [R8 - 3*CLimbSize],EAX
2496
2497@Rest2:
2498
2499MOV EAX,[RCX - 2*CLimbSize]
2500MOV [R8 - 2*CLimbSize],EAX
2501
2502@Rest1:
2503
2504MOV EAX,[RCX - CLimbSize]
2505MOV [R8 - CLimbSize],EAX
2506
2507@Exit:
2508
2509end;
2510{$ENDIF WIN64}
2511{$ENDIF !PUREPASCAL}
2512
2513class procedure BigInteger.InternalAndNot(Left, Right, Result: PLimb; LSize, RSize: Integer);
2514{$IFDEF PUREPASCAL}
2515var
2516I: Integer;
2517begin
2518
2519// Note: AndNot is - of course - not commutative.
2520if LSize < RSize then
2521RSize := LSize;
2522for I := 0 to RSize - 1 do
2523Result[I] := not Right[I] and Left[I];
2524for I := RSize to LSize - 1 do
2525Result[I] := Left[I];
2526end;
2527{$ELSE !PUREPASCAL}
2528{$IFDEF WIN32}
2529asm
2530PUSH ESI
2531PUSH EDI
2532PUSH EBX
2533
2534MOV EBX,RSize
2535MOV EDI,LSize
2536
2537CMP EDI,EBX
2538JAE @SkipSwap
2539MOV EBX,EDI
2540
2541@SkipSwap:
2542
2543SUB EDI,EBX
2544PUSH EDI // Number of "rest" loops
2545MOV EDI,EBX
2546AND EDI,CUnrollMask
2547SHR EBX,CUnrollShift
2548JE @MainTail
2549
2550@MainLoop:
2551
2552MOV ESI,[EDX]
2553NOT ESI
2554AND ESI,[EAX]
2555MOV [ECX],ESI
2556
2557MOV ESI,[EDX + CLimbSize]
2558NOT ESI
2559AND ESI,[EAX + CLimbSize]
2560MOV [ECX + CLimbSize],ESI
2561
2562MOV ESI,[EDX + 2*CLimbSize]
2563NOT ESI
2564AND ESI,[EAX + 2*CLimbSize]
2565MOV [ECX + 2*CLimbSize],ESI
2566
2567MOV ESI,[EDX + 3*CLimbSize]
2568NOT ESI
2569AND ESI,[EAX + 3*CLimbSize]
2570MOV [ECX + 3*CLimbSize],ESI
2571
2572LEA EAX,[EAX + 4*CLimbSize]
2573LEA EDX,[EDX + 4*CLimbSize]
2574LEA ECX,[ECX + 4*CLimbSize]
2575DEC EBX
2576JNE @MainLoop
2577
2578@MainTail:
2579
2580LEA EAX,[EAX + EDI*CLimbSize]
2581LEA EDX,[EDX + EDI*CLimbSize]
2582LEA ECX,[ECX + EDI*CLimbSize]
2583LEA EBX,[@JumpsMain]
2584JMP [EBX + EDI*TYPE Pointer]
2585
2586.ALIGN 16
2587
2588@JumpsMain:
2589
2590DD @DoRestLoop
2591DD @Main1
2592DD @Main2
2593DD @Main3
2594
2595@Main3:
2596
2597MOV ESI,[EDX - 3*CLimbSize]
2598NOT ESI
2599AND ESI,[EAX - 3*CLimbSize]
2600MOV [ECX - 3*CLimbSize],ESI
2601
2602@Main2:
2603
2604MOV ESI,[EDX - 2*CLimbSize]
2605NOT ESI
2606AND ESI,[EAX - 2*CLimbSize]
2607MOV [ECX - 2*CLimbSize],ESI
2608
2609@Main1:
2610
2611MOV ESI,[EDX - CLimbSize]
2612NOT ESI
2613AND ESI,[EAX - CLimbSize]
2614MOV [ECX - CLimbSize],ESI
2615
2616@DoRestLoop:
2617
2618XOR EDX,EDX
2619POP EBX
2620MOV EDI,EBX
2621AND EDI,CUnrollMask
2622SHR EBX,CUnrollShift
2623JE @RestLast3
2624
2625@RestLoop:
2626
2627// X AND NOT 0 = X AND -1 = X
2628MOV EDX,[EAX]
2629MOV [ECX],EDX
2630
2631MOV EDX,[EAX + CLimbSize]
2632MOV [ECX + CLimbSize],EDX
2633
2634MOV EDX,[EAX + 2*CLimbSize]
2635MOV [ECX + 2*CLimbSize],EDX
2636
2637MOV EDX,[EAX + 3*CLimbSize]
2638MOV [ECX + 3*CLimbSize],EDX
2639
2640LEA EAX,[EAX + 4*CLimbSize]
2641LEA ECX,[ECX + 4*CLimbSize]
2642DEC EBX
2643JNE @RestLoop
2644
2645@RestLast3:
2646
2647LEA EAX,[EAX + EDI*CLimbSize]
2648LEA ECX,[ECX + EDI*CLimbSize]
2649LEA EBX,[@RestJumps]
2650JMP [EBX + EDI*TYPE Pointer]
2651
2652// Align jump table manually, with NOPs.
2653
2654@RestJumps:
2655
2656DD @Exit
2657DD @Rest1
2658DD @Rest2
2659DD @Rest3
2660
2661@Rest3:
2662
2663MOV EDX,[EAX - 3*CLimbSize]
2664MOV [ECX - 3*CLimbSize],EDX
2665
2666@Rest2:
2667
2668MOV EDX,[EAX - 2*CLimbSize]
2669MOV [ECX - 2*CLimbSize],EDX
2670
2671@Rest1:
2672
2673MOV EDX,[EAX - CLimbSize]
2674MOV [ECX - CLimbSize],EDX
2675
2676@Exit:
2677
2678POP EBX
2679POP EDI
2680POP ESI
2681end;
2682{$ELSE WIN64}
2683asm
2684MOV R10D,RSize
2685
2686CMP R9D,R10D
2687JAE @SkipSwap
2688MOV R10D,R9D
2689
2690@SkipSwap:
2691
2692SUB R9D,R10D
2693PUSH R9
2694MOV R9D,R10D
2695AND R9D,CUnrollMask
2696SHR R10D,CUnrollShift
2697JE @MainTail
2698
2699@MainLoop:
2700
2701MOV RAX,[RDX]
2702NOT RAX
2703AND RAX,[RCX]
2704MOV [R8],RAX
2705
2706MOV RAX,[RDX + DLimbSize]
2707NOT RAX
2708AND RAX,[RCX + DLimbSize]
2709MOV [R8 + DLimbSize],RAX
2710
2711LEA RCX,[RCX + 2*DLimbSize]
2712LEA RDX,[RDX + 2*DLimbSize]
2713LEA R8,[R8 + 2*DLimbSize]
2714DEC R10D
2715JNE @MainLoop
2716
2717@MainTail:
2718
2719LEA RCX,[RCX + R9*CLimbSize]
2720LEA RDX,[RDX + R9*CLimbSize]
2721LEA R8,[R8 + R9*CLimbSize]
2722LEA R10,[@JumpsMain]
2723JMP [R10 + R9*TYPE Pointer]
2724
2725// Align jump table manually, with NOPs.
2726
2727DB $90,$90,$90
2728
2729@JumpsMain:
2730
2731DQ @DoRestLoop
2732DQ @Main1
2733DQ @Main2
2734DQ @Main3
2735
2736@Main3:
2737
2738MOV EAX,[RDX - 3*CLimbSize]
2739NOT EAX
2740AND EAX,[RCX - 3*CLimbSize]
2741MOV [R8 - 3*CLimbSize],EAX
2742
2743@Main2:
2744
2745MOV EAX,[RDX - 2*CLimbSize]
2746NOT EAX
2747AND EAX,[RCX - 2*CLimbSize]
2748MOV [R8 - 2*CLimbSize],EAX
2749
2750@Main1:
2751
2752MOV EAX,[RDX - CLimbSize]
2753NOT EAX
2754AND EAX,[RCX - CLimbSize]
2755MOV [R8 - CLimbSize],EAX
2756
2757@DoRestLoop:
2758
2759POP R10
2760TEST R10D,R10D
2761JE @Exit
2762MOV R9D,R10D
2763AND R9D,CUnrollMask
2764SHR R10D,CUnrollShift
2765JE @RestLast3
2766
2767@RestLoop:
2768
2769// X AND NOT 0 = X AND -1 = X
2770
2771MOV RAX,[RCX]
2772MOV RDX,[RCX + DLimbSize]
2773MOV [R8],RAX
2774MOV [R8 + DLimbSize],RDX
2775
2776LEA RCX,[RCX + 2*DLimbSize]
2777LEA R8,[R8 + 2*DLimbSize]
2778DEC R10D
2779JNE @RestLoop
2780
2781@RestLast3:
2782
2783LEA RCX,[RCX + R9*CLimbSize]
2784LEA R8,[R8 + R9*CLimbSize]
2785LEA R10,[@RestJumps]
2786JMP [R10 + R9*TYPE Pointer]
2787
2788// Align jump table manually, with NOPs.
2789
2790DB $90,$90
2791
2792@RestJumps:
2793
2794DQ @Exit
2795DQ @Rest1
2796DQ @Rest2
2797DQ @Rest3
2798
2799@Rest3:
2800
2801MOV EAX,[RCX - 3*CLimbSize]
2802MOV [R8 - 3*CLimbSize],EAX
2803
2804@Rest2:
2805
2806MOV EAX,[RCX - 2*CLimbSize]
2807MOV [R8 - 2*CLimbSize],EAX
2808
2809@Rest1:
2810
2811MOV EAX,[RCX - CLimbSize]
2812MOV [R8 - CLimbSize],EAX
2813
2814@Exit:
2815
2816end;
2817{$ENDIF WIN64}
2818{$ENDIF !PUREPASCAL}
2819
2820class procedure BigInteger.InternalNotAnd(Left, Right, Result: PLimb; LSize, RSize: Integer);
2821begin
2822InternalAndNot(Right, Left, Result, RSize, LSize);
2823end;
2824
2825class operator BigInteger.BitwiseAnd(const Left, Right: BigInteger): BigInteger;
2826begin
2827
2828// Special handling for 0.
2829if (Left.FData = nil) or (Right.FData = nil) then
2830begin
2831Result.FData := nil;
2832Result.FSize := 0;
2833Exit;
2834end;
2835
2836InternalBitwise(Left, Right, Result, InternalAnd, InternalOr, InternalAndNot);
2837end;
2838
2839class operator BigInteger.BitwiseOr(const Left, Right: BigInteger): BigInteger;
2840begin
2841
2842// Special handling for 0.
2843if Left.FData = nil then
2844begin
2845Result.FSize := Right.FSize;
2846Result.FData := Right.FData;
2847Exit;
2848end
2849else if Right.FData = nil then
2850begin
2851Result.FSize := Left.FSize;
2852Result.FData := Left.FData;
2853Exit;
2854end;
2855
2856InternalBitwise(Left, Right, Result, InternalOr, InternalAnd, InternalNotAnd);
2857end;
2858
2859class operator BigInteger.BitwiseXor(const Left, Right: BigInteger): BigInteger;
2860begin
2861
2862// Special handling for 0.
2863if Left.FData = nil then
2864begin
2865ShallowCopy(Right, Result);
2866Exit;
2867end
2868else if Right.FData = nil then
2869begin
2870ShallowCopy(Left, Result);
2871Exit;
2872end;
2873
2874InternalBitwise(Left, Right, Result, InternalXor, InternalXor, InternalXor);
2875end;
2876
2877function BigInteger.Clone: BigInteger;
2878begin
2879DeepCopy(Self, Result);
2880end;
2881
2882class procedure BigInteger.Compact(var Data: TMagnitude; var Size: Integer);
2883var
2884NewSize: Integer;
2885begin
2886if Data = nil then
2887begin
2888Size := 0;
2889Exit;
2890end;
2891
2892NewSize := ActualSize(PLimb(Data), Size and SizeMask);
2893if NewSize < (Size and SizeMask) then
2894begin
2895if NewSize = 0 then
2896begin
2897Size := 0;
2898Data := nil;
2899end
2900else
2901begin
2902Size := SignBitOf(Size) or NewSize;
2903{$IFDEF RESETSIZE}
2904SetLength(Data, (NewSize + 4) and CapacityMask);
2905{$ENDIF}
2906end;
2907end;
2908end;
2909
2910procedure BigInteger.Compact;
2911begin
2912Compact(FData, FSize);
2913end;
2914
2915class function BigInteger.Compare(const Left, Right: BigInteger): Integer;
2916const
2917Results: array[Boolean] of Integer = (-1, 1);
2918var
2919LSize, RSize: Integer;
2920begin
2921if Left.FData = nil then
2922if Right.FData = nil then
2923Exit(0) // Compare(0, 0) = 0
2924else
2925Exit(Results[Right.FSize < 0]) // Compare(0, negative) = 1
2926else if Right.FData = nil then
2927Exit(Results[Left.FSize > 0]); // Compare(positive, 0) = 1
2928
2929if ((Left.FSize xor Right.FSize) and SignMask) <> 0 then
2930Exit(Results[Left.FSize > 0]); // Compare(positive, negative) = 1; Compare(negative, positive) = -1
2931
2932// Same sign:
2933LSize := Left.FSize and SizeMask;
2934RSize := Right.FSize and SizeMask;
2935Result := InternalCompare(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize);
2936
2937if Left.FSize < 0 then
2938Result := -Result;
2939end;
2940
2941constructor BigInteger.Create(const Value: BigInteger);
2942begin
2943Self.FSize := Value.FSize;
2944Self.FData := Value.FData;
2945end;
2946
2947constructor BigInteger.Create(const Magnitude: TMagnitude; Negative: Boolean);
2948begin
2949FSize := Length(Magnitude) or (Ord(Negative) * SignMask);
2950FData := Copy(Magnitude); // Must copy; otherwise modifying magnitude would modify this BigInteger.
2951Compact;
2952end;
2953
2954constructor BigInteger.Create(const Value: Int32);
2955begin
2956if (Value >= Low(ValueCache)) and (Value <= High(ValueCache)) then
2957Self := ValueCache[Value]
2958else
2959begin
2960Create(UInt32(System.Abs(Value)));
2961if Value < 0 then
2962FSize := FSize or SignMask;
2963Compact;
2964end;
2965end;
2966
2967constructor BigInteger.Create(const Value: Int64);
2968begin
2969if (Value >= Low(ValueCache)) and (Value <= High(ValueCache)) then
2970Self := ValueCache[Value]
2971else
2972begin
2973Create(UInt64(System.Abs(Value)));
2974if Value < 0 then
2975FSize := FSize or SignMask;
2976Compact;
2977end;
2978end;
2979
2980constructor BigInteger.Create(const Value: Cardinal);
2981begin
2982if Value <= UInt32(High(ValueCache)) then
2983Self := ValueCache[Value]
2984else
2985begin
2986if Value <> 0 then
2987begin
2988FSize := 1;
2989SetLength(FData, 4);
2990FData[0] := Value;
2991end
2992else
2993begin
2994FData := nil;
2995FSize := 0;
2996end;
2997Compact;
2998end;
2999end;
3000
3001constructor BigInteger.Create(const Value: UInt64);
3002begin
3003if Value <= High(ValueCache) then
3004Self := ValueCache[Value]
3005else
3006begin
3007FData := nil;
3008if Value <> 0 then
3009begin
3010if Value > High(UInt32) then
3011FSize := CUInt64Limbs
3012else
3013FSize := 1;
3014SetLength(FData, 4);
3015Move(Value, FData[0], SizeOf(Value));
3016end
3017else
3018begin
3019FData := nil;
3020FSize := 0;
3021end;
3022Compact;
3023end;
3024end;
3025
3026const
3027CMantissaBits = 52;
3028CMaxShift = 62;
3029
3030constructor BigInteger.Create(const Value: Double);
3031var
3032Exponent: Integer;
3033Mantissa: UInt64;
3034Sign, Guard, Round, Sticky: Boolean;
3035Shift: Integer;
3036ZeroExponentLimit: Integer;
3037begin
3038FSize := 0;
3039// FData := nil;
3040
3041// Error for special values.
3042if IsNan(Value) or IsInfinite(Value) then
3043Error(ecInvalidArgFloat, ['Double']);
3044
3045// Get the required values from TDoubleHelper.
3046Mantissa := GetSignificand(Value);
3047Exponent := GetExponent(Value);
3048Sign := PInt64(@Value)^ < 0;
3049
3050// Make 0 for denormal values and values < 0.5.
3051if FRoundingMode <> rmTruncate then
3052ZeroExponentLimit := -1
3053else
3054ZeroExponentLimit := 0;
3055
3056// Denormals and values with small exponent convert to 0.
3057if IsDenormal(Value) or (Exponent < ZeroExponentLimit) then
3058begin
3059Self := BigInteger.Zero;
3060Exit;
3061end;
3062
3063// Internal shift of the mantissa.
3064Shift := Exponent;
3065if Shift > CMaxShift then
3066Shift := CMaxShift;
3067
3068// Guard, Round and Sticky bits are used to determine rounding.
3069Guard := False;
3070Round := False;
3071Sticky := False;
3072if (FRoundingMode <> rmTruncate) and (Exponent < CMantissaBits) then
3073begin
3074// Round anything with a fraction >= 0.5 away from 0. No Round and Sticky bits required.
3075Guard := ((UInt64(1) shl (CMantissaBits - 1 - Exponent)) and Mantissa) <> 0;
3076
3077if FRoundingMode = rmRound then
3078begin
3079// Only if full rounding (like System.Round() performs) is required: Round any fraction > 0.5 away from 0.
3080Round := ((UInt64(1) shl (CMantissaBits - 2 - Exponent)) and Mantissa) <> 0;
3081Sticky := ((Int64(-1) shr (Exponent + (64 - CMantissaBits + 2))) and Mantissa) <> 0;
3082end;
3083end;
3084
3085// Shift mantissa left or right to get the most bits out of it before converting to BigInteger.
3086if Shift > CMantissaBits then
3087Mantissa := Mantissa shl (Shift - CMantissaBits)
3088else
3089Mantissa := Mantissa shr (CMantissaBits - Shift);
3090
3091// Round shifted mantissa.
3092if ((RoundingMode = rmSchool) and Guard) or
3093((RoundingMode = rmRound) and (Guard and (Round or Sticky))) then
3094Inc(Mantissa);
3095
3096// Turn shifted mantissa (a UInt64) into BigInteger.
3097Self := 0;
3098Self.Create(UInt64(Mantissa));
3099
3100// Shift left by the remaining value of the exponent.
3101if Exponent > Shift then
3102Self := Self shl (Exponent - Shift);
3103if Sign then
3104FSize := FSize or SignMask;
3105Compact;
3106end;
3107
3108{$IFNDEF NoAnsi}
3109constructor BigInteger.Create(const Value: PAnsiChar);
3110begin
3111if not TryParse(string(AnsiString(Value)), Self) then
3112Error(ecParse, [string(AnsiString(Value)), 'BigInteger']);
3113end;
3114{$ENDIF}
3115
3116constructor BigInteger.Create(const Value: PWideChar);
3117begin
3118if not TryParse(Value, Self) then
3119Error(ecParse, [Value, 'BigInteger']);
3120end;
3121
3122// Bytes are considered to contain value in two's complement format.
3123constructor BigInteger.Create(const Bytes: array of Byte);
3124var
3125Limbs: TMagnitude;
3126Negative: Boolean;
3127begin
3128Negative := Bytes[High(Bytes)] > Byte(High(Shortint));
3129SetLength(Limbs, (Length(Bytes) + 3) div 4);
3130if Negative then
3131Limbs[High(Limbs)] := TLimb(-1);
3132Move((@Bytes[0])^, PLimb(Limbs)^, Length(Bytes));
3133if Negative then
3134InternalNegate(PLimb(Limbs), PLimb(Limbs), Length(Limbs));
3135Create(Limbs, Negative);
3136Compact;
3137end;
3138
3139// This assumes sign-magnitude format.
3140constructor BigInteger.Create(const Limbs: array of TLimb; Negative: Boolean);
3141var
3142LSize: Integer;
3143begin
3144LSize := Length(Limbs);
3145if LSize > 0 then
3146begin
3147MakeSize(LSize);
3148FSize := LSize or (Ord(Negative) * SignMask);
3149CopyLimbs(@Limbs[0], PLimb(FData), LSize);
3150Compact;
3151end
3152else
3153FSize := 0;
3154end;
3155
3156constructor BigInteger.Create(NumBits: Integer; Random: TRandom32Proc);
3157var
3158I: Integer;
3159begin
3160if NumBits <= 0 then
3161begin
3162FSize := 0;
3163FData := nil;
3164Exit;
3165end;
3166
3167FSize := (NumBits + CLimbBits - 1) div CLimbBits;
3168SetLength(FData, (4 * FSize + 3) div 4);
3169for I := 0 to FSize - 1 do
3170FData[I] := Random();
3171
3172// At most Numbits bits, so mask top limb.
3173FData[FSize - 1] := FData[FSize - 1] and (1 shl (NumBits and CLimbBits) - 1);
3174Compact;
3175end;
3176
3177constructor BigInteger.Create(NumBits: Integer; const Random: IRandom);
3178var
3179Bytes: TArray<Byte>;
3180Bits: Byte;
3181begin
3182if NumBits = 0 then
3183begin
3184ShallowCopy(Zero, Self);
3185Exit;
3186end;
3187
3188SetLength(Bytes, (NumBits + 7) shr 3 + 1);
3189Random.NextBytes(Bytes);
3190
3191// One byte too many was allocated, to get a top byte of 0, i.e. always positive.
3192Bytes[High(Bytes)] := 0;
3193
3194// Set bits above required bit length to 0.
3195Bits := NumBits and $07;
3196if Bits = 0 then
3197Bits := 8;
3198Bytes[High(Bytes) - 1] := Bytes[High(Bytes) - 1] and ($FF shr (8 - Bits));
3199Create(Bytes);
3200Compact;
3201// Assert(BitLength <= Numbits, Format('BitLength (%d) >= NumBits (%d): %s', [BitLength, NumBits, Self.ToString(2)]));
3202end;
3203
3204function BigInteger.GetAllocated: Integer;
3205begin
3206Result := Length(FData);
3207end;
3208
3209function BigInteger.IsEven: Boolean;
3210begin
3211Result := IsZero or ((FData[0] and 1) = 0);
3212end;
3213
3214function BigInteger.IsNegative: Boolean;
3215begin
3216Result := Assigned(FData) and (FSize < 0);
3217end;
3218
3219function BigInteger.IsOne: Boolean;
3220begin
3221Result := Assigned(FData) and (FSize = 1) and (FData[0] = 1);
3222end;
3223
3224function BigInteger.IsPositive: Boolean;
3225begin
3226Result := Assigned(FData) and (FSize > 0);
3227end;
3228
3229function BigInteger.IsPowerOfTwo: Boolean;
3230var
3231FirstNonZeroIndex: Integer;
3232AHigh: Integer;
3233begin
3234AHigh := (FSize and SizeMask) - 1;
3235if (FData = nil) or not Velthuis.Numerics.IsPowerOfTwo(FData[AHigh]) then
3236Result := False
3237else
3238begin
3239FirstNonZeroIndex := 0;
3240
3241// All limbs below top one must be 0
3242while FData[FirstNonZeroIndex] = 0 do
3243Inc(FirstNonZeroIndex);
3244
3245// Top limb must be power of two.
3246Result := (FirstNonZeroIndex = AHigh);
3247end;
3248end;
3249
3250function BigInteger.GetSign: Integer;
3251begin
3252if FData = nil then
3253begin
3254FSize := 0;
3255Exit(0);
3256end;
3257
3258Result := 2 * Ord(FSize > 0) - 1;
3259end;
3260
3261function BigInteger.GetSize: Integer;
3262begin
3263if FData = nil then
3264FSize := 0;
3265Result := FSize and SizeMask;
3266end;
3267
3268function BigInteger.Data: PLimb;
3269begin
3270Result := PLimb(FData);
3271end;
3272
3273class operator BigInteger.GreaterThan(const Left, Right: BigInteger): Boolean;
3274begin
3275Result := Compare(Left, Right) > 0;
3276// Result := not (Left <= Right);
3277end;
3278
3279class operator BigInteger.GreaterThanOrEqual(const Left, Right: BigInteger): Boolean;
3280begin
3281Result := Compare(left, Right) >= 0;
3282end;
3283
3284// Divide and Conquer. For N = 100,000, this is 20 x as fast as a plain iterative multiplication.
3285class function BigInteger.Factorial(N: Integer): BigInteger;
3286
3287////////////////////////////////////////////////////////////////////////////
3288// Alternative algorithm: //
3289// [1 2 3 4 5 6 7 8 9] --> [1*9 2*8 3*7 4*6 5] = [9 16 21 24 5] //
3290// [9 16 21 24 5] --> [9*5 16*24 21] = [45 384 21] //
3291// [45 384 21] --> [45*21 384] = [945 384] //
3292// Result = 945 * 384 = 362880 = 9! //
3293// But that is a little slower than the following and needs an array //
3294// of (N div 2) BigIntegers. //
3295////////////////////////////////////////////////////////////////////////////
3296
3297////////////////////////////////////////////////////////////////////////////
3298// Optimization: every even integer is shifted right by 1. The end result //
3299// is shifted back by an equal amount of bits (n div 2). //
3300////////////////////////////////////////////////////////////////////////////
3301
3302function MultiplyRange(First, Last: Integer): BigInteger;
3303var
3304Split: Integer;
3305begin
3306if Last - First <= 3 then
3307begin
3308if Odd(First) then
3309case Last - First of
33100: Result := BigInteger(First);
33111: Result := BigInteger(First) * BigInteger(Last shr 1);
33122: Result := BigInteger(First) * BigInteger((First + 1) shr 1) * BigInteger(Last);
33133: Result := BigInteger(First) * BigInteger((First + 1) shr 1) * BigInteger(First + 2) * BigInteger(Last shr 1);
3314end
3315else
3316case Last - First of
33170: Result := BigInteger(First shr 1);
33181: Result := BigInteger(First shr 1) * BigInteger(Last);
33192: Result := BigInteger(First shr 1) * BigInteger(First + 1) * BigInteger(Last shr 1);
33203: Result := BigInteger(First shr 1) * BigInteger(First + 1) * BigInteger(First shr 1 + 1) * BigInteger(Last);
3321end;
3322end
3323else
3324begin
3325Split := (First + Last) shr 1;
3326Result := MultiplyRange(First, Split) * MultiplyRange(Split + 1, Last);
3327end;
3328end;
3329
3330begin
3331if N <= 0 then
3332Result := 0
3333else if N = 1 then
3334Result := 1
3335else
3336Result := MultiplyRange(2, N) shl (N shr 1);
3337end;
3338
3339// https://www.nayuki.io/page/fast-fibonacci-algorithms
3340// https://codegolf.stackexchange.com/questions/3191/write-the-fastest-fibonacci
3341// https://math.stackexchange.com/questions/1124590/need-help-understanding-fibonacci-fast-doubling-proof
3342class function BigInteger.Fibonacci(N: Integer): BigInteger;
3343
3344////////////////////////////////////////////////////////////////////////////////////////////////////
3345// So called "fast doubling". Relies on the following formulas: //
3346// //
3347// fib(2n) = fib(n)*(2*fib(n+1) – fib(n)) //
3348// fib(2n + 1) = fib(n)^2 + fib(n+1)^2 //
3349// //
3350// Another method relies on the fact that if we exponentiate the simple matrix below, we get: //
3351// //
3352// [ 1 1 ]^n = [ fib(n+1) fib(n) ] //
3353// [ 1 0 ] [ fib(n) fib(n-1) ] //
3354// //
3355// Most methods use exponentiation by squaring to exponentiate the matrix. But we must write //
3356// a (rather slow?) matrix multiplication algorithm, which generally makes it slower than //
3357// fast doubling. //
3358////////////////////////////////////////////////////////////////////////////////////////////////////
3359
3360var
3361FibOfN, FibOfNPlus1, Temp: BigInteger;
3362FibOf2N, FibOf2NPlus1: BigInteger;
3363Bit: Integer;
3364begin
3365FibOfN := BigInteger.Zero;
3366FibOfNPlus1 := BigInteger.One;
3367Bit := HighestOneBit(N);
3368Temp := N;
3369while Bit <> 0 do
3370begin
3371FibOf2N := FibOfN * ((FibOfNPlus1 shl 1) - FibOfN); // fib(2n) = fib(n) * (2 * fib(n + 1) - fib(n))
3372FibOf2NPlus1 := FibOfN * FibOfN + FibOfNPlus1 * FibOfNPlus1; // fib(2n + 1) = fib(n)^2 + fib(n + 1)^2
3373FibOfN := FibOf2N;
3374FibOfNPlus1 := FibOf2NPlus1;
3375
3376// Advance by one conditionally
3377if (N and Bit) <> 0 then
3378begin
3379Temp := FibOfN + FibOfNPlus1;
3380FibOfN := FibOfNPlus1;
3381FibOfNPlus1 := Temp;
3382end;
3383
3384Bit := Bit shr 1;
3385end;
3386Result := FibOfN;
3387end;
3388
3389// http://en.wikipedia.org/wiki/Binary_GCD_algorithm
3390class function BigInteger.GreatestCommonDivisor(const Left, Right: BigInteger): BigInteger;
3391var
3392Shift: Integer;
3393ALeft, ARight: BigInteger;
3394Temp: BigInteger;
3395begin
3396// GCD(left, 0) = left; GCD(0, right) = right; GCD(0, 0) = 0
3397if Left.IsZero then
3398Exit(Abs(Right));
3399if Right.IsZero then
3400Exit(Abs(Left));
3401
3402ALeft := Abs(Left);
3403ARight := Abs(Right);
3404
3405// Let Shift = Log2(K), where K is the greatest power of 2 dividing
3406// both ALeft and ARight.
3407Shift := IntMin(Left.LowestSetBit, Right.LowestSetBit);
3408ALeft := ALeft shr Shift;
3409ARight := ARight shr Shift;
3410
3411while ALeft.IsEven do
3412ALeft := ALeft shr 1;
3413
3414// Now, ALeft is always odd.
3415repeat
3416// Remove all factors of 2 in ARight, since they are not in common.
3417// ARight is not 0, so the loop will terminate
3418while ARight.IsEven do
3419ARight := ARight shr 1;
3420
3421// ALeft and ARight are both odd. Swap if necessary, so that ALeft <= ARight,
3422// then set ARight to ARight - ALeft (which is even).
3423if ALeft > ARight then
3424begin
3425// Swap ALeft and ARight.
3426Temp := ALeft;
3427Aleft := ARight;
3428ARight := Temp;
3429end;
3430ARight := ARight - ALeft;
3431until ARight = 0;
3432
3433// Restore common factors of 2.
3434Result := ALeft shl Shift;
3435end;
3436
3437class procedure BigInteger.Hexadecimal;
3438begin
3439FBase := 16;
3440end;
3441
3442class procedure BigInteger.Hex;
3443begin
3444FBase := 16;
3445end;
3446
3447class operator BigInteger.Implicit(const Value: Int32): BigInteger;
3448begin
3449// Note: Create will also get BigIntegers from the ValueCache, but this is a little faster.
3450if (Value >= Low(ValueCache)) and (Value <= High(ValueCache)) then
3451Result := ValueCache[Value]
3452else
3453Result := BigInteger.Create(Value);
3454end;
3455
3456class operator BigInteger.Implicit(const Value: UInt32): BigInteger;
3457begin
3458if Value <= UInt32(High(ValueCache)) then
3459Result := ValueCache[Value]
3460else
3461Result := BigInteger.Create(Value);
3462end;
3463
3464class operator BigInteger.Implicit(const Value: Int64): BigInteger;
3465begin
3466if (Value >= Low(ValueCache)) and (Value <= High(ValueCache)) then
3467Result := ValueCache[Value]
3468else
3469Result := BigInteger.Create(Value);
3470end;
3471
3472class operator BigInteger.Implicit(const Value: UInt64): BigInteger;
3473begin
3474if Value <= High(ValueCache) then
3475Result := ValueCache[Value]
3476else
3477Result := BigInteger.Create(Value);
3478end;
3479
3480class constructor BigInteger.Initialize;
3481var
3482I: Integer;
3483J: Integer;
3484LPower: BigInteger;
3485LMaxPower: BigInteger;
3486begin
3487for I := Low(ValueCache) to High(ValueCache) do
3488begin
3489if I <> 0 then
3490begin
3491SetLength(ValueCache[I].FData, 4);
3492if I < 0 then
3493begin
3494ValueCache[I].FData[0] := -I;
3495ValueCache[I].FSize := 1 or SignMask;
3496end
3497else
3498begin
3499ValueCache[I].FData[0] := I;
3500ValueCache[I].FSize := 1;
3501end;
3502end
3503else
3504begin
3505ValueCache[0].FData := nil;
3506Valuecache[0].FSize := 0;
3507end;
3508end;
3509MinusOne := ValueCache[-1];
3510Zero := ValueCache[0];
3511One := ValueCache[1];
3512Ten := ValueCache[10];
3513FBase := 10;
3514FRoundingMode := rmTruncate;
3515FLog2 := System.Ln(2.0);
3516{$IFNDEF PUREPASCAL}
3517// See comments for BigInteger.InternalAddEmu.
3518BigInteger.DetectPartialFlagsStall;
3519{$ELSE}
3520FInternalAdd := InternalAddPurePascal;
3521FInternalSubtract := InternalSubtractPurePascal;
3522{$ENDIF}
3523for I := Low(TNumberBase) to High(TNumberBase) do
3524begin
3525LMaxPower := CBaseInfos[I].MaxPower;
3526SetLength(CBasePowers[I], 10);
3527LPower := BigInteger.One;
3528for J := 0 to High(CBasePowers[I]) do
3529begin
3530CBasePowers[I, J] := LPower;
3531LPower := LPower * LMaxPower;
3532end;
3533// LMaxPower := BigInteger.Zero; // $$RV Rio: leak if not set to zero.
3534end;
3535end;
3536
3537class operator BigInteger.IntDivide(const Left, Right: BigInteger): BigInteger;
3538begin
3539Result := Divide(Left, Right);
3540end;
3541
3542class operator BigInteger.IntDivide(const Left: BigInteger; Right: UInt16): BigInteger;
3543begin
3544Result := Divide(Left, Right);
3545end;
3546
3547class operator BigInteger.IntDivide(const Left: BigInteger; Right: UInt32): BigInteger;
3548begin
3549Result := Divide(Left, Right);
3550end;
3551
3552{$IFNDEF PUREPASCAL}
3553class procedure BigInteger.InternalAddModified(Left, Right, Result: PLimb; LSize, RSize: Integer);
3554{$IFDEF WIN32}
3555asm
3556PUSH ESI
3557PUSH EDI
3558PUSH EBX
3559
3560MOV ESI,EAX // ESI: Left
3561MOV EDI,EDX // EDI: Right
3562MOV EBX,ECX // EBX: Result
3563
3564MOV ECX,RSize
3565MOV EDX,LSize
3566CMP EDX,ECX // Swap Left/Right and LSize/RSize if RSize > LSize
3567JAE @SkipSwap
3568XCHG ECX,EDX
3569XCHG ESI,EDI
3570
3571@SkipSwap:
3572
3573// Here: Left longer than or equal length as Right.
3574
3575SUB EDX,ECX
3576PUSH EDX // Difference in sizes --> length of rest loop.
3577MOV EDX,ECX // Smallest size.
3578AND EDX,CUnrollMask // Tail counter.
3579SHR ECX,CUnrollShift // Unrolled loop counter.
3580CLC
3581JE @MainTail
3582
3583// Intel proposal:
3584// Intel 64 and IA-32 Architectures Optimization Reference Manual
3585// 3.5.2.6 Partial Flag Register Stalls -- Example 3-29
3586
3587XOR EAX,EAX
3588
3589.ALIGN 16
3590
3591@MainLoop:
3592
3593// Unrolled main loop.
3594
3595ADD EAX,[ESI]
3596ADC EAX,[EDI]
3597MOV [EBX],EAX
3598
3599MOV EAX,[ESI + CLimbSize]
3600ADC EAX,[EDI + CLimbSize]
3601MOV [EBX + CLimbSize],EAX
3602
3603MOV EAX,[ESI + 2*CLimbSize]
3604ADC EAX,[EDI + 2*CLimbSize]
3605MOV [EBX + 2*CLimbSize],EAX
3606
3607MOV EAX,[ESI + 3*CLimbSize]
3608ADC EAX,[EDI + 3*CLimbSize]
3609MOV [EBX + 3*CLimbSize],EAX
3610
3611SETC AL
3612MOVZX EAX,AL
3613
3614LEA ESI,[ESI + CUnrollIncrement*CLimbSize]
3615LEA EDI,[EDI + CUnrollIncrement*CLimbSize]
3616LEA EBX,[EBX + CUnrollIncrement*CLimbSize]
3617
3618DEC ECX
3619JNZ @MainLoop
3620
3621@MainTail:
3622
3623LEA ESI,[ESI + EDX*CLimbSize]
3624LEA EDI,[EDI + EDX*CLimbSize]
3625LEA EBX,[EBX + EDX*CLimbSize]
3626
3627LEA ECX,[@JumpsMain]
3628JMP [ECX + EDX*TYPE Pointer]
3629
3630.ALIGN 4
3631
3632@JumpsMain:
3633
3634DD @DoRestLoop
3635DD @Main1
3636DD @Main2
3637DD @Main3
3638
3639@Main3:
3640
3641MOV EAX,[ESI - 3*CLimbSize]
3642ADC EAX,[EDI - 3*CLimbSize]
3643MOV [EBX - 3*CLimbSize],EAX
3644
3645@Main2:
3646
3647MOV EAX,[ESI - 2*CLimbSize]
3648ADC EAX,[EDI - 2*CLimbSize]
3649MOV [EBX - 2*CLimbSize],EAX
3650
3651@Main1:
3652
3653MOV EAX,[ESI - CLimbSize]
3654ADC EAX,[EDI - CLimbSize]
3655MOV [EBX - CLimbSize],EAX
3656
3657@DoRestLoop:
3658
3659SETC AL // Save Carry Flag
3660XOR EDI,EDI
3661POP ECX
3662MOV EDX,ECX
3663AND EDX,CUnrollMask // Tail counter
3664SHR ECX,CUnrollShift // Unrolled loop counter
3665ADD AL,255 // Restore Carry Flag.
3666JECXZ @RestLastN
3667
3668.ALIGN 16
3669
3670@RestLoop:
3671
3672/////////////////////////////////////////////////////////////////////
3673// Tests showed that branching out of the loop as soon as the //
3674// carry is clear (using JNC @label, where @label is in a second //
3675// loop that only copies and does not add anymore) actually makes //
3676// the code slightly SLOWER, most of the time. //
3677/////////////////////////////////////////////////////////////////////
3678
3679MOV EAX,[ESI]
3680ADC EAX,EDI
3681MOV [EBX],EAX
3682
3683MOV EAX,[ESI + CLimbSize]
3684ADC EAX,EDI
3685MOV [EBX + CLimbSize],EAX
3686
3687MOV EAX,[ESI + 2*CLimbSize]
3688ADC EAX,EDI
3689MOV [EBX + 2*CLimbSize],EAX
3690
3691MOV EAX,[ESI + 3*CLimbSize]
3692ADC EAX,EDI
3693MOV [EBX + 3*CLimbSize],EAX
3694
3695SETC AL
3696MOVZX EAX,AL
3697
3698LEA ESI,[ESI + CUnrollIncrement*CLimbSize]
3699LEA EBX,[EBX + CUnrollIncrement*CLimbSize]
3700
3701LOOP @RestLoop
3702
3703@RestLastN:
3704
3705LEA ESI,[ESI + EDX*CLimbSize]
3706LEA EBX,[EBX + EDX*CLimbSize]
3707
3708LEA ECX,[@RestJumps]
3709JMP [ECX + EDX*TYPE Pointer]
3710
3711.ALIGN 4
3712
3713@RestJumps:
3714
3715DD @LastLimb
3716DD @Rest1
3717DD @Rest2
3718DD @Rest3
3719
3720@Rest3:
3721
3722MOV EAX,[ESI - 3*CLimbSize]
3723ADC EAX,EDI
3724MOV [EBX - 3*CLimbSize],EAX
3725
3726@Rest2:
3727
3728MOV EAX,[ESI - 2*CLimbSize]
3729ADC EAX,EDI
3730MOV [EBX - 2*CLimbSize],EAX
3731
3732@Rest1:
3733
3734MOV EAX,[ESI - CLimbSize]
3735ADC EAX,EDI
3736MOV [EBX - CLimbSize],EAX
3737
3738@LastLimb:
3739
3740ADC EDI,EDI
3741MOV [EBX],EDI
3742
3743@Exit:
3744
3745POP EBX
3746POP EDI
3747POP ESI
3748end;
3749{$ELSE WIN64}
3750asm
3751MOV R10,RCX
3752MOV ECX,RSize
3753
3754CMP R9D,ECX
3755JAE @SkipSwap
3756XCHG ECX,R9D
3757XCHG R10,RDX
3758
3759@SkipSwap:
3760
3761SUB R9D,ECX
3762PUSH R9
3763
3764MOV R9D,ECX
3765AND R9D,CUnrollMask
3766SHR ECX,CUnrollShift
3767
3768CLC
3769JE @MainTail
3770
3771.ALIGN 16
3772
3773@MainLoop:
3774
3775MOV RAX,[R10]
3776ADC RAX,[RDX]
3777MOV [R8],RAX
3778
3779MOV RAX,[R10 + DLimbSize]
3780ADC RAX,[RDX + DLimbSize]
3781MOV [R8 + DLimbSize],RAX
3782
3783LEA R10,[R10 + 2*DLimbSize]
3784LEA RDX,[RDX + 2*DLimbSize]
3785LEA R8,[R8 + 2*DLimbSize]
3786
3787LEA RCX,[RCX - 1]
3788JECXZ @MainTail
3789JMP @MainLoop
3790
3791@MainTail:
3792
3793LEA RCX,[@MainJumps]
3794JMP [RCX + R9*TYPE Pointer]
3795
3796.ALIGN 16
3797
3798@MainJumps:
3799
3800DQ @DoRestLoop
3801DQ @Main1
3802DQ @Main2
3803DQ @Main3
3804
3805@Main3:
3806
3807MOV RAX,[R10]
3808ADC RAX,[RDX]
3809MOV [R8],RAX
3810
3811MOV EAX,[R10 + 2*CLimbSize]
3812ADC EAX,[RDX + 2*CLimbSize]
3813MOV [R8 + 2*CLimbSize],EAX
3814
3815LEA R10,[R10 + 3*CLimbSize]
3816LEA RDX,[RDX + 3*CLimbSize]
3817LEA R8,[R8 + 3*CLimbSize]
3818
3819JMP @DoRestLoop
3820
3821@Main2:
3822
3823MOV RAX,[R10]
3824ADC RAX,[RDX]
3825MOV [R8],RAX
3826
3827LEA R10,[R10 + 2*CLimbSize]
3828LEA RDX,[RDX + 2*CLimbSize]
3829LEA R8,[R8 + 2*CLimbSize]
3830
3831JMP @DoRestLoop
3832
3833@Main1:
3834
3835MOV EAX,[R10]
3836ADC EAX,[RDX]
3837MOV [R8],EAX
3838
3839LEA R10,[R10 + CLimbSize]
3840LEA RDX,[RDX + CLimbSize]
3841LEA R8,[R8 + CLimbSize]
3842
3843@DoRestLoop:
3844
3845SETC AL // Save Carry Flag
3846
3847XOR EDX,EDX
3848
3849POP RCX
3850MOV R9D,ECX
3851AND R9D,CUnrollMask
3852SHR ECX,CUnrollShift
3853
3854ADD AL,255 // Restore Carry Flag.
3855
3856JECXZ @RestLast3
3857
3858.ALIGN 16
3859
3860@RestLoop:
3861
3862MOV RAX,[R10]
3863ADC RAX,RDX
3864MOV [R8],RAX
3865
3866MOV RAX,[R10 + DLimbSize]
3867ADC RAX,RDX
3868MOV [R8 + DLimbSize],RAX
3869
3870LEA R10,[R10 + 2*DLimbSize]
3871LEA R8,[R8 + 2*DLimbSize]
3872
3873LEA RCX,[RCX - 1]
3874JECXZ @RestLast3
3875JMP @RestLoop
3876
3877@RestLast3:
3878
3879LEA RCX,[@RestJumps]
3880JMP [RCX + R9*TYPE Pointer]
3881
3882.ALIGN 16
3883
3884@RestJumps:
3885
3886DQ @LastLimb
3887DQ @Rest1
3888DQ @Rest2
3889DQ @Rest3
3890
3891@Rest3:
3892
3893MOV RAX,[R10]
3894ADC RAX,RDX
3895MOV [R8],RAX
3896
3897MOV EAX,[R10 + 2*CLimbSize]
3898ADC EAX,EDX
3899MOV [R8 + 2*CLimbSize],EAX
3900
3901LEA R8,[R8 + 3*CLimbSize]
3902
3903JMP @LastLimb
3904
3905@Rest2:
3906
3907MOV RAX,[R10]
3908ADC RAX,RDX
3909MOV [R8],RAX
3910
3911LEA R8,[R8 + 2*CLimbSize]
3912
3913JMP @LastLimb
3914
3915@Rest1:
3916
3917MOV EAX,[R10]
3918ADC EAX,EDX
3919MOV [R8],EAX
3920
3921LEA R8,[R8 + CLimbSize]
3922
3923@LastLimb:
3924
3925ADC EDX,EDX
3926MOV [R8],EDX
3927
3928@Exit:
3929
3930end;
3931{$ENDIF WIN32/WIN64}
3932
3933class procedure BigInteger.InternalAddPlain(Left, Right, Result: PLimb; LSize, RSize: Integer);
3934
3935////////////////////////////////////////////////////
3936/// To understand the code, please read this: ///
3937/// ///
3938/// http://stackoverflow.com/q/32084204/95954 ///
3939/// ///
3940/// especially Peter Cordes' answer: ///
3941/// ///
3942/// http://stackoverflow.com/a/32087095/95954 ///
3943////////////////////////////////////////////////////
3944
3945{$IFDEF WIN32}
3946asm
3947PUSH ESI
3948PUSH EDI
3949PUSH EBX
3950
3951MOV ESI,EAX // Left
3952MOV EDI,EDX // Right
3953MOV EBX,ECX // Result
3954
3955MOV ECX,RSize
3956MOV EDX,LSize
3957
3958CMP EDX,ECX
3959JAE @SkipSwap
3960XCHG ECX,EDX
3961XCHG ESI,EDI
3962
3963@SkipSwap:
3964
3965SUB EDX,ECX
3966PUSH EDX
3967XOR EDX,EDX
3968
3969XOR EAX,EAX
3970
3971MOV EDX,ECX
3972AND EDX,CUnrollMask
3973SHR ECX,CUnrollShift
3974
3975CLC
3976JE @MainTail
3977
3978@MainLoop:
3979
3980MOV EAX,[ESI]
3981ADC EAX,[EDI]
3982MOV [EBX],EAX
3983
3984MOV EAX,[ESI + CLimbSize]
3985ADC EAX,[EDI + CLimbSize]
3986MOV [EBX + CLimbSize],EAX
3987
3988MOV EAX,[ESI + 2*CLimbSize]
3989ADC EAX,[EDI + 2*CLimbSize]
3990MOV [EBX + 2*CLimbSize],EAX
3991
3992MOV EAX,[ESI + 3*CLimbSize]
3993ADC EAX,[EDI + 3*CLimbSize]
3994MOV [EBX + 3*CLimbSize],EAX
3995
3996LEA ESI,[ESI + 4*CLimbSize]
3997LEA EDI,[EDI + 4*CLimbSize]
3998LEA EBX,[EBX + 4*CLimbSize]
3999
4000DEC ECX // Does not affect carry flag, but that can cause partial flags stall.
4001JNE @MainLoop
4002
4003@MainTail:
4004
4005LEA ESI,[ESI + EDX*CLimbSize]
4006LEA EDI,[EDI + EDX*CLimbSize]
4007LEA EBX,[EBX + EDX*CLimbSize]
4008
4009LEA ECX,[@JumpsMain]
4010JMP [ECX + EDX*TYPE Pointer]
4011
4012.ALIGN 16
4013
4014@JumpsMain:
4015
4016DD @DoRestLoop
4017DD @Main1
4018DD @Main2
4019DD @Main3
4020
4021@Main3:
4022
4023MOV EAX,[ESI - 3*CLimbSize]
4024ADC EAX,[EDI - 3*CLimbSize]
4025MOV [EBX - 3*CLimbSize],EAX
4026
4027@Main2:
4028
4029MOV EAX,[ESI - 2*CLimbSize]
4030ADC EAX,[EDI - 2*CLimbSize]
4031MOV [EBX - 2*CLimbSize],EAX
4032
4033@Main1:
4034
4035MOV EAX,[ESI - CLimbSize]
4036ADC EAX,[EDI - CLimbSize]
4037MOV [EBX - CLimbSize],EAX
4038
4039@DoRestLoop:
4040
4041SETC AL // Save Carry Flag
4042
4043XOR EDI,EDI
4044
4045POP ECX
4046MOV EDX,ECX
4047AND EDX,CUnrollMask
4048SHR ECX,CUnrollShift
4049
4050ADD AL,255 // Restore Carry Flag.
4051
4052INC ECX
4053DEC ECX
4054JE @RestLast3 // JECXZ is slower than INC/DEC/JE
4055
4056@RestLoop:
4057
4058MOV EAX,[ESI]
4059ADC EAX,EDI
4060MOV [EBX],EAX
4061
4062MOV EAX,[ESI + CLimbSize]
4063ADC EAX,EDI
4064MOV [EBX + CLimbSize],EAX
4065
4066MOV EAX,[ESI + 2*CLimbSize]
4067ADC EAX,EDI
4068MOV [EBX + 2*CLimbSize],EAX
4069
4070MOV EAX,[ESI + 3*CLimbSize]
4071ADC EAX,EDI
4072MOV [EBX + 3*CLimbSize],EAX
4073
4074LEA ESI,[ESI + 4*CLimbSize]
4075LEA EBX,[EBX + 4*CLimbSize]
4076
4077DEC ECX
4078JNE @RestLoop
4079
4080@RestLast3:
4081
4082LEA ESI,[ESI + EDX*CLimbSize]
4083LEA EBX,[EBX + EDX*CLimbSize]
4084
4085LEA ECX,[@RestJumps]
4086JMP [ECX + EDX*TYPE Pointer]
4087
4088.ALIGN 16
4089
4090@RestJumps:
4091
4092DD @LastLimb
4093DD @Rest1
4094DD @Rest2
4095DD @Rest3
4096
4097@Rest3:
4098
4099MOV EAX,[ESI - 3*CLimbSize]
4100ADC EAX,EDI
4101MOV [EBX - 3*CLimbSize],EAX
4102
4103@Rest2:
4104
4105MOV EAX,[ESI - 2*CLimbSize]
4106ADC EAX,EDI
4107MOV [EBX - 2*CLimbSize],EAX
4108
4109@Rest1:
4110
4111MOV EAX,[ESI - CLimbSize]
4112ADC EAX,EDI
4113MOV [EBX - CLimbSize],EAX
4114
4115@LastLimb:
4116
4117ADC EDI,EDI
4118MOV [EBX],EDI
4119
4120@Exit:
4121
4122POP EBX
4123POP EDI
4124POP ESI
4125end;
4126{$ELSE WIN64}
4127asm
4128MOV R10,RCX
4129MOV ECX,RSize
4130
4131CMP R9D,ECX
4132JAE @SkipSwap
4133XCHG ECX,R9D
4134XCHG R10,RDX
4135
4136@SkipSwap:
4137
4138SUB R9D,ECX
4139PUSH R9
4140
4141MOV R9D,ECX
4142AND R9D,CUnrollMask
4143SHR ECX,CUnrollShift
4144
4145CLC
4146JE @MainTail
4147
4148@MainLoop:
4149
4150MOV RAX,[R10]
4151ADC RAX,[RDX]
4152MOV [R8],RAX
4153
4154MOV RAX,[R10 + DLimbSize]
4155ADC RAX,[RDX + DLimbSize]
4156MOV [R8 + DLimbSize],RAX
4157
4158LEA R10,[R10 + 2*DLimbSize]
4159LEA RDX,[RDX + 2*DLimbSize]
4160LEA R8,[R8 + 2*DLimbSize]
4161
4162DEC ECX
4163JNE @MainLoop
4164
4165@MainTail:
4166
4167LEA RCX,[@MainJumps]
4168JMP [RCX + R9*TYPE Pointer]
4169
4170.ALIGN 16
4171
4172@MainJumps:
4173
4174DQ @DoRestLoop
4175DQ @Main1
4176DQ @Main2
4177DQ @Main3
4178
4179@Main3:
4180
4181MOV RAX,[R10]
4182ADC RAX,[RDX]
4183MOV [R8],RAX
4184
4185MOV EAX,[R10 + 2*CLimbSize]
4186ADC EAX,[RDX + 2*CLimbSize]
4187MOV [R8 + 2*CLimbSize],EAX
4188
4189LEA R10,[R10 + 3*CLimbSize]
4190LEA RDX,[RDX + 3*CLimbSize]
4191LEA R8,[R8 + 3*CLimbSize]
4192
4193JMP @DoRestLoop
4194
4195@Main2:
4196
4197MOV RAX,[R10]
4198ADC RAX,[RDX]
4199MOV [R8],RAX
4200
4201LEA R10,[R10 + 2*CLimbSize]
4202LEA RDX,[RDX + 2*CLimbSize]
4203LEA R8,[R8 + 2*CLimbSize]
4204
4205JMP @DoRestLoop
4206
4207@Main1:
4208
4209MOV EAX,[R10]
4210ADC EAX,[RDX]
4211MOV [R8],EAX
4212
4213LEA R10,[R10 + CLimbSize]
4214LEA RDX,[RDX + CLimbSize]
4215LEA R8,[R8 + CLimbSize]
4216
4217@DoRestLoop:
4218
4219SETC AL // Save Carry Flag
4220
4221XOR EDX,EDX
4222
4223POP RCX
4224MOV R9D,ECX
4225AND R9D,CUnrollMask
4226SHR ECX,CUnrollShift
4227
4228ADD AL,255 // Restore Carry Flag.
4229
4230INC ECX
4231DEC ECX
4232JE @RestLast3
4233
4234@RestLoop:
4235
4236MOV RAX,[R10]
4237ADC RAX,RDX
4238MOV [R8],RAX
4239
4240MOV RAX,[R10 + DLimbSize]
4241ADC RAX,RDX
4242MOV [R8 + DLimbSize],RAX
4243
4244LEA R10,[R10 + 2*DLimbSize]
4245LEA R8,[R8 + 2*DLimbSize]
4246
4247DEC ECX
4248JNE @RestLoop
4249
4250@RestLast3:
4251
4252LEA RCX,[@RestJumps]
4253JMP [RCX + R9*TYPE Pointer]
4254
4255// If necessary, align second jump table with NOPs
4256
4257// -- Aligned.
4258
4259@RestJumps:
4260
4261DQ @LastLimb
4262DQ @Rest1
4263DQ @Rest2
4264DQ @Rest3
4265
4266@Rest3:
4267
4268MOV RAX,[R10]
4269ADC RAX,RDX
4270MOV [R8],RAX
4271
4272MOV EAX,[R10 + DLimbSize]
4273ADC EAX,EDX
4274MOV [R8 + DLimbSize],EAX
4275
4276LEA R8,[R8 + 3*CLimbSize]
4277
4278JMP @LastLimb
4279
4280@Rest2:
4281
4282MOV RAX,[R10]
4283ADC RAX,RDX
4284MOV [R8],RAX
4285
4286LEA R8,[R8 + DLimbSize]
4287
4288JMP @LastLimb
4289
4290@Rest1:
4291
4292MOV EAX,[R10]
4293ADC EAX,EDX
4294MOV [R8],EAX
4295
4296LEA R8,[R8 + CLimbSize]
4297
4298@LastLimb:
4299
4300ADC EDX,EDX
4301MOV [R8],EDX
4302
4303@Exit:
4304
4305end;
4306{$ENDIF !WIN32}
4307{$ENDIF !PUREPASCAL}
4308
4309{$IFDEF PUREPASCAL}
4310class procedure BigInteger.InternalAddPurePascal(Left, Right, Result: PLimb; LSize, RSize: Integer);
4311var
4312LCount, LTail: Integer;
4313Sum: NativeUInt;
4314I: Integer;
4315L: PLimb;
4316begin
4317if LSize < RSize then
4318begin
4319I := LSize;
4320LSize := RSize;
4321RSize := I;
4322L := Left;
4323Left := Right;
4324Right := L;
4325end;
4326
4327Sum := 0;
4328
4329Dec(LSize, RSize); // LSize is length of non-overlapping part.
4330
4331LTail := RSize and CUnrollMask;
4332LCount := RSize shr CUnrollShift;
4333
4334while LCount > 0 do
4335begin
4336{$IFDEF CPU64BITS}
4337Sum := UInt64(Left[0]) + Right[0] + (Sum shr 32);
4338Result[0] := TLimb(Sum);
4339
4340Sum := UInt64(Left[1]) + Right[1] + (Sum shr 32);
4341Result[1] := TLimb(Sum);
4342
4343Sum := UInt64(Left[2]) + Right[2] + (Sum shr 32);
4344Result[2] := TLimb(Sum);
4345
4346Sum := UInt64(Left[3]) + Right[3] + (Sum shr 32);
4347Result[3] := TLimb(Sum);
4348{$ELSE}
4349Sum := UInt32(PUInt16(Left)[0]) + PUInt16(Right)[0] + (Sum shr 16);
4350PUInt16(Result)[0] := UInt16(Sum);
4351
4352Sum := UInt32(PUInt16(Left)[1]) + PUInt16(Right)[1] + (Sum shr 16);
4353PUInt16(Result)[1] := UInt16(Sum);
4354
4355Sum := UInt32(PUInt16(Left)[2]) + PUInt16(Right)[2] + (Sum shr 16);
4356PUInt16(Result)[2] := UInt16(Sum);
4357
4358Sum := UInt32(PUInt16(Left)[3]) + PUInt16(Right)[3] + (Sum shr 16);
4359PUInt16(Result)[3] := UInt16(Sum);
4360
4361Sum := UInt32(PUInt16(Left)[4]) + PUInt16(Right)[4] + (Sum shr 16);
4362PUInt16(Result)[4] := UInt16(Sum);
4363
4364Sum := UInt32(PUInt16(Left)[5]) + PUInt16(Right)[5] + (Sum shr 16);
4365PUInt16(Result)[5] := UInt16(Sum);
4366
4367Sum := UInt32(PUInt16(Left)[6]) + PUInt16(Right)[6] + (Sum shr 16);
4368PUInt16(Result)[6] := UInt16(Sum);
4369
4370Sum := UInt32(PUInt16(Left)[7]) + PUInt16(Right)[7] + (Sum shr 16);
4371PUInt16(Result)[7] := UInt16(Sum);
4372{$ENDIF}
4373
4374Inc(Left, CUnrollIncrement);
4375Inc(Right, CUnrollIncrement);
4376Inc(Result, CUnrollIncrement);
4377Dec(LCount);
4378end;
4379while LTail > 0 do
4380begin
4381{$IFDEF CPU64BITS}
4382Sum := UInt64(Left[0]) + Right[0] + (Sum shr 32);
4383Result[0] := TLimb(Sum);
4384{$ELSE}
4385Sum := UInt32(PUInt16(Left)[0]) + PUInt16(Right)[0] + (Sum shr 16);
4386PUInt16(Result)[0] := UInt16(Sum);
4387
4388Sum := UInt32(PUInt16(Left)[1]) + PUInt16(Right)[1] + (Sum shr 16);
4389PUInt16(Result)[1] := UInt16(Sum);
4390{$ENDIF}
4391
4392Inc(Left);
4393Inc(Right);
4394Inc(Result);
4395Dec(LTail);
4396end;
4397
4398LTail := LSize and CUnrollMask;
4399LCount := LSize shr CUnrollShift;
4400
4401while LCount > 0 do
4402begin
4403{$IFDEF CPU64BITS}
4404Sum := UInt64(Left[0]) + (Sum shr 32);
4405Result[0] := TLimb(Sum);
4406
4407Sum := UInt64(Left[1]) + (Sum shr 32);
4408Result[1] := TLimb(Sum);
4409
4410Sum := UInt64(Left[2]) + (Sum shr 32);
4411Result[2] := TLimb(Sum);
4412
4413Sum := UInt64(Left[3]) + (Sum shr 32);
4414Result[3] := TLimb(Sum);
4415{$ELSE}
4416Sum := UInt32(PUInt16(Left)[0]) + (Sum shr 16);
4417PUInt16(Result)[0] := UInt16(Sum);
4418
4419Sum := UInt32(PUInt16(Left)[1]) + (Sum shr 16);
4420PUInt16(Result)[1] := UInt16(Sum);
4421
4422Sum := UInt32(PUInt16(Left)[2]) + (Sum shr 16);
4423PUInt16(Result)[2] := UInt16(Sum);
4424
4425Sum := UInt32(PUInt16(Left)[3]) + (Sum shr 16);
4426PUInt16(Result)[3] := UInt16(Sum);
4427
4428Sum := UInt32(PUInt16(Left)[4]) + (Sum shr 16);
4429PUInt16(Result)[4] := UInt16(Sum);
4430
4431Sum := UInt32(PUInt16(Left)[5]) + (Sum shr 16);
4432PUInt16(Result)[5] := UInt16(Sum);
4433
4434Sum := UInt32(PUInt16(Left)[6]) + (Sum shr 16);
4435PUInt16(Result)[6] := UInt16(Sum);
4436
4437Sum := UInt32(PUInt16(Left)[7]) + (Sum shr 16);
4438PUInt16(Result)[7] := UInt16(Sum);
4439{$ENDIF}
4440
4441Inc(Left, CUnrollIncrement);
4442Inc(Result, CUnrollIncrement);
4443Dec(LCount);
4444end;
4445
4446while LTail > 0 do
4447begin
4448{$IFDEF CPU64BITS}
4449Sum := UInt64(Left[0]) + (Sum shr 32);
4450Result[0] := TLimb(Sum);
4451{$ELSE}
4452Sum := UInt32(PUInt16(Left)[0]) + (Sum shr 16);
4453PUInt16(Result)[0] := UInt16(Sum);
4454
4455Sum := UInt32(PUInt16(Left)[1]) + (Sum shr 16);
4456PUInt16(Result)[1] := UInt16(Sum);
4457{$ENDIF}
4458
4459Inc(Left);
4460Inc(Result);
4461Dec(LTail);
4462end;
4463
4464{$IFDEF CPU64BITS}
4465Result[0] := Sum shr 32;
4466{$ELSE}
4467Result[0] := Sum shr 16;
4468{$ENDIF};
4469
4470end;
4471{$ENDIF}
4472
4473class procedure BigInteger.InternalMultiply(Left, Right, Result: PLimb; LSize, RSize: Integer);
4474{$IFDEF PUREPASCAL}
4475
4476//////////////////////////////////////////////////////////////////////////////
4477// Tests with using a UInt32 Product and emulating 16 bit limbs produced //
4478// 50% slower code. //
4479// 64 bit multiplication and addition doesn't generate optimal code, but //
4480// it is apparently still faster than faking 16 bit limbs. //
4481//////////////////////////////////////////////////////////////////////////////
4482// What is really needed is a function that multiplies two UInt32 and //
4483// produces an UInt64 directly, i.e. without conversion of the UInt32s into //
4484// UInt64 first. This is easy in assembler, but not in PUREPASCAL. //
4485//////////////////////////////////////////////////////////////////////////////
4486
4487type
4488TUInt64 = packed record
4489Lo, Hi: TLimb;
4490end;
4491var
4492Product: UInt64;
4493LTail, LCount: Integer;
4494CurrentRightLimb: TLimb;
4495PLeft, PDest, PRight, PDestRowStart: PLimb;
4496LCarry: TLimb;
4497begin
4498// Ensure that Left is the longer of both magnitudes.
4499if RSize > LSize then
4500begin
4501SwapPLimbs(Left, Right);
4502SwapIntegers(LSize, RSize);
4503end;
4504
4505// Each new row is one limb further to the left.
4506PRight := Right;
4507PDestRowStart := Result;
4508
4509PLeft := Left;
4510PDest := PDestRowStart;
4511Inc(PDestRowStart);
4512CurrentRightLimb := PRight^;
4513Inc(PRight);
4514TUInt64(Product).Hi := 0;
4515Dec(RSize);
4516LCount := LSize;
4517LCarry := 0;
4518
4519// First row. No previous result, so no need to add it in.
4520while LCount > 0 do
4521begin
4522Product := UInt64(PLeft^) * CurrentRightLimb;
4523Inc(Product, LCarry);
4524PDest^ := TUInt64(Product).Lo;
4525LCarry := TUInt64(Product).Hi;
4526Inc(PLeft);
4527Inc(PDest);
4528Dec(LCount);
4529end;
4530PDest^ := TUInt64(Product).Hi;
4531
4532LTail := LSize and CUnrollMask; // Low 2 bits: 0..3.
4533LSize := LSize shr CUnrollShift; // Divide by 4.
4534while RSize > 0 do
4535begin
4536PLeft := Left;
4537PDest := PDestRowStart;
4538Inc(PDestRowStart);
4539CurrentRightLimb := PRight^;
4540Inc(PRight);
4541
4542if CurrentRightLimb <> 0 then
4543begin
4544LCarry := 0;
4545LCount := LSize;
4546
4547// Inner loop, unrolled.
4548while LCount > 0 do
4549begin
4550
4551// Note: The following will not produce an overflow.
4552// Proof: say B = High(TLimb) + 1 = $100000000
4553// Assume PLeft[0], CurrentRightLimb, PRight[0] and Product.Hi are all
4554// the maximum value (B - 1) (i.e. $FFFFFFFF).
4555// Then Product = (B - 1)^2 + (B - 1) + (B - 1)
4556// = B^2 - 2*B + 1 + 2*B - 2
4557// = B^2 - 1 = $FFFFFFFFFFFFFFFF = High(UInt64)
4558// so no overflow possible!
4559
4560// Note2: The previous code was
4561//
4562// Product := UInt64(PLeft[0]) * CurrentRightLimb + PDest[0] + TUInt64(Product).Hi;
4563// etc...
4564//
4565// The following source produces shorter generated code, but is only slightly faster
4566// than the above (3% speed increase).
4567
4568Product := UInt64(PLeft[0]) * CurrentRightLimb;
4569Inc(Product, PDest[0]);
4570Inc(Product, LCarry);
4571PDest[0] := TLimb(Product);
4572LCarry := TUInt64(Product).Hi;
4573
4574Product := UInt64(PLeft[1]) * CurrentRightLimb;
4575Inc(Product, PDest[1]);
4576Inc(Product, LCarry);
4577PDest[1] := TLimb(Product);
4578LCarry := TUInt64(Product).Hi;
4579
4580Product := UInt64(PLeft[2]) * CurrentRightLimb;
4581Inc(Product, PDest[2]);
4582Inc(Product, LCarry);
4583PDest[2] := TLimb(Product);
4584LCarry := TUInt64(Product).Hi;
4585
4586Product := UInt64(PLeft[3]) * CurrentRightLimb;
4587Inc(Product, PDest[3]);
4588Inc(Product, LCarry);
4589PDest[3] := TLimb(Product);
4590LCarry := TUInt64(Product).Hi;
4591
4592Inc(PLeft, CUnrollIncrement);
4593Inc(PDest, CunrollIncrement);
4594Dec(LCount);
4595end;
4596
4597// Rest loop.
4598LCount := LTail;
4599while LCount > 0 do
4600begin
4601Product := UInt64(PLeft^) * CurrentRightLimb;
4602Inc(Product, PDest^);
4603Inc(Product, LCarry);
4604LCarry := TUInt64(Product).Hi;
4605PDest^ := TUInt64(Product).Lo;
4606
4607Inc(PLeft);
4608Inc(PDest);
4609Dec(LCount);
4610end;
4611
4612// Last (top) limb of this row.
4613PDest^ := TUInt64(Product).Hi;
4614end;
4615Dec(RSize);
4616end;
4617end;
4618{$ELSE !PUREPASCAL}
4619{$IFDEF WIN32)}
4620var
4621SaveResult: PLimb;
4622LRest, LCount: Integer;
4623PRight, PDestRowStart: PLimb;
4624LLeft, LRight: PLimb;
4625asm
4626PUSH ESI
4627PUSH EDI
4628PUSH EBX
4629
4630MOV SaveResult,ECX
4631
4632MOV ESI,LSize
4633MOV EDI,RSize
4634CMP ESI,EDI
4635JA @SkipSwap
4636
4637XCHG EAX,EDX
4638XCHG ESI,EDI
4639MOV LSize,ESI
4640MOV RSize,EDI
4641
4642// The longest loop should ideally be unrolled. After this, Left should be longer or same length.
4643
4644@SkipSwap:
4645
4646MOV LLeft,EAX
4647MOV LRight,EDX
4648
4649// First loop, setting up first row:
4650
4651MOV PRight,EDX
4652MOV EDI,SaveResult
4653MOV PDestRowStart,EDI // EDI = PDest
4654
4655MOV ESI,LLeft // ESI = PLeft
4656
4657// If CurrentLimbRight = 0, we can skip a lot, and simply do a FillChar
4658
4659MOV ECX,[EDX] // CurrentRightLimb
4660XOR EBX,EBX // PreviousProductHi
4661ADD PDestRowStart,CLimbSize
4662ADD PRight,CLimbSize
4663MOV EAX,LSize
4664MOV LCount,EAX
4665
4666// The setup loop fills the row without an attempt to add to the data already in the result.
4667
4668@SetupLoop:
4669
4670MOV EAX,[ESI]
4671MUL EAX,ECX // Uses MUL EAX,ECX syntax because of bug in XE2 assembler.
4672ADD EAX,EBX
4673ADC EDX,KZero
4674MOV [EDI],EAX
4675MOV EBX,EDX
4676LEA ESI,[ESI + CLimbSize]
4677LEA EDI,[EDI + CLimbSize]
4678DEC LCount
4679JNE @SetupLoop
4680MOV [EDI],EDX
4681
4682MOV EAX,LSize
4683MOV EDX,EAX
4684SHR EAX,CUnrollShift
4685MOV LSize,EAX
4686AND EDX,CUnrollMask
4687MOV LRest,EDX
4688
4689DEC RSize
4690JE @Exit
4691
4692// The outer loop iterates over the limbs of the shorter operand. After the setup loop, the lowest limb
4693// has already been taken care of.
4694
4695@OuterLoop:
4696
4697MOV ESI,LLeft
4698MOV EDI,PDestRowStart
4699ADD PDestRowStart,CLimbSize
4700MOV EAX,PRight
4701ADD PRight,CLimbSize
4702
4703// If PRight^ is 0, then we can skip multiplication for the entire row.
4704
4705MOV ECX,[EAX]
4706TEST ECX,ECX
4707JE @NextOuterLoop
4708
4709XOR EBX,EBX
4710MOV EAX,LSize
4711MOV LCount,EAX
4712
4713
4714CMP EAX,KZero
4715JE @EndInnerLoop
4716
4717.ALIGN 16
4718
4719@InnerLoop:
4720
4721// Loop unrolled. Approx. 70% faster than simple loop.
4722
4723// TODO: Use MMX registers for multiplication and addition.
4724// E.g.
4725// MOV MM7,ECX
4726// PXOR MM6,MM6
4727// @Innerloop0:
4728// MOV MM0,[ESI]
4729// MOV MM1,[ESI + CLimbSize]
4730// MOV MM2,[ESI + 2*CLimbSize]
4731// MOV MM3,[ESI + 3*CLimbSize]
4732// PMULUDQ MM0,MM7
4733// PADDQ MM6,MM0
4734// MOV [ESI],MM6
4735// PSHRQ MM6,32
4736// PMULUDQ MM1,MM7
4737// PADDQ MM6,MM1
4738// MOV [ESI+CLimbSize],MM6
4739// PSHRQ MM6,32
4740// etc...
4741// @InnerLoopRest:
4742// Do the same as above, but add to existing content in [ESI+...]
4743
4744MOV EAX,[ESI] // The following pattern is not faster:
4745MUL ECX // MOV EAX,[ESI]
4746ADD EAX,[EDI] // MUL ECX
4747ADC EDX,0 // ADD EAX,EBX
4748ADD EAX,EBX // ADC EDX,0
4749ADC EDX,0 // ADD [EDI],EAX
4750MOV [EDI],EAX // ADC EDX,0
4751MOV EBX,EDX // MOV EBX,EDX
4752
4753MOV EAX,[ESI + CLimbSize]
4754MUL ECX
4755ADD EAX,[EDI + CLimbSize]
4756ADC EDX,0
4757ADD EAX,EBX
4758ADC EDX,0
4759MOV [EDI + CLimbSize],EAX
4760MOV EBX,EDX
4761
4762MOV EAX,[ESI + 2*CLimbSize]
4763MUL ECX
4764ADD EAX,[EDI + 2*CLimbSize]
4765ADC EDX,0
4766ADD EAX,EBX
4767ADC EDX,0
4768MOV [EDI + 2*CLimbSize],EAX
4769MOV EBX,EDX
4770
4771MOV EAX,[ESI + 3*CLimbSize]
4772MUL ECX
4773ADD EAX,[EDI + 3*CLimbSize]
4774ADC EDX,0
4775ADD EAX,EBX
4776ADC EDX,0
4777MOV [EDI + 3*CLimbSize],EAX
4778MOV EBX,EDX
4779
4780LEA ESI,[ESI + 4*CLimbSize]
4781LEA EDI,[EDI + 4*CLimbSize]
4782
4783DEC LCount
4784JNE @InnerLoop
4785
4786@EndInnerLoop:
4787
4788// The remaining limbs to be handled.
4789
4790MOV EAX,LRest
4791MOV LCount,EAX
4792CMP EAX,0
4793JE @EndInnerRestLoop
4794
4795@InnerRestLoop:
4796
4797MOV EAX,[ESI]
4798MUL EAX,ECX
4799ADD EAX,EBX
4800ADC EDX,0
4801ADD EAX,[EDI]
4802ADC EDX,0
4803MOV [EDI],EAX
4804MOV EBX,EDX
4805LEA ESI,[ESI + CLimbSize]
4806LEA EDI,[EDI + CLimbSize]
4807DEC LCount
4808JNE @InnerRestLoop
4809
4810@EndInnerRestLoop:
4811
4812// The last (left) limb gets the top of the 64 bit product.
4813
4814MOV [EDI],EBX
4815
4816@NextOuterLoop:
4817
4818DEC RSize
4819JNE @OuterLoop
4820
4821@Exit:
4822POP EBX
4823POP EDI
4824POP ESI
4825end;
4826{$ELSE WIN64}
4827
4828// This uses 64 bit multiplication as much as possible. The logic handles any odd (top) limbs especially.
4829
4830var
4831LeftOdd, RightOdd: Boolean; // Left, Right (resp.): odd number of limbs?
4832SaveLeft: PLimb;
4833LeftSize, RightSize: Integer;
4834asm
4835.PUSHNV RSI
4836.PUSHNV RDI
4837.PUSHNV RBX
4838.PUSHNV R12
4839
4840MOV EDI,RSize
4841CMP R9D,EDI
4842JAE @SwapEnd
4843
4844XCHG RCX,RDX
4845XCHG R9D,EDI
4846
4847@SwapEnd:
4848
4849MOV SaveLeft,RCX
4850XOR R12,R12
4851
4852MOV EAX,R9D
4853SHR R9D,1
4854MOV LeftSize,R9D // Number of double limbs of Left
4855AND AL,1
4856MOV LeftOdd,AL // Does Left have an odd number of limbs?
4857
4858MOV EAX,EDI
4859SHR EDI,1
4860MOV RightSize,EDI // Number of double limbs of Right
4861AND AL,1
4862MOV RightOdd,AL // Does Right have an odd number of limbs?
4863
4864MOV R10,RDX // Current limb to be multiplied
4865XOR RBX,RBX // Top DWORD (EDX) of previous multiplication
4866
4867// If no more 64 bit limbs in Right, we must skip to final odd limb.
4868
4869CMP RightSize,0
4870JE @FinalOddPart
4871
4872MOV RCX,[R10] // Current Right limb's value
4873MOV RDI,R8 // Result limb pointer
4874MOV RSI,SaveLeft // Left limb pointer
4875ADD R8,DLimbSize // Result's pointer to start of current row
4876ADD R10,DLimbSize // Current Right limb pointer
4877
4878MOV R11D,LeftSize // Loop counter
4879CMP R11D,0
4880JE @SetupOddPart
4881
4882// Setup loop (64 bit part)
4883
4884@SetupLoop64:
4885
4886MOV RAX,[RSI]
4887MUL RCX
4888ADD RAX,RBX
4889ADC RDX,R12
4890MOV [RDI],RAX
4891MOV RBX,RDX
4892LEA RSI,[RSI + DLimbSize]
4893LEA RDI,[RDI + DLimbSize]
4894DEC R11D
4895JNE @SetupLoop64
4896
4897// Setup loop, last limb ("odd" part).
4898
4899@SetupOddPart:
4900
4901CMP LeftOdd,0
4902JE @SkipSetupOddPart
4903
4904MOV EAX,[RSI] // 32 bit register to read odd limb of this loop
4905MUL RCX
4906ADD RAX,RBX
4907ADC RDX,R12
4908MOV [RDI],RAX
4909MOV [RDI + DLimbSize],RDX
4910JMP @SkipSkipSetupOddPart
4911
4912@SkipSetupOddPart:
4913
4914MOV [RDI],RDX
4915
4916@SkipSkipSetupOddPart:
4917
4918DEC RightSize
4919JE @FinalOddPart
4920
4921@OuterLoop:
4922
4923MOV RDI,R8
4924ADD R8,DLimbSize
4925MOV RCX,[R10]
4926ADD R10,DLimbSize
4927
4928TEST RCX,RCX
4929JE @NextOuterLoop
4930
4931MOV RSI,SaveLeft
4932XOR RBX,RBX
4933MOV R11D,LeftSize
4934CMP R11D,0
4935JE @InnerLoopOddPart
4936
4937SHR R11D,CUnrollShift
4938JE @InnerTail64
4939
4940@InnerLoop64:
4941
4942MOV RAX,[RSI] // Get double limb from Left data
4943MUL RCX // multiply it with current Right double limb's value --> RDX:RAX
4944ADD RAX,RBX // Add top limb from previous multiplication to RDX:RAX
4945ADC RDX,R12
4946ADD [RDI],RAX // Add RAX to result array
4947ADC RDX,R12 // And adjust top limb again
4948MOV RBX,RDX // And save top limb as "carry".
4949
4950MOV RAX,[RSI + DLimbSize]
4951MUL RCX
4952ADD RAX,RBX
4953ADC RDX,R12
4954ADD [RDI + DLimbSize],RAX
4955ADC RDX,R12
4956MOV RBX,RDX
4957
4958MOV RAX,[RSI + 2*DLimbSize]
4959MUL RCX
4960ADD RAX,RBX
4961ADC RDX,R12
4962ADD [RDI + 2*DLimbSize],RAX
4963ADC RDX,R12
4964MOV RBX,RDX
4965
4966MOV RAX,[RSI + 3*DLimbSize]
4967MUL RCX
4968ADD RAX,RBX
4969ADC RDX,R12
4970ADD [RDI + 3*DLimbSize],RAX
4971ADC RDX,R12
4972MOV RBX,RDX
4973
4974LEA RSI,[RSI + 4*DLimbSize]
4975LEA RDI,[RDI + 4*DLimbSize]
4976DEC R11D
4977JNE @InnerLoop64
4978
4979@InnerTail64:
4980
4981MOV R11D,LeftSize
4982AND R11D,CUnrollMask
4983JE @InnerLoopOddPart
4984
4985@InnerTailLoop64:
4986
4987MOV RAX,[RSI]
4988MUL RCX
4989ADD RAX,[RDI]
4990ADC RDX,R12
4991ADD RAX,RBX
4992ADC RDX,R12
4993MOV [RDI],RAX
4994MOV RBX,RDX
4995LEA RSI,[RSI + DLimbSize]
4996LEA RDI,[RDI + DLimbSize]
4997DEC R11D
4998JNE @InnerTailLoop64
4999
5000@InnerLoopOddPart:
5001
5002CMP LeftOdd,0 // If Left's size is odd, handle last limb.
5003JE @InnerLoopLastLimb
5004
5005MOV RAX,[RSI]
5006MUL RCX
5007ADD RAX,[RDI]
5008ADC RDX,R12
5009ADD RAX,RBX
5010ADC RDX,R12
5011MOV [RDI],RAX
5012MOV [RDI + DLimbSize],RDX
5013JMP @NextOuterLoop
5014
5015@InnerLoopLastLimb:
5016
5017MOV [RDI],RDX
5018
5019@NextOuterLoop:
5020
5021DEC RightSize
5022JNE @OuterLoop
5023
5024@FinalOddPart:
5025
5026CMP RightOdd,0
5027JE @Exit
5028
5029MOV RDI,R8
5030MOV RSI,SaveLeft
5031MOV RAX,R10
5032MOV ECX,[RAX] // Right is odd, so read single TLimb
5033XOR RBX,RBX
5034MOV R11D,LeftSize
5035CMP R11D,0
5036JE @SkipFinalLoop
5037
5038.ALIGN 16
5039
5040@FinalLoop:
5041
5042MOV RAX,[RSI]
5043MUL RCX
5044ADD RAX,[RDI]
5045ADC RDX,0
5046ADD RAX,RBX
5047ADC RDX,0
5048MOV [RDI],RAX
5049MOV RBX,RDX
5050LEA RSI,[RSI + DLimbSize]
5051LEA RDI,[RDI + DLimbSize]
5052DEC R11D
5053JNE @FinalLoop
5054
5055@SkipFinalLoop:
5056
5057CMP LeftOdd,0
5058JE @LastLimb
5059
5060MOV EAX,[RSI]
5061MUL RCX
5062ADD RAX,[RDI]
5063ADC RDX,0
5064ADD RAX,RBX
5065ADC RDX,0
5066MOV [RDI],RAX
5067MOV [RDI + DLimbSize],RDX
5068JMP @Exit
5069
5070@LastLimb:
5071
5072MOV [RDI],RDX
5073
5074@Exit:
5075
5076end;
5077{$ENDIF !WIN32}
5078{$ENDIF !PUREPASCAL}
5079
5080function BigInteger.ToBinaryString: string;
5081begin
5082Result := ToString(2);
5083end;
5084
5085function BigInteger.ToByteArray: TArray<Byte>;
5086var
5087Mag: TMagnitude;
5088Bytes, Bits: Integer;
5089ExtraByte: Byte;
5090begin
5091if IsZero then
5092begin
5093SetLength(Result, 1);
5094Result[0] := 0;
5095Exit;
5096end;
5097
5098Bytes := BitLength;
5099Bits := Bytes and $07;
5100Bytes := (Bytes + 7) shr 3;
5101if FSize > 0 then
5102begin
5103Mag := FData;
5104ExtraByte := $00;
5105end
5106else
5107begin
5108SetLength(Mag, Size);
5109InternalNegate(PLimb(FData), PLimb(Mag), Size);
5110ExtraByte := $FF;
5111end;
5112SetLength(Result, Bytes + Byte(Bits = 0));
5113Move(Mag[0], Result[0], Bytes);
5114if Bits = 0 then
5115Result[Bytes] := ExtraByte;
5116end;
5117
5118function BigInteger.ToDecimalString: string;
5119begin
5120Result := ToString(10);
5121end;
5122
5123function BigInteger.ToHexString: string;
5124begin
5125Result := ToString(16);
5126end;
5127
5128function BigInteger.ToOctalString: string;
5129begin
5130Result := ToString(8);
5131end;
5132
5133{$IFNDEF PUREPASCAL}
5134procedure DivModNativeUInts(Dividend, Divisor: NativeUInt; var Quotient, Remainder: NativeUint);
5135{$IFDEF WIN32}
5136asm
5137PUSH EBX
5138MOV EBX,EDX
5139XOR EDX,EDX
5140DIV EAX,EBX
5141MOV [ECX],EAX
5142MOV EBX,Remainder
5143MOV [EBX],EDX
5144POP EBX
5145end;
5146{$ELSE WIN64}
5147asm
5148.NOFRAME
5149
5150MOV RAX,RCX
5151MOV RCX,RDX
5152XOR EDX,EDX
5153DIV RAX,RCX
5154MOV [R8],RAX
5155MOV [R9],RDX
5156end;
5157{$ENDIF WIN64}
5158{$ENDIF !PUREPASCAL}
5159
5160const
5161TwoDigitTable: array[0..99, 0..1] of Char =
5162(
5163'00', '01', '02', '03', '04', '05', '06', '07', '08', '09',
5164'10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
5165'20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
5166'30', '31', '32', '33', '34', '35', '36', '37', '38', '39',
5167'40', '41', '42', '43', '44', '45', '46', '47', '48', '49',
5168'50', '51', '52', '53', '54', '55', '56', '57', '58', '59',
5169'60', '61', '62', '63', '64', '65', '66', '67', '68', '69',
5170'70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
5171'80', '81', '82', '83', '84', '85', '86', '87', '88', '89',
5172'90', '91', '92', '93', '94', '95', '96', '97', '98', '99'
5173);
5174
5175{$IF DEFINED(WIN32)}
5176// Checked
5177Div100Const = UInt32(UInt64($1FFFFFFFFF) div 100 + 1);
5178Div100PostShift = 5;
5179{$ELSEIF DEFINED(WIN64)}
5180{$IFDEF LIBDIVIDE}
5181// Parameters calculated using
5182// https://github.com/ridiculousfish/libdivide/blob/master/divide_by_constants_codegen_reference.c
5183Div100Const = $47AE147AE147AE15;
5184Div100PostShift = 6;
5185{$ELSE}
5186Div100Const = $A3D70A3D70A3D70B; // UInt64(UInt128($3F FFFF FFFF FFFF FFFF) div 100 + 1)
5187Div100PostShift = 6;
5188{$ENDIF}
5189{$IFEND}
5190
5191{$IFNDEF PUREPASCAL}
5192
5193///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
5194/// The following calculates X div 100 using multiplication by a constant, taking the high part of the 64 bit ///
5195/// result and shifting right. The return value is the remainder, calculated as X - quotient * 100. ///
5196/// ///
5197/// This was tested to work safely and quickly for all values of UInt32. ///
5198/// ///
5199/// The 64 bit part is taken from: https://raw.github.com/ridiculousfish/libdivide/master/libdivide.h ///
5200///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
5201///
5202class function BigInteger.InternalDivMod100(var X: NativeUInt): NativeUInt;
5203{$IFDEF WIN32}
5204asm
5205PUSH EBX
5206MOV EDX,Div100Const
5207MOV ECX,EAX
5208MOV EAX,[ECX]
5209MOV EBX,EAX
5210MUL EDX
5211SHR EDX,Div100PostShift
5212MOV [ECX],EDX // Quotient
5213
5214// Slightly faster than MUL
5215
5216LEA EDX,[EDX + 4*EDX] // EDX := EDX * 5;
5217LEA EDX,[EDX + 4*EDX] // EDX := EDX * 5;
5218SHL EDX,2 // EDX := EDX * 4; 5*5*4 = 100.
5219
5220MOV EAX,EBX
5221SUB EAX,EDX // Remainder
5222POP EBX
5223end;
5224{$ELSE WIN64}
5225asm
5226.NOFRAME
5227
5228// See libdivide.h: libdivide_u64_do() after a call to libdivide_u64_gen(100)
5229
5230MOV RAX,[RCX]
5231MOV R8,RAX
5232XOR RDX,RDX
5233MOV R9,Div100Const
5234MUL R9 // RDX = Q
5235
5236{$IFDEF LIBDIVIDE}
5237MOV R9,R8 // Q := Q + (X - Q) shr 1;
5238SUB R9,RDX
5239SHR R9,1
5240ADD RDX,R9
5241{$ENDIF}
5242
5243SHR RDX,Div100PostShift // Q := Q shr 6;
5244MOV [RCX],RDX // X := Q;
5245
5246// Faster than LEA and SHL
5247
5248MOV RAX,RDX
5249MOV R9D,100
5250MUL R9
5251SUB R8,RAX
5252MOV RAX,R8 // Remainder
5253end;
5254{$ENDIF WIN32}
5255{$ENDIF !PUREPASCAL}
5256
5257{$IFNDEF PUREPASCAL}
5258class procedure BigInteger.InternalIntToStrDecimal(const Value: NativeUInt; var WritePtr: PChar; MaxDigits: Integer);
5259var
5260LRemainder, LDividend: NativeUInt;
5261LSectionStart: PChar;
5262begin
5263LSectionStart := WritePtr - MaxDigits;
5264LDividend := Value;
5265if Odd(MaxDigits) and (LDividend <> 0) then
5266begin
5267DivModNativeUInts(LDividend, 10, LDividend, LRemainder);
5268Dec(WritePtr);
5269WritePtr^ := Char(LRemainder + Ord('0'));
5270end;
5271while LDividend > 0 do
5272begin
5273LRemainder := InternalDivMod100(LDividend);
5274Dec(WritePtr, 2);
5275WritePtr[0] := TwoDigitTable[LRemainder, 0];
5276WritePtr[1] := TwoDigitTable[LRemainder, 1];
5277end;
5278
5279while WritePtr > LSectionStart do
5280begin
5281Dec(WritePtr);
5282WritePtr^ := '0';
5283end;
5284end;
5285{$ENDIF}
5286
5287// Simple version of IntToStr for any given base, for unsigned integers only.
5288class procedure BigInteger.InternalIntToStrBase(const Value: NativeUInt; Base: Cardinal; var WritePtr: PChar;
5289MaxDigits: Integer);
5290var
5291{$IFDEF PUREPASCAL}
5292LRemainder: UInt64;
5293LDividend: UInt64;
5294{$ELSE}
5295LRemainder: NativeUInt;
5296LDividend: NativeUInt;
5297{$ENDIF PUREPASCAL}
5298LSectionStart: PChar;
5299begin
5300{$IFNDEF PUREPASCAL}
5301if Base = 10 then
5302begin
5303InternalIntToStrDecimal(Value, WritePtr, MaxDigits);
5304Exit;
5305end;
5306{$ENDIF}
5307LSectionStart := WritePtr - MaxDigits;
5308LDividend := Value;
5309while LDividend > 0 do
5310begin
5311{$IFDEF PUREPASCAL}
5312System.Math.DivMod(LDividend, Base, LDividend, LRemainder);
5313{$ELSE}
5314DivModNativeUInts(LDividend, Base, LDividend, LRemainder);
5315{$ENDIF PUREPASCAL}
5316Dec(WritePtr);
5317WritePtr^ := CBaseChars[LRemainder];
5318end;
5319
5320while WritePtr > LSectionStart do
5321begin
5322Dec(WritePtr);
5323WritePtr^ := '0';
5324end;
5325end;
5326
5327// This should be easy. Simply shift (beginning from the back) and output. This can be done limb-wise.
5328class procedure BigInteger.InternalShiftedToString(const Value: BigInteger; Base: Integer; var WritePtr: PChar);
5329var
5330LMaxDigits: Integer;
5331LShift: Integer;
5332LMask, LLImb: TLimb;
5333LSectionStart: PChar;
5334I: Integer;
5335begin
5336Assert(Base in [2, 4, 16]);
5337case Base of
53382:
5339begin
5340LMaxDigits := 32;
5341LShift := 1;
5342LMask := $00000001;
5343end;
53444:
5345begin
5346LMaxDigits := 16;
5347LShift := 2;
5348LMask := $00000003;
5349end;
5350else
5351begin
5352LMaxDigits := 8;
5353LShift := 4;
5354LMask := $0000000F;
5355end;
5356end;
5357Assert(Value.FSize >= 0);
5358for I := 0 to Value.FSize - 1 do
5359begin
5360LLimb := Value.FData[I];
5361LSectionStart := WritePtr - LMaxDigits;
5362while LLimb <> 0 do
5363begin
5364Dec(WritePtr);
5365WritePtr^ := CBaseChars[LLimb and LMask];
5366LLimb := LLimb shr LShift;
5367end;
5368while WritePtr > LSectionStart do
5369begin
5370Dec(WritePtr);
5371WritePtr^ := '0';
5372end;
5373end;
5374end;
5375
5376// This is pretty self-documenting, but also cf. Brent, Zimmermann [3], "Modern Computer Arithmetic", algorithm 1.24
5377class procedure BigInteger.InternalPlainToString(const Value: BigInteger; Base: Integer;
5378const BaseInfo: TNumberBaseInfo; var WritePtr: PChar; SectionCount: Integer);
5379var
5380LQuotient, LRemainder: BigInteger;
5381LSectionStart: PChar;
5382begin
5383LQuotient := Value;
5384LSectionStart := WritePtr - SectionCount * BaseInfo.MaxDigits;
5385
5386while Assigned(LQuotient.FData) do
5387begin
5388BigInteger.DivMod(LQuotient, BaseInfo.MaxPower, LQuotient, LRemainder);
5389{$IFDEF CPU32BITS}
5390if Assigned(LRemainder.FData) then
5391InternalIntToStrBase(LRemainder.FData[0], Base, WritePtr, BaseInfo.MaxDigits)
5392else
5393InternalIntToStrBase(0, Base, WritePtr, BaseInfo.MaxDigits);
5394{$ELSE}
5395InternalIntToStrBase(UInt64(LRemainder), Base, WritePtr, BaseInfo.MaxDigits);
5396{$ENDIF}
5397end;
5398
5399while WritePtr > LSectionStart do
5400begin
5401Dec(WritePtr);
5402WritePtr^ := '0';
5403end;
5404end;
5405
5406///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
5407/// About sections: conversion is finally done in digit sections. A section is, depending on base, the number ///
5408/// of digits that corresponds to the maximum power of the given base that fits in a NativeUInt. ///
5409/// ///
5410/// Example: the highest power of base 10 that fits in a UInt32 is 9 (so MaxPower for base 10 is 10^9 and the ///
5411/// number of digits that corresponds with it is 9: MaxDigits). These 9 digits form a section. Since these fit ///
5412/// in a UInt32, simple conversion can be done by dividing a UInt32 repeatedly by 10, which is considerably ///
5413/// faster than dividing a BigInteger by 10. That is why conversion is done in multiples of a section. FWIW, in ///
5414/// 64 bit code, the maxium power of 10 that fits in a UInt64 is 19, so in that case, a section for base 10 is ///
5415/// 19 digits. ///
5416/// ///
5417/// See bases.inc for MaxPower, MaxDigits and MaxFactor for each base and NativeUInt size. ///
5418///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
5419
5420function GetSectionCount(Size, Base: Integer): Integer;
5421begin
5422Result := (UInt64(Size) * CBaseInfos[Base].MaxFactor) shr (CMaxFactorShift - 5) + 1;
5423end;
5424
5425// This makes InternalRecursiveToString approx. 27% faster (on huge strings, like the large prime 2^74207281 - 1).
5426function GetBasePower(Base, Exponent: Integer; MaxPower: NativeUInt): BigInteger;
5427begin
5428if Exponent > High(CBasePowers[Base]) then
5429SetLength(CBasePowers[Base], Exponent + 1);
5430Result := CBasePowers[Base, Exponent];
5431
5432// Note that "uninitialized" BigIntegers have an FData of nil, so they return True on IsZero.
5433if Result.IsZero then
5434begin
5435
5436//////////////////////////////////////////////////////////////////////////////////////////////////////
5437/// Note: I tried using a LastExponent, and if the current exponent was above the last, it would ///
5438/// multiply the lastly found value with Pow(MaxPower, difference). But that did not provide any ///
5439/// improvement. ///
5440//////////////////////////////////////////////////////////////////////////////////////////////////////
5441
5442Result := BigInteger.Pow(MaxPower, Exponent);
5443CBasePowers[Base, Exponent] := Result;
5444end;
5445end;
5446
5447// cf. Brent, Zimmermann [3], "Modern Computer Arithmetic", algorithm 1.26
5448class procedure BigInteger.InternalRecursiveToString(const Value: BigInteger; Base: Integer;
5449const BaseInfo: TNumberBaseInfo; var WritePtr: PChar; SectionCount: Integer);
5450var
5451LHalfSectionCount: Integer;
5452LDivisor, LQuotient, LRemainder: BigInteger;
5453LSectionStart: PChar;
5454begin
5455LSectionStart := WritePtr - SectionCount * BaseInfo.MaxDigits;
5456
5457if SectionCount < RecursiveToStringThreshold then
5458begin
5459InternalPlainToString(Value, Base, BaseInfo, WritePtr, SectionCount);
5460Exit;
5461end;
5462
5463LHalfSectionCount:= SectionCount shr 1;
5464
5465LDivisor := GetBasePower(Base, LHalfSectionCount, BaseInfo.MaxPower);
5466BigInteger.DivMod(Value, LDivisor, LQuotient, LRemainder);
5467
5468InternalRecursiveToString(LRemainder, Base, BaseInfo, WritePtr, LHalfSectionCount);
5469InternalRecursiveToString(LQuotient, Base, BaseInfo, WritePtr, SectionCount - LHalfSectionCount);
5470
5471while WritePtr > LSectionStart do
5472begin
5473Dec(WritePtr);
5474WritePtr^ := '0';
5475end;
5476
5477end;
5478
5479function BigInteger.ToString: string;
5480begin
5481Result := ToString(FBase);
5482end;
5483
5484function BigInteger.ToString(Base: Integer): string;
5485var
5486WritePtr: PChar;
5487LBuffer: PChar;
5488LAbsValue: BigInteger;
5489LSectionCount: Integer; // Expected number of digit sections.
5490LBufLen: Integer;
5491LBaseInfo: TNumberBaseInfo;
5492begin
5493if not Base in [2..36] then
5494Error(ecInvalidBase, []);
5495
5496if FData = nil then
5497Exit('0');
5498
5499if FSize < 0 then
5500LAbsValue := -Self
5501else
5502LAbsValue := Self;
5503
5504LBaseInfo := CBaseInfos[Base];
5505
5506LSectionCount := GetSectionCount(LAbsValue.Size, Base);
5507LBufLen := (LBaseInfo.MaxDigits + 1) * (LSectionCount) + 1;
5508GetMem(LBuffer, LBufLen * SizeOf(Char));
5509try
5510WritePtr := LBuffer + LBufLen - 1;
5511WritePtr^ := #0;
5512
5513if Base in [2, 4, 16] then
5514// 2, 4 and 16 are easy: just take each limb, shift and output, from bottom (could also start from top,
5515// but this should be compatible with the other methods).
5516InternalShiftedToString(LAbsValue, Base, WritePtr)
5517else if (FSize and SizeMask) < RecursiveToStringThreshold then
5518// "Small" BigIntegers take the simple approach.
5519InternalPlainToString(LAbsValue, Base, LBaseInfo, WritePtr, LSectionCount)
5520else
5521// Large BigIntegers take the recursive divide-and-conquer approach.
5522InternalRecursiveToString(LAbsValue, Base, LBaseInfo, WritePtr, LSectionCount);
5523
5524while WritePtr^ = '0' do
5525Inc(WritePtr);
5526if FSize < 0 then
5527begin
5528Dec(WritePtr);
5529WritePtr^ := '-';
5530end;
5531
5532Result := string(WritePtr);
5533
5534finally
5535FreeMem(LBuffer);
5536end;
5537end;
5538
5539// For debugging purposes. May be removed, so don't use it for regular purposes.
5540function BigInteger.ToStringClassic(Base: Integer): string;
5541var
5542P: PChar;
5543LBuffer: TArray<Char>;
5544LMagnitude: TMagnitude;
5545LSize: Integer;
5546begin
5547if not Base in [2..36] then
5548Error(ecInvalidBase, []);
5549if FData = nil then
5550begin
5551Result := '0';
5552Exit;
5553end;
5554LSize := FSize and SizeMask;
5555SetLength(LBuffer, LSize * CStringMaxLengths[Base] + 1);
5556LMagnitude := System.Copy(FData);
5557P := PChar(LBuffer) + Length(LBuffer);
5558Dec(P);
5559P^ := #0;
5560while LSize > 0 do
5561begin
5562Dec(P);
5563P^ := CBaseChars[InternalDivideByBase(PLimb(LMagnitude), Base, LSize)];
5564end;
5565if FSize < 0 then
5566begin
5567Dec(P);
5568P^ := '-';
5569end;
5570Result := P;
5571end;
5572
5573// By default, uses FBase as numeric base, otherwise, if string "starts" with $, 0x, 0b or 0o, uses
5574// 16, 16 (both hex), 2 (binary) and 8 (octal) respectively.
5575class function BigInteger.TryParse(const S: string; var Value: BigInteger): Boolean;
5576var
5577LTrimmed: string;
5578LIsNegative: Boolean;
5579P: PChar;
5580LBase, LBaseNew: Integer;
5581begin
5582Result := False;
5583LTrimmed := UpperCase(Trim(S)); // Make string case insensitive.
5584if LTrimmed = '' then
5585Exit;
5586LIsNegative := False;
5587P := PChar(LTrimmed);
5588if (P^ = '-') or (P^ = '+') then
5589begin
5590LIsNegative := (P^ = '-');
5591Inc(P);
5592end;
5593LBase := FBase; // By default, use global numeric base.
5594case P^ of
5595'$': // $ prefix indicates hexadecimal (equivalent to 0x and %16r)
5596begin
5597Inc(P);
5598LBase := 16;
5599end;
5600'0':
5601begin
5602Inc(P);
5603case P^ of
5604#0:
5605begin
5606Value := Zero;
5607Exit(True);
5608end;
5609'B': // 0b prefix indicates binary (equivalent to %2r)
5610LBase := 2;
5611'O', 'K': // 0o17, 0k17 prefixes indicate octal (equivalent to %8r)
5612LBase := 8;
5613'X': // 0x prefix indicates hexadecimal (equivalent to $ and %16r)
5614LBase := 16;
5615'D':
5616LBase := 10;
5617else
5618Dec(P);
5619end;
5620Inc(P);
5621end;
5622'%': // %nnr prefix indicates base n (nn is always decimal)
5623begin
5624Inc(P);
5625LBaseNew := 0;
5626while P^ <> 'R' do
5627begin
5628if P^ = #0 then
5629Exit;
5630LBaseNew := LBaseNew * 10 + Ord(P^) - CNumBase;
5631Inc(P);
5632end;
5633Inc(P);
5634if not (LBaseNew in [2..36]) then
5635Exit;
5636LBase := LBaseNew;
5637end;
5638end;
5639Result := TryParse(P, LBase, Value);
5640if Result and LIsNegative then
5641Value := -Value;
5642end;
5643
5644// cf. Brent, Zimmermann, "Modern Computer Arithmetic", algorithm 1.23
5645class function BigInteger.TryParse(const S: string; ABase: TNumberBase; var AValue: BigInteger): Boolean;
5646var
5647LIsNegative: Boolean;
5648LTrimmed: string;
5649LVal: Integer;
5650P: PChar;
5651begin
5652Result := False;
5653LTrimmed := Trim(S);
5654if LTrimmed = '' then
5655Exit;
5656LIsNegative := False;
5657
5658AValue.MakeSize(Length(S) div CStringMinLengths[ABase] + 1);
5659AValue.FSize := 0;
5660
5661P := PChar(LTrimmed);
5662if (P^ = '-') or (P^ = '+') then
5663begin
5664LIsNegative := (P^ = '-');
5665Inc(P);
5666end;
5667if ABase = 10 then
5668Result := InternalParseDecimal(P, AValue)
5669else if ABase = 16 then
5670Result := InternalParseHex(P, AValue)
5671else
5672begin
5673while P^ <> #0 do
5674begin
5675if (P^ = '_') or (P^ = ' ') or (P^ = ',') then
5676begin
5677Inc(P);
5678Continue;
5679end;
5680LVal := Ord(P^);
5681Inc(P);
5682if LVal in [Ord('0')..Ord('9')] then
5683Dec(LVal, CNumBase)
5684else if LVal >= CAlphaBase then
5685begin
5686if LVal >= Ord('a') then
5687Dec(LVal, 32);
5688Dec(LVal, CAlphaBase - 10);
5689end
5690else
5691Exit;
5692if LVal >= ABase then
5693Exit;
5694InternalMultiplyAndAdd16(PLimb(AValue.FData), ABase, LVal, AValue.FSize);
5695end;
5696Result := True;
5697end;
5698if not Result then
5699begin
5700AValue := BigInteger.Zero;
5701Exit;
5702end;
5703{$IFDEF RESETSIZE}
5704AValue.Compact; // FSize is already correct, but Compact also reallocates down, if RESETSIZE requires it.
5705{$ENDIF}
5706if LIsNegative then
5707AValue := -AValue;
5708end;
5709
5710const
5711CIntPowersOfTen: array[1..9] of Integer =
5712(
571310,
5714100,
57151000,
571610*1000,
5717100*1000,
57181000*1000,
571910*1000*1000,
5720100*1000*1000,
57211000*1000*1000
5722);
5723
5724class function BigInteger.InternalParseDecimal(P: PChar; var Value: BigInteger): Boolean;
5725var
5726Cumulative: Cardinal;
5727N: Integer;
5728begin
5729Value := BigInteger.Zero;
5730Result := False;
5731while P^ <> #0 do
5732begin
5733N := 0;
5734Cumulative := 0;
5735while N < 9 do
5736begin
5737case P^ of
5738'_', ' ', ',':
5739begin
5740Inc(P); // Ignore!
5741Continue;
5742end;
5743#0:
5744Break;
5745'0'..'9':
5746Cumulative := Cumulative * 10 + Ord(P^) - Ord('0');
5747else
5748Exit;
5749end;
5750Inc(N);
5751Inc(P);
5752end;
5753Value := Value * CIntPowersOfTen[N] + Cumulative;
5754end;
5755Result := True;
5756end;
5757
5758class function BigInteger.InternalParseHex(P: PChar; var Value: BigInteger): Boolean;
5759var
5760Cumulative: Cardinal;
5761N: Integer;
5762begin
5763while P^ <> #0 do
5764begin
5765N := 0;
5766Cumulative := 0;
5767while N <= 7 do
5768begin
5769case P^ of
5770'_', ' ', ',':
5771begin
5772Inc(P); // Ignore!
5773Continue;
5774end;
5775#0:
5776Break;
5777'0'..'9':
5778Cumulative := Cumulative shl 4 + Ord(P^) - Ord('0');
5779'A'..'F':
5780Cumulative := Cumulative shl 4 + Ord(P^) - Ord('A') + 10;
5781'a'..'f':
5782Cumulative := Cumulative shl 4 + Ord(P^) - Ord('a') + 10;
5783else
5784Exit(False);
5785end;
5786Inc(N);
5787Inc(P);
5788end;
5789Value := Value shl (4 * N) + Cumulative;
5790end;
5791Result := True;
5792end;
5793
5794class procedure BigInteger.Decimal;
5795begin
5796FBase := 10;
5797end;
5798
5799class function BigInteger.Divide(const Left: BigInteger; Right: UInt16): BigInteger;
5800var
5801LSign: Integer;
5802begin
5803if Right = 0 then
5804Error(ecDivByZero, []);
5805if Left.FData = nil then
5806begin
5807ShallowCopy(Zero, Result);
5808Exit;
5809end;
5810LSign := Left.FSize and SignMask;
5811Result.MakeSize(Left.FSize and SizeMask);
5812InternalDivMod16(PLimb(Left.FData), Right, PLImb(Result.FData), nil, Left.FSize and SizeMask);
5813Result.Compact;
5814if Assigned(Result.FData) then
5815Result.FSize := (Result.FSize and SizeMask) or LSign;
5816end;
5817
5818class function BigInteger.Divide(const Left: BigInteger; Right: UInt32): BigInteger;
5819var
5820LSign: Integer;
5821begin
5822if Right = 0 then
5823Error(ecDivByZero, []);
5824if Left.FData = nil then
5825begin
5826ShallowCopy(Zero, Result);
5827Exit;
5828end;
5829LSign := Left.FSize and SignMask;
5830Result.MakeSize(Left.FSize and SizeMask);
5831InternalDivMod32(PLimb(Left.FData), Right, PLimb(Result.FData), nil, Left.FSize and SizeMask);
5832Result.Compact;
5833if Assigned(Result.FData) then
5834Result.FSize := (Result.FSize and SizeMask) or LSign;
5835end;
5836
5837class function BigInteger.Divide(const Left, Right: BigInteger): BigInteger;
5838var
5839Sign, LSize, RSize: Integer;
5840Remainder: BigInteger;
5841begin
5842if Right.FData = nil then
5843Error(ecDivByZero, []);
5844
5845if Left.FData = nil then
5846Exit(Zero);
5847
5848Sign := (Left.FSize and SignMask) xor (Right.FSize and SignMask);
5849LSize := Left.FSize and SizeMask;
5850RSize := Right.FSize and SizeMask;
5851
5852case InternalCompare(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize) of
5853-1:
5854begin
5855ShallowCopy(Zero, Result);
5856end;
58570:
5858begin
5859if Sign = 0 then
5860ShallowCopy(One, Result)
5861else
5862ShallowCopy(MinusOne, Result);
5863end;
5864else
5865begin
5866if ShouldUseBurnikelZiegler(LSize, RSize) then
5867DivModBurnikelZiegler(Left, Right, Result, Remainder)
5868else
5869DivModKnuth(Left, Right, Result, Remainder);
5870
5871if Result.FSize <> 0 then
5872Result.FSize := (Result.FSize and SizeMask) or Sign;
5873end;
5874end;
5875end;
5876
5877{$IFNDEF BIGINTEGERIMMUTABLE}
5878function BigInteger.Divide(const Other: BigInteger): PBigInteger;
5879begin
5880Result := @Self;
5881Self := Self div Other;
5882end;
5883{$ENDIF}
5884
5885class procedure BigInteger.DivMod(const Dividend, Divisor: BigInteger; var Quotient, Remainder: BigInteger);
5886var
5887LSize, RSize: Integer;
5888begin
5889if Divisor.FData = nil then
5890Error(ecDivByZero, []);
5891
5892LSize := Dividend.FSize and SizeMask;
5893RSize := Divisor.FSize and SizeMask;
5894
5895case InternalCompare(PLimb(Dividend.FData), PLimb(Divisor.FData), LSize, RSize) of
5896-1:
5897begin
5898ShallowCopy(Dividend, Remainder);
5899ShallowCopy(Zero, Quotient);
5900Exit;
5901end;
59020:
5903begin
5904if (Dividend.FSize xor Divisor.FSize) and SignMask = 0 then
5905ShallowCopy(One, Quotient)
5906else
5907ShallowCopy(MinusOne, Quotient);
5908ShallowCopy(Zero, Remainder);
5909Exit;
5910end
5911else
5912begin
5913if ShouldUseBurnikelZiegler(LSize, RSize) then
5914DivModBurnikelZiegler(Dividend, Divisor, Quotient, Remainder)
5915else
5916UncheckedDivModKnuth(Dividend, Divisor, Quotient, Remainder);
5917end;
5918end;
5919end;
5920
5921class procedure BigInteger.UncheckedDivModKnuth(const Left, Right: BigInteger; var Quotient, Remainder: BigInteger);
5922var
5923LSign, RSign: Integer;
5924LSize, RSize: Integer;
5925Q, R: BigInteger;
5926Offset: Integer; // Offset into left and right data when eliminating common trailing zero limbs.
5927
5928// Establish number of common trailing zero limbs.
5929function CommonTrailingZeros(const Left, Right: PLimb; LSize, RSize: Integer): Integer;
5930var
5931I: Integer;
5932begin
5933Result := 0;
5934for I := 0 to IntMin(LSize, RSize) - 1 do
5935if (Left[I] or Right[I]) <> 0 then
5936Exit(I);
5937end;
5938
5939begin
5940if Right.FData = nil then
5941Error(ecDivByZero, []);
5942
5943LSign := Left.FSize and SignMask;
5944RSign := Right.FSize and SignMask;
5945LSize := Left.FSize and SizeMask;
5946RSize := Right.FSize and SizeMask;
5947
5948if (LSize and RSize) <> 0 then
5949begin
5950Offset := CommonTrailingZeros(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize);
5951end
5952else
5953Offset := 0;
5954
5955Q.MakeSize(LSize - RSize + 1);
5956R.MakeSize(RSize + 1); // RSize should be enough, but apparently in 64 mode asm, it overwrites one extra limb.
5957if not InternalDivMod(PLimb(Left.FData) + Offset, PLimb(Right.FData) + Offset, PLimb(Q.FData),
5958PLimb(R.FData) + Offset, LSize - Offset, RSize - Offset) then
5959Error(ecInvalidBase, []);
5960Q.Compact;
5961R.Compact;
5962
5963if Q.FSize <> 0 then
5964Q.FSize := (Q.FSize and SizeMask) or (LSign xor RSign);
5965if R.FSize <> 0 then
5966R.FSize := (R.FSize and SizeMask) or LSign;
5967ShallowCopy(Q, Quotient);
5968ShallowCopy(R, Remainder);
5969end;
5970
5971class procedure BigInteger.DivModKnuth(const Left, Right: BigInteger; var Quotient, Remainder: BigInteger);
5972var
5973LSign, RSign: Integer;
5974LSize, RSize: Integer;
5975Q, R: BigInteger;
5976Offset: Integer; // Offset into left and right data when eliminating common trailing zero limbs.
5977
5978// Establish number of common trailing zero limbs.
5979function CommonTrailingZeros(const Left, Right: PLimb; LSize, RSize: Integer): Integer;
5980var
5981I: Integer;
5982begin
5983Result := 0;
5984for I := 0 to IntMin(LSize, RSize) - 1 do
5985if (Left[I] or Right[I]) <> 0 then
5986Exit(I);
5987end;
5988
5989begin
5990if Right.FData = nil then
5991Error(ecDivByZero, []);
5992
5993LSign := Left.FSize and SignMask;
5994RSign := Right.FSize and SignMask;
5995LSize := Left.FSize and SizeMask;
5996RSize := Right.FSize and SizeMask;
5997
5998if (LSize and RSize) <> 0 then
5999begin
6000Offset := CommonTrailingZeros(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize);
6001end
6002else
6003Offset := 0;
6004
6005case InternalCompare(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize) of
6006-1:
6007begin
6008ShallowCopy(Left, Remainder);
6009ShallowCopy(Zero, Quotient);
6010Exit;
6011end;
60120:
6013begin
6014ShallowCopy(Zero, Remainder);
6015if LSign = RSign then
6016ShallowCopy(One, Quotient)
6017else
6018ShallowCopy(MinusOne, Quotient);
6019Exit;
6020end
6021else
6022begin
6023Q.MakeSize(LSize - RSize + 1);
6024R.MakeSize(RSize + 1); // RSize should be enough, but apparently in 64 mode asm, it overwrites one extra limb.
6025if not InternalDivMod(PLimb(Left.FData) + Offset, PLimb(Right.FData) + Offset, PLimb(Q.FData),
6026PLimb(R.FData) + Offset, LSize - Offset, RSize - Offset) then
6027Error(ecInvalidBase, []);
6028Q.Compact;
6029R.Compact;
6030if Q.FSize <> 0 then
6031Q.FSize := (Q.FSize and SizeMask) or (LSign xor RSign);
6032if R.FSize <> 0 then
6033R.FSize := (R.FSize and SizeMask) or LSign;
6034ShallowCopy(Q, Quotient);
6035ShallowCopy(R, Remainder);
6036end;
6037end;
6038end;
6039
6040class procedure BigInteger.InternalShiftLeft(Source, Dest: PLimb; Shift, Size: Integer);
6041{$IF DEFINED(PUREPASCAL)}
6042var
6043I: Integer;
6044begin
6045Shift := Shift and 31;
6046if Shift = 0 then
6047CopyLimbs(Source, Dest, Size)
6048else
6049begin
6050Dest[Size] := Source[Size - 1] shr (CLimbBits - Shift);
6051for I := Size - 1 downto 1 do
6052Dest[I] := (Source[I] shl Shift) or (Source[I - 1] shr (CLimbBits - Shift));
6053Dest[0] := Source[0] shl Shift;
6054end;
6055end;
6056{$ELSEIF DEFINED(WIN32)}
6057asm
6058PUSH ESI
6059PUSH EDI
6060PUSH EBX
6061
6062MOV ESI,EAX
6063MOV EDI,EDX
6064XOR EAX,EAX
6065
6066// No need to test for nil.
6067MOV EBX,Size
6068
6069DEC EBX
6070JS @LoopEnd
6071
6072@ShiftLoop:
6073
6074MOV EDX,[ESI + CLimbSize*EBX]
6075SHLD EAX,EDX,CL
6076MOV [EDI + CLimbSize*EBX + CLimbSize],EAX
6077MOV EAX,EDX
6078
6079@ShiftStart:
6080
6081DEC EBX
6082JNS @ShiftLoop
6083
6084@LoopEnd:
6085
6086SHL EAX,CL
6087MOV [EDI],EAX
6088
6089@Exit:
6090
6091POP EBX
6092POP EDI
6093POP ESI
6094end;
6095{$ELSE}
6096asm
6097XCHG RCX,R8
6098MOV R10,RDX
6099
6100XOR EAX,EAX
6101DEC R9D
6102JS @LoopEnd
6103
6104@ShiftLoop:
6105
6106MOV EDX,[R8 + CLimbSize*R9]
6107SHLD EAX,EDX,CL
6108MOV [R10 + CLimbSize*R9 + CLimbSize],EAX
6109MOV EAX,EDX
6110
6111@ShiftStart:
6112
6113DEC R9D
6114JNS @ShiftLoop
6115
6116@LoopEnd:
6117
6118SHL EAX,CL
6119MOV [R10],EAX
6120
6121@Exit:
6122end;
6123{$IFEND}
6124
6125class procedure BigInteger.InternalShiftRight(Source, Dest: PLimb; Shift, Size: Integer);
6126{$IF DEFINED(PUREPASCAL)}
6127var
6128I: Integer;
6129begin
6130Shift := Shift and 31;
6131if Shift = 0 then
6132CopyLimbs(Source, Dest, Size)
6133else
6134begin
6135for I := 0 to Size - 1 do
6136Dest[I] := (Source[I] shr Shift) or (Source[I + 1] shl (CLimbBits - Shift));
6137Dest[Size - 1] := Source[Size - 1] shr Shift;
6138end;
6139end;
6140{$ELSEIF DEFINED(WIN32)}
6141asm
6142PUSH ESI
6143PUSH EDI
6144PUSH EBX
6145
6146MOV ESI,EAX
6147MOV EDI,EDX
6148MOV EBX,Size
6149MOV EAX,[ESI]
6150LEA ESI,[ESI + CLimbSize]
6151DEC EBX
6152JE @EndLoop
6153
6154@ShiftLoop:
6155
6156MOV EDX,[ESI]
6157SHRD EAX,EDX,CL
6158MOV [EDI],EAX
6159MOV EAX,EDX
6160LEA ESI,[ESI + CLimbSize]
6161LEA EDI,[EDI + CLimbSize]
6162DEC EBX
6163JNE @ShiftLoop
6164
6165@EndLoop:
6166
6167SHR EAX,CL
6168MOV [EDI],EAX
6169
6170@Exit:
6171
6172POP EBX
6173POP EDI
6174POP ESI
6175end;
6176{$ELSE}
6177asm
6178XCHG RCX,R8 // R8 = source, ECX = shift
6179
6180MOV EAX,[R8]
6181LEA R8,[R8 + CLimbSize]
6182DEC R9D
6183JE @LoopEnd
6184
6185@ShiftLoop:
6186
6187MOV R10D,[R8]
6188SHRD EAX,R10D,CL
6189MOV [RDX],EAX
6190MOV EAX,R10D
6191LEA RDX,[RDX + CLimbSize]
6192LEA R8,[R8 + CLimbSize]
6193DEC R9D
6194JNE @ShiftLoop
6195
6196@LoopEnd:
6197
6198SHR EAX,CL
6199MOV [RDX],EAX
6200
6201@Exit:
6202
6203end;
6204{$IFEND}
6205
6206type
6207{$IFDEF CPU64BITS}
6208TDivLimb = UInt32;
6209TDblLimb = UInt64;
6210{$ELSE}
6211TDivLimb = UInt16;
6212TDblLimb = UInt32;
6213{$ENDIF}
6214PDivLimb = ^TDivLimb;
6215PDblLimb = ^TDblLimb;
6216
6217const
6218CDivLimbBase = TDblLimb(High(TDivLimb)) + 1;
6219CDivLimbBits = SizeOf(TDivLimb) * 8;
6220CDblLimbBits = SizeOf(TDblLimb) * 8;
6221
6222class function BigInteger.InternalDivMod16(Dividend: PLimb; Divisor: UInt16; Quotient, Remainder: PLimb;
6223LSize: Integer): Boolean;
6224{$IFDEF PUREPASCAL}
6225// In PUREPASCAL, using 16-bit division with an intermediate 32-bit result turned out to be faster than
6226// 32-bit division with an intermediate 64-bit result.
6227type
6228PUInt16 = ^UInt16;
6229var
6230J: Integer;
6231LRemainder: UInt16;
6232begin
6233LSize := LSize + LSize;
6234
6235LRemainder := 0;
6236for J := LSize - 1 downto 0 do
6237System.Math.DivMod(Cardinal(LRemainder shl 16 + PUInt16(Dividend)[J]), Divisor, PUInt16(Quotient)[J], LRemainder);
6238
6239if Assigned(Remainder) then
6240Remainder[0] := LRemainder;
6241Exit(True);
6242end;
6243{$ELSE !PUREPASCAL}
6244// In assembler, 32 bit division is faster, so promote divisor to 32 bit and use InternalDivMod32.
6245begin
6246Result := InternalDivMod32(Dividend, UInt32(Divisor), Quotient, Remainder, LSize);
6247end;
6248{$ENDIF !PUREPASCAL}
6249
6250class function BigInteger.InternalDivMod32(Dividend: PLimb; Divisor: UInt32; Quotient, Remainder: PLimb;
6251LSize: Integer): Boolean;
6252{$IFDEF PUREPASCAL}
6253{$IFDEF CPU32BITS}
6254begin
6255// In 32PP, plain division using System.Math.DivMod(UInt64, ...) is much slower than this:
6256Result := InternalDivMod(Dividend, @Divisor, Quotient, Remainder, LSize, 1);
6257end;
6258{$ELSE CPU64BITS}
6259var
6260J: Integer;
6261LQuotient, LRemainder: UInt64;
6262begin
6263LRemainder := 0;
6264for J := LSize - 1 downto 0 do
6265begin
6266// DivMod(UInt64, UInt64, var UInt64, var UInt64)
6267{$IFOPT R+}
6268{$DEFINE RCHECKS}
6269{$R-}
6270{$ENDIF}
6271System.Math.DivMod((LRemainder shl 32) or Dividend[J], Divisor, LQuotient, LRemainder);
6272{$IFDEF RCHECKS}
6273{$R+}
6274{$UNDEF RCHECKS}
6275{$ENDIF}
6276Quotient[J] := TLimb(LQuotient);
6277end;
6278if Assigned(Remainder) then
6279Remainder[0] := TLimb(LRemainder);
6280Exit(True);
6281end;
6282{$ENDIF CPU64BITS}
6283{$ELSE !PUREPASCAL}
6284{$IFDEF WIN32}
6285asm
6286
6287// Note: in some versions of Delphi, DIV EBX generates the wrong opcode, while DIV EAX,EBX doesn't. The same for
6288// MUL EBX and MUL EAX,EBX.
6289
6290PUSH ESI
6291PUSH EDI
6292PUSH EBX
6293
6294MOV EBX,EDX
6295
6296MOV EDI,LSize
6297LEA ESI,[EAX + CLimbSize*EDI - CLimbSize]
6298LEA ECX,[ECX + CLimbSize*EDI - CLimbSize]
6299XOR EDX,EDX
6300
6301SHR EDI,CUnrollShift
6302JE @Tail
6303
6304@DivLoop:
6305
6306MOV EAX,[ESI]
6307DIV EAX,EBX
6308MOV [ECX],EAX
6309
6310MOV EAX,[ESI - CLimbSize]
6311DIV EAX,EBX
6312MOV [ECX - CLimbSize],EAX
6313
6314MOV EAX,[ESI - 2 * CLimbSize]
6315DIV EAX,EBX
6316MOV [ECX - 2 * CLimbSize],EAX
6317
6318MOV EAX,[ESI - 3 * CLimbSize]
6319DIV EAX,EBX
6320MOV [ECX - 3 * CLimbSize],EAX
6321
6322LEA ESI,[ESI - 4 * CLimbSize]
6323LEA ECX,[ECX - 4 * CLimbSize]
6324DEC EDI
6325JNE @DivLoop
6326
6327@Tail:
6328
6329MOV EDI,LSize
6330AND EDI,CUnrollMask
6331JE @StoreRemainder
6332
6333@TailLoop:
6334
6335MOV EAX,[ESI]
6336DIV EAX,EBX
6337MOV [ECX],EAX
6338LEA ESI,[ESI - CLimbSize]
6339LEA ECX,[ECX - CLimbSize]
6340DEC EDI
6341JNE @TailLoop
6342
6343@StoreRemainder:
6344
6345MOV EBX,Remainder
6346OR EBX,EBX
6347JE @Exit
6348
6349MOV [EBX],EDX
6350
6351@Exit:
6352
6353MOV EAX,1
6354
6355POP EBX
6356POP EDI
6357POP ESI
6358
6359end;
6360{$ELSE WIN64}
6361asm
6362MOV R10D,EDX
6363
6364MOV R11D,LSize
6365LEA RCX,[RCX + R11*CLimbSize]
6366LEA R8,[R8 + R11*CLimbSize]
6367XOR EDX,EDX
6368
6369SHR R11D,CUnrollShift
6370JE @Tail
6371
6372@DivLoop:
6373
6374// Note: 64 bit division turned out to be considerably slower!
6375
6376MOV EAX,[RCX - CLimbSize]
6377DIV EAX,R10D // Uses DIV EAX,R10D syntax because of bug in XE 64 bit assembler.
6378MOV [R8 - CLimbSize],EAX
6379
6380MOV EAX,[RCX - 2 * CLimbSize]
6381DIV EAX,R10D
6382MOV [R8 - 2 * CLimbSize],EAX
6383
6384MOV EAX,[RCX - 3 * CLimbSize]
6385DIV EAX,R10D
6386MOV [R8 - 3 * CLimbSize],EAX
6387
6388MOV EAX,[RCX - 4 * CLimbSize]
6389DIV EAX,R10D
6390MOV [R8 - 4 * CLimbSize],EAX
6391
6392LEA RCX,[RCX - 4 * CLimbSize]
6393LEA R8,[R8 - 4 * CLimbSize]
6394DEC R11D
6395JNE @DivLoop
6396
6397@Tail:
6398
6399MOV R11D,LSize
6400AND R11D,CUnrollMask
6401JE @StoreRemainder
6402
6403@TailLoop:
6404
6405MOV EAX,[RCX - ClimbSize]
6406DIV EAX,R10D
6407MOV [R8 - CLimbSize],EAX
6408LEA RCX,[RCX - CLimbSize]
6409LEA R8,[R8 - CLimbSize]
6410DEC R11D
6411JNE @TailLoop
6412
6413@StoreRemainder:
6414
6415OR R9,R9
6416JE @Exit
6417MOV [R9],EDX
6418
6419@Exit:
6420
6421MOV EAX,1
6422
6423end;
6424{$ENDIF}
6425{$ENDIF PUREPASCAL}
6426
6427class function BigInteger.InternalDivMod(Dividend, Divisor, Quotient, Remainder: PLimb; LSize, RSize: Integer): Boolean;
6428
6429// Basecase division, see Knuth TAOCP, Vol. 2.
6430
6431{$IF DEFINED(PUREPASCAL)}
6432var
6433PDividend, PDivisor, PQuotient, PRemainder: PDivLimb;
6434NormDividend, NormDivisor: TArray<TDivLimb>; // Normalized dividend and divisor
6435QHat: TDblLimb; // Estimate quotient limb
6436RHat: TDblLimb; // Remainder after calculating QHat
6437Product: TDblLimb; // Product of limb and QHat
6438Shift, RevShift, I, J: Integer; // Help variables
6439NormDividendTop2, NormDivisorTop: TDblLimb;
6440{$IF SizeOf(TDivLimb) = SizeOf(TLimb)}
6441Carry, Value: Int64;
6442{$ELSE}
6443Carry, Value: Integer;
6444{$IFEND}
6445begin
6446Assert(SizeOf(TDblLimb) = 2 * SizeOf(TDivLimb));
6447PDividend := PDivLimb(Dividend);
6448PDivisor := PDivLimb(Divisor);
6449PQuotient := PDivLimb(Quotient);
6450PRemainder := PDivLimb(Remainder);
6451
6452{$IF SizeOf(TLimb) > SizeOf(TDivLimb)}
6453LSize := LSize + LSize;
6454RSize := RSize + RSize;
6455
6456if PDivisor[RSize - 1] = 0 then
6457Dec(RSize);
6458{$IFEND}
6459
6460///////////////////////////////////////////////////////////////////////////////////////////////////////
6461/// NOTE: In Win32, this uses 16-bit division (with 32-bit intermediate results) to avoid having ///
6462/// to use 64-bit unsigned integers. This turned out to be (approx. 17%) faster than using ///
6463/// 32-bit limbs. ///
6464/// In Win64, this uses 32-bit division with 64-bit intermediate results. ///
6465///////////////////////////////////////////////////////////////////////////////////////////////////////
6466
6467if (LSize < RSize) then
6468Exit(False);
6469
6470while (RSize > 0) and (PDivisor[RSize - 1] = 0) do
6471Dec(RSize);
6472if RSize = 0 then
6473Exit(False);
6474
6475while (LSize > 0) and (PDividend[LSize - 1] = 0) do
6476Dec(LSize);
6477
6478///////////////////////////////////////////////////////////////////////////////////////////////////////
6479/// Perhaps it makes sense to shift away common trailing zero limbs, if divisor > certain size. ///
6480/// Shifting should be pretty simple: simply remove any common zeroes in both dividend and divisor, ///
6481/// generate an offset to the lowest non-zero limb and shift accordingly (when normalizing). ///
6482/// Note that the remainder must be amended accordingly. ///
6483/// Note2: No need to amend the result: x / y == (x/n) / (y/n), when n is the trailing zero part. ///
6484///////////////////////////////////////////////////////////////////////////////////////////////////////
6485
6486if RSize = 1 then
6487begin
6488// Handle single-digit divisor.
6489
6490{$IF SizeOf(TDivLimb) = SizeOf(TLimb)}
6491Exit(InternalDivMod32(Dividend, PDivisor[0], Quotient, Remainder, LSize));
6492{$ELSE}
6493Exit(InternalDivMod16(Dividend, PDivisor[0], Quotient, Remainder, (LSize + 1) div 2));
6494{$IFEND}
6495end;
6496// Normalize by shifting divisor left just enough so that its high-order bit is set, and shift
6497// dividend left the same amount. A high-order digit is prepended to dividend unconditionally.
6498
6499// Get number of leading zeroes.
6500Shift := Velthuis.Numerics.NumberOfLeadingZeros(PDivisor[RSize - 1]); // 0 <= Shift < Bits.
6501RevShift := CDivLimbBits - Shift;
6502
6503// Normalize divisor and shift dividend left accordingly.
6504SetLength(NormDivisor, RSize);
6505SetLength(NormDividend, LSize + 1);
6506if Shift > 0 then
6507begin
6508for I := RSize - 1 downto 1 do
6509NormDivisor[I] := TDivLimb((TDblLimb(PDivisor[I]) shl Shift) or (TDblLimb(PDivisor[I - 1]) shr RevShift));
6510NormDivisor[0] := TDivLimb(TDblLimb(PDivisor[0]) shl Shift);
6511
6512NormDividend[LSize] := PDividend[LSize - 1] shr RevShift;
6513for I := LSize - 1 downto 1 do
6514NormDividend[I] := TDivLimb((TDblLimb(PDividend[I]) shl Shift) or (TDblLimb(PDividend[I - 1]) shr RevShift));
6515NormDividend[0] := TDivLimb(TDblLimb(PDividend[0]) shl Shift);
6516end
6517else
6518begin
6519// SizeOf(TDivLimb) is not always SizeOf(TLimb), so don't use MoveLimbs() here.
6520Move(PDivisor[0], NormDivisor[0], RSize * SizeOf(TDivLimb));
6521Move(PDividend[0], NormDividend[0], LSize * SizeOf(TDivLimb));
6522end;
6523
6524// Knuth's basecase algorithm.
6525
6526// Main loop.
6527for J := LSize - RSize downto 0 do
6528begin
6529NormDivisorTop := NormDivisor[RSize - 1];
6530NormDividendTop2 := PDblLimb(@NormDividend[J + RSize - 1])^;
6531
6532// QHat -- q^ in TAOCP -- is (first) estimate of Quotient[J]
6533QHat := NormDividendTop2 div NormDivisorTop;
6534
6535// RHat -- r^ in TAOCP -- is remainder belonging to q^.
6536RHat := NormDividendTop2 - QHat * NormDivisorTop;
6537
6538while (QHat * NormDivisor[RSize - 2] > RHat shl CDivLimbBits + NormDividend[J + RSize - 2]) or
6539(QHat >= CDivLimbBase) do
6540begin
6541Dec(QHat);
6542Inc(RHat, NormDivisorTop);
6543
6544if RHat >= CDivLimbBase then
6545Break;
6546end;
6547
6548// Multiply and subtract.
6549Carry := 0;
6550for I := 0 to RSize - 1 do
6551begin
6552Product := QHat * NormDivisor[I];
6553Value := NormDividend[I + J] - Carry - TDivLimb(Product);
6554NormDividend[I + J] := TDivLimb(Value);
6555{$IF SizeOf(TLimb) = SizeOf(TDivLimb)}
6556// Integer cast to force sign-extension of 'Value shr Bits'
6557Carry := Int64(Product) shr CDivLimbBits - Integer(Value shr CDivLimbBits);
6558{$ELSE}
6559// Smallint cast to force sign-extension of 'Value shr Bits'
6560Carry := Integer(Product) shr CDivLimbBits - Smallint(Value shr CDivLimbBits);
6561{$IFEND}
6562end;
6563Value := NormDividend[J + RSize] - Carry;
6564NormDividend[J + RSize] := TDivLimb(Value);
6565
6566if Value < 0 then
6567begin
6568
6569// If too much was subtracted, add back.
6570Dec(QHat);
6571Value := 0;
6572for I := 0 to RSize - 1 do
6573begin
6574
6575/////////////////////////////////////////////////////////////////////////////////////////////////
6576/// Note: the original code was: ///
6577/// ///
6578/// Value := NormDividend[I + J] + NormDivisor[I] + Value shr CDivLimbBits; ///
6579/// ///
6580/// That caused bad results in 64 bit, probably because the first two operands were ///
6581/// treated as 32 bit first, i.e. ///
6582/// ///
6583/// UInt64 := UInt64(UInt32 + UInt32) + UInt64; ///
6584/// ///
6585/// instead of ///
6586/// ///
6587/// UInt64 := UInt64 + UInt64 + UInt64; ///
6588/////////////////////////////////////////////////////////////////////////////////////////////////
6589
6590Value := Value shr CDivLimbBits + NormDividend[I + J] + NormDivisor[I];
6591NormDividend[I + J] := TDivLimb(Value);
6592end;
6593Inc(NormDividend[J + RSize], Value shr CDivLimbBits);
6594end;
6595
6596PQuotient[J] := QHat;
6597end;
6598
6599// If the caller wants the remainder, unnormalize it and pass it back.
6600if Assigned(PRemainder) then
6601if Shift <> 0 then
6602for I := 0 to RSize - 1 do
6603PRemainder[I] := TDivLimb((TDblLimb(NormDividend[I]) shr Shift) or (TDblLimb(NormDividend[I + 1]) shl RevShift))
6604else
6605for I := 0 to RSize - 1 do
6606PRemainder[I] := NormDividend[I];
6607
6608Result := True;
6609end;
6610{$ELSEIF DEFINED(WIN32)}
6611var
6612LDividend, LDivisor, LQuotient: PLimb; // Local copies of passed registers
6613NormDividend, NormDivisor: PLimb; // Manually managed dynamic arrays
6614QHat, RHat, Product: TUInt64; // 64 bit intermediate results
6615Overflow: TLimb; // "Carry" between multiplications
6616Shift: Integer; // Normalization shift
6617asm
6618PUSH ESI
6619PUSH EDI
6620PUSH EBX
6621
6622// To avoid reference count problems with Delphi's dynamic array types, we do our own,
6623// "old school" dynarrays, using GetMem and FreeMem.
6624
6625XOR EBX,EBX // Set "dynarrays" to nil, so the FreeMem calls won't fail.
6626MOV NormDividend,EBX
6627MOV NormDivisor,EBX
6628
6629MOV LDividend,EAX
6630MOV LDivisor,EDX
6631MOV LQuotient,ECX
6632
6633MOV ESI,LSize
6634MOV EDI,RSize
6635CMP ESI,EDI
6636JL @ExitFalse
6637
6638DEC EDI
6639JS @ExitFalse
6640JNE @MultiLimbDivisor
6641
6642/////////////////////////////////////////////////////////////////////////////////////////////////////////
6643/// Simple division ///
6644/// Divisor only contains one single limb: simple division and exit. ///
6645/////////////////////////////////////////////////////////////////////////////////////////////////////////
6646
6647@SingleLimbDivisor:
6648
6649MOV EBX,[EDX]
6650DEC ESI
6651MOV EDI,EAX
6652XOR EDX,EDX
6653
6654@SingleDivLoop:
6655
6656MOV EAX,[EDI + CLimbSize*ESI]
6657DIV EAX,EBX
6658MOV [ECX + CLimbSize*ESI],EAX
6659DEC ESI
6660JNS @SingleDivLoop
6661MOV EAX,Remainder
6662TEST EAX,EAX
6663JZ @ExitTrue
6664MOV [EAX],EDX
6665JMP @ExitTrue
6666
6667/////////////////////////////////////////////////////////////////////////////////////////////////////////
6668/// Multilimb division ///
6669/// Divisor contains more than one limb: basecase division as described in Knuth's TAoCP. ///
6670/////////////////////////////////////////////////////////////////////////////////////////////////////////
6671
6672@MultiLimbDivisor:
6673
6674MOV EAX,RSize // GetMem(NormDivisor, (RSize + LSize + 1) * CLimbSize;
6675ADD EAX,LSize
6676LEA EAX,[EAX*CLimbSize + CLimbSize]
6677CALL System.AllocMem
6678MOV NormDivisor,EAX
6679MOV EDX,RSize
6680LEA EAX,[EAX + EDX*CLimbSize]
6681MOV NormDividend,EAX
6682
6683// First: normalize Divisor by shifting left to eliminate leading zeroes
6684// and shift Dividend left by same number of bits.
6685
6686// Get number of leading Divisor zeros (into ECX).
6687
6688MOV ESI,LDivisor
6689MOV EBX,[ESI+CLimbSize*EDI]
6690BSR EBX,EBX
6691MOV ECX,31
6692SUB ECX,EBX
6693MOV Shift,ECX
6694
6695// Shift Divisor to NormDivisor by CL.
6696
6697MOV EBX,EDI
6698MOV EDI,NormDivisor
6699MOV EAX,[ESI + CLimbSize*EBX]
6700JMP @ShiftDivisor
6701
6702@ShiftDivisorLoop:
6703
6704MOV EDX,[ESI + CLimbSize*EBX]
6705SHLD EAX,EDX,CL
6706MOV [EDI + CLimbSize*EBX + CLimbSize],EAX
6707MOV EAX,EDX
6708
6709@ShiftDivisor:
6710
6711DEC EBX
6712JNS @ShiftDivisorLoop
6713
6714// Handle lowest limb.
6715
6716SHL EAX,CL
6717MOV [EDI],EAX
6718
6719// Shift Dividend to NormDividend by CL.
6720
6721MOV EBX,LSize
6722MOV ESI,LDividend
6723MOV EDI,NormDividend
6724XOR EAX,EAX
6725JMP @ShiftDividend
6726
6727@ShiftDividendLoop:
6728
6729MOV EDX,[ESI + CLimbSize*EBX]
6730SHLD EAX,EDX,CL
6731MOV [EDI + CLimbSize*EBX + CLimbSize],EAX
6732MOV EAX,EDX
6733
6734@ShiftDividend:
6735
6736DEC EBX
6737JNS @ShiftDividendLoop
6738
6739// Handle lowest limb.
6740
6741SHL EAX,CL
6742MOV [EDI],EAX
6743
6744MOV EBX,LSize
6745MOV ECX,RSize
6746
6747MOV ESI,NormDividend
6748MOV EDI,NormDivisor
6749LEA EDI,[EDI + CLimbSize*ECX - CLimbSize]
6750
6751@MainLoop:
6752
6753XOR EDX,EDX
6754MOV EAX,[ESI + CLimbSize*EBX]
6755DIV EAX,[EDI]
6756MOV QHat.Hi,EAX
6757MOV EAX,[ESI + CLimbSize*EBX - CLimbSize]
6758DIV EAX,[EDI]
6759MOV QHat.Lo,EAX
6760MOV RHat.Lo,EDX
6761XOR EDX,EDX
6762MOV RHat.Hi,EDX
6763
6764@CheckAdjust:
6765
6766CMP QHat.Hi,0
6767JNE @DoAdjust
6768MOV EAX,QHat.Lo
6769MUL EAX,[EDI - CLimbSize]
6770
6771CMP EDX,RHat.Lo
6772JA @DoAdjust
6773JB @AdjustEnd
6774CMP EAX,[ESI + CLimbSize*EBX - 2*CLimbSize]
6775JBE @AdjustEnd
6776
6777@DoAdjust:
6778
6779SUB QHat.Lo,1
6780SBB QHat.Hi,0
6781MOV EAX,[EDI]
6782ADD RHat.Lo,EAX
6783ADC RHat.Hi,0
6784JZ @CheckAdjust
6785
6786@AdjustEnd:
6787
6788// Now multiply NormDivisor by QHat and subtract the product from NormDividend[J].
6789
6790// Save a few registers.
6791
6792PUSH EDI
6793PUSH EBX
6794PUSH ECX
6795
6796MOV ECX,EBX
6797SUB ECX,RSize
6798LEA EDI,[ESI + CLimbSize*ECX]
6799MOV EAX,LQuotient
6800MOV EDX,QHat.Lo
6801MOV [EAX + CLimbSize*ECX],EDX
6802XOR EBX,EBX
6803MOV Overflow,EBX
6804
6805@SubtractProduct:
6806
6807MOV EAX,NormDivisor
6808MOV EAX,[EAX + CLimbSize*EBX]
6809MUL EAX,QHat.Lo
6810MOV Product.Lo,EAX
6811MOV Product.Hi,EDX
6812XOR EDX,EDX
6813MOV EAX,[EDI + CLimbSize*EBX]
6814SUB EAX,Overflow
6815SBB EDX,0
6816SUB EAX,Product.Lo
6817SBB EDX,0
6818MOV [EDI + CLimbSize*EBX],EAX
6819MOV EAX,Product.Hi
6820SUB EAX,EDX
6821MOV Overflow,EAX
6822INC EBX
6823CMP EBX,RSize
6824JL @SubtractProduct
6825
6826@SubtractProductEnd:
6827
6828MOV EBX,[ESP + 4]
6829MOV EDX,[ESI + CLimbSize*EBX]
6830SUB EDX,Overflow
6831MOV [ESI + CLimbSize*EBX],EDX
6832JNC @SkipAddBack
6833
6834// Add normalized divisor back, if necessary:
6835
6836MOV EAX,LQuotient
6837DEC [EAX + CLimbSize*ECX]
6838XOR EBX,EBX
6839MOV Overflow,EBX
6840
6841@AddBackLoop:
6842
6843CMP EBX,RSize
6844JE @AddBackLoopEnd
6845
6846XOR EDX,EDX
6847MOV EAX,NormDivisor
6848MOV EAX,[EAX + CLimbSize*EBX]
6849ADD EAX,Overflow
6850
6851ADC EDX,0 // Note: forgetting this caused errors that only exhibited when I started testing ModPow.
6852
6853ADD [EDI + CLimbSize*EBX],EAX
6854ADC EDX,0
6855MOV Overflow,EDX
6856INC EBX
6857JMP @AddBackLoop
6858
6859@AddBackLoopEnd:
6860
6861ADD [EDI + CLimbSize*EBX],EDX
6862
6863@SkipAddBack:
6864
6865POP ECX
6866POP EBX
6867POP EDI
6868
6869// End of main loop; loop if required.
6870
6871DEC EBX
6872CMP EBX,ECX
6873JGE @MainLoop
6874
6875// NormDividend now contains remainder, scaled by Shift.
6876// If Assigned(Remainder), then shift NormDividend down into Remainder.
6877
6878MOV EAX,Remainder
6879TEST EAX,EAX
6880JE @ExitTrue
6881XOR EBX,EBX
6882MOV ESI,NormDividend
6883MOV EDI,EAX
6884MOV ECX,Shift
6885MOV EAX,[ESI + CLimbSize*EBX]
6886
6887@RemainderLoop:
6888
6889MOV EDX,[ESI + CLimbSize*EBX + CLimbSize]
6890SHRD EAX,EDX,CL
6891MOV [EDI + CLimbSize*EBX],EAX
6892MOV EAX,EDX
6893INC EBX
6894CMP EBX,RSize
6895JL @RemainderLoop
6896SHR EDX,CL
6897MOV [EDI + CLimbSize*EBX],EDX
6898JMP @ExitTrue
6899
6900@ExitFalse:
6901
6902MOV BL,0
6903JMP @Exit
6904
6905@ExitTrue:
6906
6907MOV BL,1
6908
6909@Exit:
6910
6911// Clear dynamic arrays.
6912
6913MOV EAX,NormDivisor
6914CALL System.@FreeMem
6915
6916MOV EAX,EBX
6917
6918POP EBX
6919POP EDI
6920POP ESI
6921end;
6922{$ELSE}
6923var
6924LDividend, LDivisor, LQuotient, LRemainder: PLimb;
6925NormDividend, NormDivisor: PLimb;
6926QHat, RHat, Product: TUInt64;
6927Overflow: TLimb;
6928Shift: Integer;
6929SaveRDI, SaveRBX, SaveRCX: NativeUInt;
6930asm
6931.PUSHNV RSI
6932.PUSHNV RDI
6933.PUSHNV RBX
6934
6935// To avoid reference count problems with Delphi's dynamic array types, we do our own,
6936// "old school" dynarrays, using GetMem and FreeMem.
6937
6938XOR EBX,EBX // Set "dynarrays" to nil, so FreeMem calls won't fail.
6939MOV NormDividend,RBX
6940MOV NormDivisor,RBX
6941
6942MOV LDividend,RCX
6943MOV LDivisor,RDX
6944MOV LQuotient,R8
6945MOV LRemainder,R9
6946
6947MOV ESI,LSize
6948MOV EDI,RSize
6949CMP ESI,EDI
6950JL @ExitFalse
6951
6952DEC EDI
6953JS @ExitFalse
6954JNE @MultiLimbDivisor
6955
6956/////////////////////////////////////////////////////////////////////////////////////////////////////////
6957/// Simple division ///
6958/// Divisor only contains one single limb: simple division and exit. ///
6959/// ///
6960/// NOTE: 32 bit division is easier and probably faster than 64 bit, even in 64 bit mode. ///
6961/// This was tested for Decimals.pas. ///
6962/////////////////////////////////////////////////////////////////////////////////////////////////////////
6963
6964@SingleLimbDivisor:
6965
6966MOV EBX,[RDX]
6967
6968DEC ESI
6969MOV RDI,RCX
6970XOR EDX,EDX
6971
6972@SingleDivLoop:
6973
6974MOV EAX,[RDI + CLimbSize*RSI]
6975
6976// ------------------------------------------------------------------------------------------- //
6977// NOTE: In XE2, in 64 bit asm, "DIV <r/m32>" is generated as "DIV <r/m64>", //
6978// but "DIV EAX,<r/m32>" is generated correctly. //
6979// The same applies to "MUL <r/m32>". //
6980// ------------------------------------------------------------------------------------------- //
6981
6982DIV EAX,EBX
6983MOV [R8 + CLimbSize*RSI],EAX
6984DEC ESI
6985JNS @SingleDivLoop
6986MOV RAX,LRemainder
6987TEST RAX,RAX
6988JZ @ExitTrue
6989MOV [RAX],EDX
6990JMP @ExitTrue
6991
6992/////////////////////////////////////////////////////////////////////////////////////////////////////////
6993/// MultiLimb division ///
6994/// Divisor contains more than one limb: basecase division as described in Knuth's TAoCP Vol. 2. ///
6995/////////////////////////////////////////////////////////////////////////////////////////////////////////
6996
6997@MultiLimbDivisor:
6998
6999MOV ECX,RSize
7000ADD ECX,ECX
7001ADD ECX,ECX
7002CALL System.AllocMem
7003MOV NormDivisor,RAX
7004
7005MOV ECX,LSize
7006INC ECX
7007ADD ECX,ECX
7008ADD ECX,ECX
7009CALL System.AllocMem
7010MOV NormDividend,RAX
7011
7012// First: normalize Divisor by shifting left to eliminate leading zeroes
7013// and shift Dividend left by same nubmer of bits.
7014
7015// Get number of leading Divisor zeroes (into ECX).
7016
7017MOV RSI,LDivisor
7018MOV EBX,[RSI + CLimbSize*RDI]
7019BSR EBX,EBX
7020MOV ECX,31
7021SUB ECX,EBX
7022MOV Shift,ECX
7023
7024// Shift Divisor to NormDivisor by CL.
7025
7026MOV EBX,EDI
7027MOV RDI,NormDivisor
7028MOV EAX,[RSI + CLimbSize*RBX]
7029JMP @ShiftDivisor
7030
7031@ShiftDivisorLoop:
7032
7033MOV EDX,[RSI + CLimbSize*RBX]
7034SHLD EAX,EDX,CL
7035MOV [RDI + CLimbSize*RBX + CLimbSize],EAX
7036MOV EAX,EDX
7037
7038@ShiftDivisor:
7039
7040DEC EBX
7041JNS @ShiftDivisorLoop
7042
7043// Handle lowest limb.
7044
7045SHL EAX,CL
7046MOV [RDI],EAX
7047
7048// Shift Dividend to NormDividend by CL.
7049
7050MOV EBX,LSize
7051MOV RSI,LDividend
7052MOV RDI,NormDividend
7053XOR EAX,EAX
7054JMP @ShiftDividend
7055
7056@ShiftDividendLoop:
7057
7058MOV EDX,[RSI + CLimbSize*RBX]
7059SHLD EAX,EDX,CL
7060MOV [RDI + CLimbSize*RBX + CLimbSize],EAX
7061MOV EAX,EDX
7062
7063@ShiftDividend:
7064
7065DEC EBX
7066JNS @ShiftDividendLoop
7067
7068// Handle lowest limb.
7069
7070SHL EAX,CL
7071MOV [RDI],EAX
7072
7073MOV EBX,LSize
7074MOV ECX,RSize
7075
7076MOV RSI,NormDividend
7077MOV RDI,NormDivisor
7078LEA RDI,[RDI + CLimbSize*RCX - CLimbSize]
7079
7080@MainLoop:
7081
7082XOR EDX,EDX
7083MOV EAX,[RSI + CLimbSize*RBX]
7084DIV EAX,[RDI]
7085MOV QHat.Hi,EAX
7086MOV EAX,[RSI + CLimbSize*RBX - CLimbSize]
7087DIV EAX,[RDI]
7088MOV QHat.Lo,EAX
7089MOV RHat.Lo,EDX
7090XOR EDX,EDX
7091MOV RHat.Hi,EDX
7092
7093@CheckAdjust:
7094
7095CMP QHat.Hi,0
7096JNE @DoAdjust
7097MOV EAX,QHat.Lo
7098MUL EAX,[RDI - CLimbSize]
7099
7100CMP EDX,RHat.Lo
7101JA @DoAdjust
7102JB @AdjustEnd
7103CMP EAX,[RSI + CLimbSize*RBX - 2*CLimbSize]
7104JBE @AdjustEnd
7105
7106@DoAdjust:
7107
7108SUB QHat.Lo,1
7109SBB QHat.Hi,0
7110MOV EAX,[RDI]
7111ADD RHat.Lo,EAX
7112ADC RHat.Hi,0
7113JZ @CheckAdjust
7114
7115@AdjustEnd:
7116
7117MOV SaveRDI,RDI
7118MOV SaveRBX,RBX
7119MOV SaveRCX,RCX
7120
7121MOV ECX,EBX
7122SUB ECX,RSize
7123LEA RDI,[RSI + CLimbSize*RCX]
7124MOV RAX,LQuotient
7125MOV EDX,QHat.Lo
7126MOV [RAX + CLimbSize*RCX],EDX
7127XOR EBX,EBX
7128MOV Overflow,EBX
7129
7130@SubtractProduct:
7131
7132MOV RAX,NormDivisor
7133MOV EAX,[RAX + CLimbSize*RBX]
7134MUL EAX,QHat.Lo
7135MOV Product.Lo,EAX
7136MOV Product.Hi,EDX
7137XOR EDX,EDX
7138MOV EAX,[RDI + CLimbSize*RBX]
7139SUB EAX,Overflow
7140SBB EDX,0
7141SUB EAX,Product.Lo
7142SBB EDX,0
7143MOV [RDI + CLimbSize*RBX],EAX
7144MOV EAX,Product.Hi
7145SUB EAX,EDX
7146MOV Overflow,EAX
7147INC EBX
7148CMP EBX,RSize
7149JL @SubtractProduct
7150
7151@SubtractProductEnd:
7152
7153MOV RBX,SaveRBX
7154MOV EDX,[RSI + CLimbSize*RBX]
7155SUB EDX,Overflow
7156MOV [RSI + CLimbSize*RBX],EDX
7157JNC @SkipAddBack
7158
7159// Add normalized divisor back, if necessary:
7160
7161MOV RAX,LQuotient
7162DEC DWORD PTR [RAX + ClimbSize*RCX]
7163XOR EBX,EBX
7164MOV Overflow,EBX
7165
7166@AddBackLoop:
7167
7168CMP EBX,RSize
7169JE @AddBackLoopEnd
7170XOR EDX,EDX
7171MOV RAX,NormDivisor
7172MOV EAX,[RAX + CLimbSize*RBX]
7173ADD EAX,Overflow
7174ADC EDX,0
7175ADD [RDI + CLimbSize*RBX],EAX
7176ADC EDX,0
7177MOV Overflow,EDX
7178INC EBX
7179JMP @AddBackLoop
7180
7181@AddBackLoopEnd:
7182
7183ADD [RDI + CLimbSize*RBX],EDX
7184
7185@SkipAddBack:
7186
7187MOV RCX,SaveRCX
7188MOV RBX,SaveRBX
7189MOV RDI,SaveRDI
7190
7191// End of main loop; loop if required
7192
7193DEC EBX
7194CMP EBX,ECX
7195JGE @MainLoop
7196
7197// NormDividend now contains remainder, scaled by Shift.
7198// If Assigned(Remainder), then shift NormDividend down into Remainder
7199
7200MOV RAX,LRemainder
7201TEST RAX,RAX
7202JE @ExitTrue
7203XOR EBX,EBX
7204MOV RSI,NormDividend
7205MOV RDI,RAX
7206MOV ECX,Shift
7207MOV EAX,[RSI + CLimbSize*RBX]
7208
7209@RemainderLoop:
7210
7211MOV EDX,[RSI + CLimbSize*RBX + CLimbSize]
7212SHRD EAX,EDX,CL
7213MOV [RDI + CLimbSize*RBX],EAX
7214MOV EAX,EDX
7215INC EBX
7216CMP EBX,RSize
7217JL @RemainderLoop
7218SHR EDX,CL
7219MOV [RDI + CLimbSize*RBX],EDX
7220JMP @ExitTrue
7221
7222@ExitFalse:
7223
7224MOV BL,False
7225JMP @Exit
7226
7227@ExitTrue:
7228
7229MOV BL,True
7230
7231@Exit:
7232
7233// Clear dynamic arrays.
7234
7235MOV RCX,NormDividend
7236CALL System.@FreeMem
7237
7238MOV RCX,NormDivisor
7239CALL System.@FreeMem
7240
7241MOV EAX,EBX
7242
7243end;
7244{$IFEND}
7245
7246// Note: only handles Abs(Self) > 0.
7247class procedure BigInteger.InternalIncrement(Limbs: PLimb; Size: Integer);
7248{$IFDEF PUREPASCAL}
7249var
7250N: TLimb;
7251begin
7252N := MaxInt;
7253while Size > 0 do
7254begin
7255N := Limbs^;
7256Inc(N);
7257Limbs^ := N;
7258if N <> 0 then
7259Break;
7260Inc(Limbs);
7261Dec(Size);
7262end;
7263if N = 0 then
7264begin
7265Limbs^ := 1;
7266end;
7267end;
7268{$ELSE !PUREPASCAL}
7269{$IFDEF WIN32}
7270asm
7271
7272TEST EDX,EDX
7273JE @Exit
7274
7275@Loop:
7276
7277MOV ECX,[EAX]
7278INC ECX
7279MOV [EAX],ECX
7280TEST ECX,ECX
7281JNE @Exit
7282LEA EAX,[EAX + CLimbSize]
7283DEC EDX
7284JNE @Loop
7285
7286@Last:
7287
7288TEST ECX,ECX
7289JNE @Exit
7290MOV TLimb PTR [EAX],1
7291
7292@Exit:
7293
7294end;
7295{$ELSE !WIN32}
7296asm
7297
7298TEST EDX,EDX
7299JE @Exit
7300
7301@Loop:
7302
7303MOV EAX,[RCX]
7304INC EAX
7305MOV [RCX],EAX
7306TEST EAX,EAX
7307JNE @Exit
7308LEA RCX,[RCX + CLimbSize]
7309DEC EDX
7310JNE @Loop
7311
7312@Last:
7313
7314TEST EAX,EAX
7315JNE @Exit
7316MOV TLimb PTR [RCX],1
7317
7318@Exit:
7319
7320end;
7321{$ENDIF !WIN32}
7322{$ENDIF !PUREPASCAL}
7323
7324// Note: only handles Abs(Self) > 1
7325class procedure BigInteger.InternalDecrement(Limbs: PLimb; Size: Integer);
7326{$IFDEF PUREPASCAL}
7327begin
7328repeat
7329Dec(Limbs^);
7330if Limbs^ <> TLimb(-1) then
7331Break;
7332Inc(Limbs);
7333Dec(Size);
7334until Size = 0;
7335end;
7336{$ELSE !PUREPASCAL}
7337{$IFDEF WIN32}
7338asm
7339
7340@Loop:
7341
7342MOV ECX,[EAX]
7343DEC ECX
7344MOV [EAX],ECX
7345CMP ECX,-1
7346JNE @Exit
7347LEA EAX,[EAX + CLimbSize]
7348DEC EDX
7349JNE @Loop
7350
7351@Exit:
7352
7353end;
7354{$ELSE !WIN32}
7355asm
7356
7357@Loop:
7358
7359MOV EAX,[RCX]
7360DEC EAX
7361MOV [RCX],EAX
7362CMP EAX,-1
7363JNE @Exit
7364LEA RCX,[RCX + CLimbSize]
7365DEC EDX
7366JNE @Loop
7367
7368@Exit:
7369
7370end;
7371{$ENDIF !WIN32}
7372{$ENDIF !PUREPASCAL}
7373
7374// Divides a magnitude (usually the FData of a TBigInteger) by Base and returns the remainder.
7375class function BigInteger.InternalDivideByBase(Mag: PLimb; Base: Integer; var Size: Integer): UInt32;
7376{$IF DEFINED(PUREPASCAL)}
7377
7378// This routine uses DivMod(Cardinal, Word, Word, Word).
7379// In Win32, that is 14 times faster than the previous version using the DivMod with UInt64 parameters.
7380// In Win64, it is only a little bit slower.
7381
7382type
7383UInt32Rec = record
7384Lo, Hi: UInt16;
7385end;
7386PUInt16 = ^UInt16;
7387
7388var
7389P, PMag: PUInt16;
7390Remainder: UInt16;
7391CurrentWord: UInt32;
7392begin
7393Result := 0;
7394if Size = 0 then
7395Exit;
7396PMag := PUInt16(Mag);
7397P := PMag + Size * 2;
7398Remainder := 0;
7399while P > PMag do
7400begin
7401Dec(P);
7402UInt32Rec(CurrentWord).Lo := P^;
7403UInt32Rec(CurrentWord).Hi := Remainder;
7404System.Math.DivMod(CurrentWord, Base, P^, Remainder);
7405end;
7406Result := Remainder;
7407if Mag[Size - 1] = 0 then
7408Dec(Size);
7409end;
7410{$ELSEIF DEFINED(WIN32)}
7411asm
7412PUSH ESI
7413PUSH EDI
7414PUSH EBX
7415MOV EBX,ECX // var Size
7416MOV ECX,EDX
7417MOV ESI,EAX // PBase (= Mag)
7418MOV EDX,[EBX]
7419XOR EAX,EAX // Result
7420TEST EDX,EDX
7421JE @Exit
7422LEA EDI,[ESI + CLimbSize*EDX] // P
7423XOR EDX,EDX // Remainder := 0;
7424CMP EDI,ESI // while P > PBase do
7425JBE @CheckSize
7426@Loop:
7427SUB EDI,4 // Dec(P);
7428MOV EAX,[EDI] // DivMod(P^ or (Remainder shl 32), 10, P^, Remainder);
7429DIV EAX,ECX
7430MOV [EDI],EAX
7431CMP EDI,ESI // while P > PBase do
7432JA @Loop
7433@CheckSize:
7434MOV EAX,EDX // if (PBase + Size - 1)^ = 0 then
7435MOV EDX,[EBX]
7436LEA ESI,[ESI + CLimbSize*EDX - CLimbSize]
7437CMP [ESI],0
7438JNE @Exit
7439DEC DWORD PTR [EBX] // Dec(Size);
7440@Exit:
7441POP EBX
7442POP EDI
7443POP ESI
7444end;
7445{$ELSE}
7446asm
7447.NOFRAME
7448
7449MOV R11,R8 // var Size
7450MOV R9,RCX // PBase := Mag;
7451MOV ECX,EDX
7452XOR EAX,EAX // Result := 0;
7453MOV EDX,[R11] // if Size = 0 then Exit;
7454OR EDX,EDX
7455JE @Exit
7456LEA R10,[R9 + CLimbSize*RDX] // P
7457XOR EDX,EDX // Remainder := 0;
7458CMP R10,R9 // while P > PBase do
7459JBE @CheckSize
7460@Loop:
7461SUB R10,4 // Dec(P)
7462MOV EAX,[R10] // DivMod(P^ or (Remainder shl 32), 10, P^, Remainder);
7463DIV EAX,ECX
7464MOV [R10],EAX
7465CMP R10,R9 // while P > PBase do
7466JA @Loop
7467@CheckSize:
7468MOV EAX,EDX
7469MOV EDX,[R11]
7470CMP [R9 + CLimbSize*RDX - CLimbSize],0 // if (PBase + Size - 1)^ = 0 then
7471JNE @Exit
7472DEC DWORD PTR [R11] // Dec(Size);
7473@Exit:
7474end;
7475{$IFEND}
7476
7477class operator BigInteger.Equal(const Left, Right: BigInteger): Boolean;
7478begin
7479Result := Compare(Left, Right) = 0;
7480end;
7481
7482class procedure BigInteger.Error(ErrorCode: TErrorCode; const ErrorInfo: array of const);
7483begin
7484case ErrorCode of
7485ecParse:
7486raise EConvertError.CreateFmt(SErrorParsingFmt, ErrorInfo);
7487ecDivbyZero:
7488raise EZeroDivide.Create(SDivisionByZero);
7489ecConversion:
7490raise EConvertError.CreateFmt(SConversionFailedFmt, ErrorInfo);
7491ecOverflow:
7492raise EOverflow.Create(SOverflow);
7493ecInvalidArgFloat:
7494raise EInvalidArgument.CreateFmt(SInvalidArgumentFloatFmt, ErrorInfo);
7495ecInvalidBase:
7496raise EInvalidArgument.Create(SInvalidArgumentBase);
7497ecInvalidArg:
7498raise EInvalidArgument.CreateFmt(SInvalidArgumentFmt, ErrorInfo);
7499ecNoInverse:
7500raise EInvalidArgument.Create(SNoInverse);
7501ecNegativeExponent:
7502raise EInvalidArgument.CreateFmt(SNegativeExponent, ErrorInfo);
7503ecNegativeRadicand:
7504raise EInvalidArgument.CreateFmt(SNegativeRadicand, ErrorInfo);
7505else
7506raise EInvalidOp.Create(SInvalidOperation);
7507end;
7508end;
7509
7510class operator BigInteger.Explicit(const Value: BigInteger): Int32;
7511begin
7512if Value.FData = nil then
7513Result := 0
7514else
7515begin
7516Result := Int32(Value.FData[0]);
7517if Value.FSize < 0 then
7518Result := -Result;
7519end;
7520end;
7521
7522class operator BigInteger.Explicit(const Value: BigInteger): UInt32;
7523begin
7524if Value.FData = nil then
7525Result := 0
7526else
7527Result := Value.FData[0];
7528if Value.FSize < 0 then
7529Result := UInt32(-Int32(Result));
7530end;
7531
7532class operator BigInteger.Explicit(const Value: BigInteger): Int64;
7533begin
7534if Value.FData = nil then
7535Result := 0
7536else
7537begin
7538TUInt64(Result).Lo := Value.FData[0];
7539if (Value.FSize and SizeMask) > 1 then
7540TUInt64(Result).Hi := Value.FData[1]
7541else
7542TUInt64(Result).Hi := 0;
7543if Value.FSize < 0 then
7544Result := -Result;
7545end;
7546end;
7547
7548class operator BigInteger.Explicit(const Value: BigInteger): UInt64;
7549begin
7550if Value.FData = nil then
7551Result := 0
7552else
7553begin
7554TUInt64(Result).Lo := Value.FData[0];
7555if (Value.FSize and SizeMask) > 1 then
7556TUInt64(Result).Hi := Value.FData[1]
7557else
7558TUInt64(Result).Hi := 0;
7559end;
7560if Value.FSize < 0 then
7561Result := UInt64(-Int64(Result));
7562end;
7563
7564function BigInteger.AsCardinal: Cardinal;
7565begin
7566Result := 0;
7567if not IsNegative and (BitLength <= CCardinalBits) then
7568Result := Cardinal(Self)
7569else
7570Error(ecConversion, ['BigInteger', 'Cardinal']);
7571end;
7572
7573function GetBitAt(FData: PLimb; BitNum: Integer): Boolean;
7574begin
7575Result := (FData[BitNum div 32] and (1 shl (BitNum and 31))) <> 0
7576end;
7577
7578class procedure BigInteger.ConvertToFloatComponents(const Value: BigInteger; SignificandSize: Integer;
7579var Sign: Integer; var Significand: UInt64; var Exponent: Integer);
7580var
7581LRemainder, LLowBit, LSignificand: BigInteger;
7582LBitLen: Integer;
7583begin
7584if Value.IsNegative then
7585Sign := -1
7586else
7587Sign := 1;
7588
7589Exponent := 0;
7590LSignificand := BigInteger.Abs(Value);
7591
7592LBitLen := LSignificand.BitLength;
7593if LBitLen > SignificandSize then
7594begin
7595// --- Shift down and adjust exponent.
7596
7597// Get lowest bit.
7598LLowBit := BigInteger.One shl (LBitLen - SignificandSize);
7599
7600// Mask out bits being shifted out and save them for later.
7601LRemainder := (LSignificand and (LLowBit - BigInteger.One)) shl 1;
7602
7603// Shift significand until it fits in SignificandSize (in bits).
7604LSignificand := LSignificand shr (LBitLen - SignificandSize);
7605Inc(Exponent, LBitLen - 1);
7606
7607// --- Round
7608if (LRemainder > LLowBit) or ((LRemainder = LLowBit) and not LSignificand.IsEven) then
7609begin
7610LSignificand := LSignificand + BigInteger.One;
7611if LSignificand.BitLength > SignificandSize then
7612begin
7613LSignificand := LSignificand shr 1;
7614Inc(Exponent);
7615end;
7616end;
7617end
7618else
7619begin
7620LSignificand := LSignificand shl (SignificandSize - LBitLen);
7621Inc(Exponent, LBitLen - 1);
7622end;
7623Significand := LSignificand.AsUInt64;
7624end;
7625
7626const
7627// Number of bits in full significand (including hidden bit, if any)
7628// of IEEE-754 floating point types.
7629KSingleSignificandBits = 24;
7630KDoubleSignificandBits = 53;
7631KExtendedSignificandBits = 64;
7632
7633// Maximum possible exponents for IEEE-754 floating point types.
7634KSingleMaxExponent = 127;
7635KDoubleMaxExponent = 1023;
7636KExtendedMaxExponent = 16383;
7637
7638function BigInteger.AsSingle: Single;
7639var
7640LSign, LExponent: Integer;
7641LMantissa: UInt64;
7642begin
7643if Self.IsZero then
7644Exit(0.0);
7645
7646ConvertToFloatComponents(Self, KSingleSignificandBits, LSign, LMantissa, LExponent);
7647
7648// Handle overflow.
7649if LExponent > KSingleMaxExponent then
7650if LSign < 0 then
7651Result := NegInfinity
7652else
7653Result := Infinity
7654// No need to check for denormals.
7655else
7656Result := Velthuis.FloatUtils.MakeSingle(LSign, LMantissa, LExponent);
7657end;
7658
7659function BigInteger.AsDouble: Double;
7660var
7661LSign, LExponent: Integer;
7662LMantissa: UInt64;
7663begin
7664if Self.IsZero then
7665Exit(0.0);
7666
7667ConvertToFloatComponents(Self, KDoubleSignificandBits, LSign, LMantissa, LExponent);
7668
7669// Handle overflow.
7670if LExponent > KDoubleMaxExponent then
7671if LSign < 0 then
7672Result := NegInfinity
7673else
7674Result := Infinity
7675// No need to check for denormals.
7676else
7677Result := Velthuis.FloatUtils.MakeDouble(LSign, LMantissa, LExponent);
7678end;
7679
7680{$IFDEF HasExtended}
7681function BigInteger.AsExtended: Extended;
7682var
7683LSign, LExponent: Integer;
7684LMantissa: UInt64;
7685begin
7686if Self.IsZero then
7687Exit(0.0);
7688
7689ConvertToFloatComponents(Self, KExtendedSignificandBits, LSign, LMantissa, LExponent);
7690
7691// Handle overflow.
7692if LExponent > KExtendedMaxExponent then
7693if LSign < 0 then
7694Result := NegInfinity
7695else
7696Result := Infinity
7697// No need to check for denormals.
7698else
7699Result := Velthuis.FloatUtils.MakeExtended(LSign, LMantissa, LExponent);
7700end;
7701{$ENDIF}
7702
7703function BigInteger.AsInt64: Int64;
7704begin
7705Result := 0;
7706if BitLength <= CInt64Bits then
7707Result := Int64(Self)
7708else
7709Error(ecConversion, ['BigInteger', 'Int64']);
7710end;
7711
7712function BigInteger.AsInteger: Integer;
7713begin
7714Result := 0;
7715if BitLength <= CIntegerBits then
7716Result := Integer(Self)
7717else
7718Error(ecConversion, ['BigInteger', 'Integer']);
7719end;
7720
7721function BigInteger.AsUInt64: UInt64;
7722begin
7723Result := 0;
7724if not IsNegative and (BitLength <= CUInt64Bits) then
7725Result := UInt64(Self)
7726else
7727Error(ecConversion, ['BigInteger', 'UInt64']);
7728end;
7729
7730class function BigInteger.InternalCompare(Left, Right: PLimb; LSize, RSize: Integer): Integer;
7731{$IFDEF PUREPASCAL}
7732var
7733L, R: PLimb;
7734begin
7735if (LSize or RSize) = 0 then
7736Exit(0);
7737if LSize > RSize then
7738Result := 1
7739else if LSize < RSize then
7740Result := -1
7741else
7742
7743// Same size, so compare limbs. Start at the "top" (most significant limb).
7744begin
7745L := Left + LSize - 1;
7746R := Right + LSize - 1;
7747while L >= Left do
7748begin
7749if L^ <> R^ then
7750begin
7751if L^ > R^ then
7752Exit(1)
7753else if L^ < R^ then
7754Exit(-1);
7755end;
7756Dec(L);
7757Dec(R);
7758end;
7759Exit(0);
7760end;
7761end;
7762{$ELSE !PUREPASCAL}
7763{$IFDEF WIN32}
7764asm
7765PUSH ESI
7766
7767TEST EAX,EAX
7768JNE @LeftNotNil
7769TEST EDX,EDX
7770JZ @ExitZero
7771JMP @ExitNeg
7772
7773@LeftNotNil:
7774
7775TEST EDX,EDX
7776JZ @ExitPos
7777
7778CMP ECX,RSize
7779JA @ExitPos
7780JB @ExitNeg
7781
7782MOV ESI,EAX
7783
7784@Loop:
7785
7786MOV EAX,[ESI + ECX*CLimbSize - CLimbSize]
7787CMP EAX,[EDX + ECX*CLimbSize - CLimbSize]
7788JA @ExitPos
7789JB @ExitNeg
7790DEC ECX
7791JNE @Loop
7792
7793@ExitZero:
7794
7795XOR EAX,EAX
7796JMP @Exit
7797
7798@ExitPos:
7799
7800MOV EAX,1
7801JMP @Exit
7802
7803@ExitNeg:
7804
7805MOV EAX,-1
7806
7807@Exit:
7808
7809POP ESI
7810end;
7811{$ELSE WIN64}
7812asm
7813TEST RCX,RCX
7814JNZ @LeftNotNil
7815
7816// Left is nil
7817TEST RDX,RDX
7818JZ @ExitZero // if Right nil too, then equal
7819JMP @ExitNeg // Otherwise, Left < Right
7820
7821@LeftNotNil:
7822
7823TEST RDX,RDX
7824JZ @ExitPos
7825
7826CMP R8D,R9D
7827JA @ExitPos
7828JB @ExitNeg
7829
7830// R8D and R9D are same.
7831
7832LEA RCX,[RCX + R8*CLimbSize]
7833LEA RDX,[RDX + R8*CLimbSize]
7834
7835TEST R8D,1
7836JZ @NotOdd
7837
7838LEA RCX,[RCX - CLimbSize]
7839LEA RDX,[RDX - CLimbSize]
7840MOV EAX,[RCX]
7841CMP EAX,[RDX]
7842JA @ExitPos
7843JB @ExitNeg
7844DEC R8D
7845
7846@NotOdd:
7847
7848SHR R8D,1
7849JZ @ExitZero
7850
7851@Loop:
7852
7853LEA RCX,[RCX - DLimbSize]
7854LEA RDX,[RDX - DLimbSize]
7855MOV RAX,[RCX]
7856CMP RAX,[RDX]
7857JA @ExitPos
7858JB @ExitNeg
7859DEC R8D
7860JNE @Loop
7861
7862@ExitZero:
7863
7864XOR EAX,EAX
7865JMP @Exit
7866
7867@ExitPos:
7868
7869MOV EAX,1
7870JMP @Exit
7871
7872@ExitNeg:
7873
7874MOV EAX,-1
7875
7876@Exit:
7877
7878end;
7879{$ENDIF WIN64}
7880{$ENDIF !PUREPASCAL}
7881
7882{$IFNDEF PUREPASCAL}
7883class procedure BigInteger.InternalSubtractModified(Larger, Smaller, Result: PLimb; LSize, SSize: Integer);
7884{$IFDEF WIN32}
7885asm
7886PUSH ESI
7887PUSH EDI
7888PUSH EBX
7889
7890MOV ESI,EAX // Left
7891MOV EDI,EDX // Right
7892MOV EBX,ECX // Result
7893
7894MOV ECX,SSize
7895MOV EDX,LSize
7896
7897SUB EDX,ECX
7898PUSH EDX
7899XOR EDX,EDX
7900
7901XOR EAX,EAX
7902
7903MOV EDX,ECX
7904AND EDX,CUnrollMask
7905SHR ECX,CUnrollShift
7906
7907CLC
7908JE @MainTail
7909
7910@MainLoop:
7911
7912MOV EAX,[ESI]
7913SBB EAX,[EDI]
7914MOV [EBX],EAX
7915
7916MOV EAX,[ESI + CLimbSize]
7917SBB EAX,[EDI + CLimbSize]
7918MOV [EBX + CLimbSize],EAX
7919
7920MOV EAX,[ESI + 2*CLimbSize]
7921SBB EAX,[EDI + 2*CLimbSize]
7922MOV [EBX + 2*CLimbSize],EAX
7923
7924MOV EAX,[ESI + 3*CLimbSize]
7925SBB EAX,[EDI + 3*CLimbSize]
7926MOV [EBX + 3*CLimbSize],EAX
7927
7928LEA ESI,[ESI + 4*CLimbSize]
7929LEA EDI,[EDI + 4*CLimbSize]
7930LEA EBX,[EBX + 4*CLimbSize]
7931
7932LEA ECX,[ECX - 1]
7933JECXZ @MainTail
7934JMP @Mainloop
7935
7936@MainTail:
7937
7938LEA ESI,[ESI + EDX*CLimbSize]
7939LEA EDI,[EDI + EDX*CLimbSize]
7940LEA EBX,[EBX + EDX*CLimbSize]
7941
7942LEA ECX,[@JumpsMain]
7943JMP [ECX + EDX*TYPE Pointer]
7944
7945.ALIGN 16
7946
7947@JumpsMain:
7948
7949DD @DoRestLoop
7950DD @Main1
7951DD @Main2
7952DD @Main3
7953
7954@Main3:
7955
7956MOV EAX,[ESI - 3*CLimbSize]
7957SBB EAX,[EDI - 3*CLimbSize]
7958MOV [EBX - 3*CLimbSize],EAX
7959
7960@Main2:
7961
7962MOV EAX,[ESI - 2*CLimbSize]
7963SBB EAX,[EDI - 2*CLimbSize]
7964MOV [EBX - 2*CLimbSize],EAX
7965
7966@Main1:
7967
7968MOV EAX,[ESI - CLimbSize]
7969SBB EAX,[EDI - CLimbSize]
7970MOV [EBX - CLimbSize],EAX
7971
7972@DoRestLoop:
7973
7974SETC AL // Save Carry Flag
7975
7976XOR EDI,EDI
7977
7978POP ECX
7979MOV EDX,ECX
7980AND EDX,CUnrollMask
7981SHR ECX,CUnrollShift
7982
7983ADD AL,255 // Restore Carry Flag.
7984
7985JECXZ @RestLast3
7986
7987@RestLoop:
7988
7989MOV EAX,[ESI]
7990SBB EAX,EDI
7991MOV [EBX],EAX
7992
7993MOV EAX,[ESI + CLimbSize]
7994SBB EAX,EDI
7995MOV [EBX + CLimbSize],EAX
7996
7997MOV EAX,[ESI + 2*CLimbSize]
7998SBB EAX,EDI
7999MOV [EBX + 2*CLimbSize],EAX
8000
8001MOV EAX,[ESI + 3*CLimbSize]
8002SBB EAX,EDI
8003MOV [EBX + 3*CLimbSize],EAX
8004
8005LEA ESI,[ESI + 4*CLimbSize]
8006LEA EBX,[EBX + 4*CLimbSize]
8007
8008LEA ECX,[ECX - 1]
8009JECXZ @RestLast3
8010JMP @RestLoop
8011
8012@RestLast3:
8013
8014LEA ESI,[ESI + EDX*CLimbSize]
8015LEA EBX,[EBX + EDX*CLimbSize]
8016
8017LEA ECX,[@RestJumps]
8018JMP [ECX + EDX*TYPE Pointer]
8019
8020.ALIGN 16
8021
8022@RestJumps:
8023
8024DD @Exit
8025DD @Rest1
8026DD @Rest2
8027DD @Rest3
8028
8029@Rest3:
8030
8031MOV EAX,[ESI - 3*CLimbSize]
8032SBB EAX,EDI
8033MOV [EBX - 3*CLimbSize],EAX
8034
8035@Rest2:
8036
8037MOV EAX,[ESI - 2*CLimbSize]
8038SBB EAX,EDI
8039MOV [EBX - 2*CLimbSize],EAX
8040
8041@Rest1:
8042
8043MOV EAX,[ESI - CLimbSize]
8044SBB EAX,EDI
8045MOV [EBX - CLimbSize],EAX
8046
8047@Exit:
8048
8049POP EBX
8050POP EDI
8051POP ESI
8052end;
8053{$ELSE WIN32/WIN64}
8054asm
8055MOV R10,RCX
8056MOV ECX,SSize
8057
8058// R10 = Left, RDX = Right, R8 = Result, R9D = LSize, ECX = SSize
8059
8060CMP R9D,ECX
8061JAE @SkipSwap
8062XCHG ECX,R9D
8063XCHG R10,RDX
8064
8065@SkipSwap:
8066
8067SUB R9D,ECX
8068PUSH R9
8069
8070MOV R9D,ECX
8071AND R9D,CUnrollMask
8072SHR ECX,CUnrollShift
8073
8074CLC
8075JE @MainTail
8076
8077@MainLoop:
8078
8079MOV RAX,[R10]
8080SBB RAX,[RDX]
8081MOV [R8],RAX
8082
8083MOV RAX,[R10 + DLimbSize]
8084SBB RAX,[RDX + DLimbSize]
8085MOV [R8 + DLimbSize],RAX
8086
8087LEA R10,[R10 + 2*DLimbSize]
8088LEA RDX,[RDX + 2*DLimbSize]
8089LEA R8,[R8 + 2*DLimbSize]
8090
8091LEA RCX,[RCX - 1]
8092JRCXZ @MainTail
8093JMP @MainLoop
8094
8095@MainTail:
8096
8097// Here, code does not add index*CLimbSize and then use negative offsets, because that would take away
8098// the advantage of using 64 bit registers.
8099// Each block is separate, no fall through.
8100
8101LEA RCX,[@MainJumps]
8102JMP [RCX + R9*TYPE Pointer]
8103
8104// Align jump table. Update if necessary!
8105
8106DB $90,$90,$90,$90,$90
8107
8108@MainJumps:
8109
8110DQ @DoRestLoop
8111DQ @Main1
8112DQ @Main2
8113DQ @Main3
8114
8115@Main3:
8116
8117MOV RAX,[R10]
8118SBB RAX,[RDX]
8119MOV [R8],RAX
8120
8121MOV EAX,[R10 + 2*CLimbSize]
8122SBB EAX,[RDX + 2*CLimbSize]
8123MOV [R8 + 2*CLimbSize],EAX
8124
8125LEA R10,[R10 + 3*CLimbSize]
8126LEA RDX,[RDX + 3*CLimbSize]
8127LEA R8,[R8 + 3*CLimbSize]
8128
8129JMP @DoRestLoop
8130
8131@Main2:
8132
8133MOV RAX,[R10]
8134SBB RAX,[RDX]
8135MOV [R8],RAX
8136
8137LEA R10,[R10 + 2*CLimbSize]
8138LEA RDX,[RDX + 2*CLimbSize]
8139LEA R8,[R8 + 2*CLimbSize]
8140
8141JMP @DoRestLoop
8142
8143@Main1:
8144
8145MOV EAX,[R10]
8146SBB EAX,[RDX]
8147MOV [R8],EAX
8148
8149LEA R10,[R10 + CLimbSize]
8150LEA RDX,[RDX + CLimbSize]
8151LEA R8,[R8 + CLimbSize]
8152
8153@DoRestLoop:
8154
8155SETC AL // Save Carry Flag
8156
8157XOR EDX,EDX
8158
8159POP RCX
8160MOV R9D,ECX
8161AND R9D,CUnrollMask
8162SHR ECX,CUnrollShift
8163
8164ADD AL,255 // Restore Carry Flag.
8165
8166JECXZ @RestLast3
8167
8168@RestLoop:
8169
8170MOV RAX,[R10]
8171SBB RAX,RDX
8172MOV [R8],RAX
8173
8174MOV RAX,[R10 + DLimbSize]
8175SBB RAX,RDX
8176MOV [R8 + DLimbSize],RAX
8177
8178LEA R10,[R10 + 2*DLimbSize]
8179LEA R8,[R8 + 2*DLimbSize]
8180
8181LEA RCX,[RCX - 1]
8182JRCXZ @RestLast3
8183JMP @RestLoop
8184
8185@RestLast3:
8186
8187LEA RCX,[@RestJumps]
8188JMP [RCX + R9*TYPE Pointer]
8189
8190// If necessary, align second jump table with NOPs
8191
8192DB $90,$90,$90,$90,$90,$90,$90
8193
8194@RestJumps:
8195
8196DQ @Exit
8197DQ @Rest1
8198DQ @Rest2
8199DQ @Rest3
8200
8201@Rest3:
8202
8203MOV RAX,[R10]
8204SBB RAX,RDX
8205MOV [R8],RAX
8206
8207MOV EAX,[R10 + DLimbSize]
8208SBB EAX,EDX
8209MOV [R8 + DLimbSize],EAX
8210
8211JMP @Exit
8212
8213@Rest2:
8214
8215MOV RAX,[R10]
8216SBB RAX,RDX
8217MOV [R8],RAX
8218
8219JMP @Exit
8220
8221@Rest1:
8222
8223MOV EAX,[R10]
8224SBB EAX,EDX
8225MOV [R8],EAX
8226
8227@Exit:
8228
8229end;
8230{$ENDIF}
8231
8232class procedure BigInteger.InternalSubtractPlain(Larger, Smaller, Result: PLimb; LSize, SSize: Integer);
8233{$IFDEF WIN32}
8234asm
8235PUSH ESI
8236PUSH EDI
8237PUSH EBX
8238
8239MOV ESI,EAX // Left
8240MOV EDI,EDX // Right
8241MOV EBX,ECX // Result
8242
8243MOV ECX,SSize
8244MOV EDX,LSize
8245
8246SUB EDX,ECX
8247PUSH EDX
8248XOR EDX,EDX
8249
8250XOR EAX,EAX
8251
8252MOV EDX,ECX
8253AND EDX,CUnrollMask
8254SHR ECX,CUnrollShift
8255
8256CLC
8257JE @MainTail
8258
8259@MainLoop:
8260
8261// Unrolled 4 times. More times will not improve speed anymore.
8262
8263MOV EAX,[ESI]
8264SBB EAX,[EDI]
8265MOV [EBX],EAX
8266
8267MOV EAX,[ESI + CLimbSize]
8268SBB EAX,[EDI + CLimbSize]
8269MOV [EBX + CLimbSize],EAX
8270
8271MOV EAX,[ESI + 2*CLimbSize]
8272SBB EAX,[EDI + 2*CLimbSize]
8273MOV [EBX + 2*CLimbSize],EAX
8274
8275MOV EAX,[ESI + 3*CLimbSize]
8276SBB EAX,[EDI + 3*CLimbSize]
8277MOV [EBX + 3*CLimbSize],EAX
8278
8279// Update pointers.
8280
8281LEA ESI,[ESI + 4*CLimbSize]
8282LEA EDI,[EDI + 4*CLimbSize]
8283LEA EBX,[EBX + 4*CLimbSize]
8284
8285// Update counter and loop if required.
8286
8287DEC ECX // Note: if INC/DEC must be emulated:
8288JNE @MainLoop // LEA ECX,[ECX - 1]; JECXZ @MainTail; JMP @MainLoop
8289
8290@MainTail:
8291
8292// Add index*CLimbSize so @MainX branches can fall through.
8293
8294LEA ESI,[ESI + EDX*CLimbSize]
8295LEA EDI,[EDI + EDX*CLimbSize]
8296LEA EBX,[EBX + EDX*CLimbSize]
8297
8298// Indexed jump.
8299
8300LEA ECX,[@JumpsMain]
8301JMP [ECX + EDX*TYPE Pointer]
8302
8303.ALIGN 16
8304
8305
8306@JumpsMain:
8307
8308DD @DoRestLoop
8309DD @Main1
8310DD @Main2
8311DD @Main3
8312
8313@Main3:
8314
8315MOV EAX,[ESI - 3*CLimbSize] // negative offset, because index*CLimbSize was already added.
8316SBB EAX,[EDI - 3*CLimbSize]
8317MOV [EBX - 3*CLimbSize],EAX
8318
8319@Main2:
8320
8321MOV EAX,[ESI - 2*CLimbSize]
8322SBB EAX,[EDI - 2*CLimbSize]
8323MOV [EBX - 2*CLimbSize],EAX
8324
8325@Main1:
8326
8327MOV EAX,[ESI - CLimbSize]
8328SBB EAX,[EDI - CLimbSize]
8329MOV [EBX - CLimbSize],EAX
8330
8331@DoRestLoop:
8332
8333SETC AL // Save Carry Flag
8334
8335XOR EDI,EDI
8336
8337POP ECX
8338MOV EDX,ECX
8339AND EDX,CUnrollMask
8340SHR ECX,CUnrollShift
8341
8342ADD AL,255 // Restore Carry Flag.
8343
8344INC ECX
8345DEC ECX
8346JE @RestLast3 // JECXZ is slower than INC/DEC/JE
8347
8348@RestLoop:
8349
8350MOV EAX,[ESI]
8351SBB EAX,EDI
8352MOV [EBX],EAX
8353
8354MOV EAX,[ESI + CLimbSize]
8355SBB EAX,EDI
8356MOV [EBX + CLimbSize],EAX
8357
8358MOV EAX,[ESI + 2*CLimbSize]
8359SBB EAX,EDI
8360MOV [EBX + 2*CLimbSize],EAX
8361
8362MOV EAX,[ESI + 3*CLimbSize]
8363SBB EAX,EDI
8364MOV [EBX + 3*CLimbSize],EAX
8365
8366LEA ESI,[ESI + 4*CLimbSize] // LEA does not affect the flags, so carry will not be changed.
8367LEA EBX,[EBX + 4*CLimbSize]
8368
8369DEC ECX // DEC does not affect carry flag, but causes partial-flags stall
8370JNE @RestLoop // (e.g. when using SBB) on older CPUs.
8371
8372@RestLast3:
8373
8374LEA ESI,[ESI + EDX*CLimbSize]
8375LEA EBX,[EBX + EDX*CLimbSize]
8376
8377LEA ECX,[@RestJumps]
8378JMP [ECX + EDX*TYPE Pointer]
8379
8380.ALIGN 16
8381
8382@RestJumps:
8383
8384DD @Exit
8385DD @Rest1
8386DD @Rest2
8387DD @Rest3
8388
8389@Rest3:
8390
8391MOV EAX,[ESI - 3*CLimbSize]
8392SBB EAX,EDI
8393MOV [EBX - 3*CLimbSize],EAX
8394
8395@Rest2:
8396
8397MOV EAX,[ESI - 2*CLimbSize]
8398SBB EAX,EDI
8399MOV [EBX - 2*CLimbSize],EAX
8400
8401@Rest1:
8402
8403MOV EAX,[ESI - CLimbSize]
8404SBB EAX,EDI
8405MOV [EBX - CLimbSize],EAX
8406
8407@Exit:
8408
8409POP EBX
8410POP EDI
8411POP ESI
8412end;
8413{$ELSE WIN32/WIN64}
8414asm
8415MOV R10,RCX // in emulating code, ECX must be used as loop counter! So do not exchange
8416MOV ECX,SSize // RCX and R10 in the editor.
8417
8418// R10 = Left, RDX = Right, R8 = Result, R9D = LSize, ECX = SSize
8419
8420CMP R9D,ECX
8421JAE @SkipSwap
8422XCHG ECX,R9D
8423XCHG R10,RDX
8424
8425@SkipSwap:
8426
8427SUB R9D,ECX
8428PUSH R9
8429
8430MOV R9D,ECX
8431AND R9D,CUnrollMask
8432SHR ECX,CUnrollShift
8433
8434CLC
8435JE @MainTail // ECX = 0, so fewer than 3 limbs to be processed in main
8436
8437@MainLoop:
8438
8439MOV RAX,[R10] // Add two limbs at once, taking advantage of 64 bit registers.
8440SBB RAX,[RDX]
8441MOV [R8],RAX
8442
8443MOV RAX,[R10 + DLimbSize] // And next two limbs too.
8444SBB RAX,[RDX + DLimbSize]
8445MOV [R8 + DLimbSize],RAX
8446
8447LEA R10,[R10 + 2*DLimbSize]
8448LEA RDX,[RDX + 2*DLimbSize]
8449LEA R8,[R8 + 2*DLimbSize]
8450
8451DEC ECX // if INC/DEC must be emulated:
8452// LEA ECX,[ECX - 1]; JECXZ @MainTail; JMP @MainLoop
8453JNE @MainLoop
8454
8455@MainTail:
8456
8457// Here, code does not add index*CLimbSize and then use negative offsets, because that would take away the
8458// advantage of using 64 bit registers.
8459// Each block is separate, no fall through.
8460
8461LEA RCX,[@MainJumps]
8462JMP [RCX + R9*TYPE Pointer]
8463
8464.ALIGN 16
8465
8466@MainJumps:
8467
8468DQ @DoRestLoop
8469DQ @Main1
8470DQ @Main2
8471DQ @Main3
8472
8473@Main3:
8474
8475MOV RAX,[R10]
8476SBB RAX,[RDX]
8477MOV [R8],RAX
8478
8479MOV EAX,[R10 + DLimbSize]
8480SBB EAX,[RDX + DLimbSize]
8481MOV [R8 + 2*CLimbSize],EAX
8482
8483LEA R10,[R10 + 3*CLimbSize]
8484LEA RDX,[RDX + 3*CLimbSize]
8485LEA R8,[R8 + 3*CLimbSize]
8486
8487JMP @DoRestLoop
8488
8489@Main2:
8490
8491MOV RAX,[R10]
8492SBB RAX,[RDX]
8493MOV [R8],RAX
8494
8495LEA R10,[R10 + DLimbSize]
8496LEA RDX,[RDX + DLimbSize]
8497LEA R8,[R8 + DLimbSize]
8498
8499JMP @DoRestLoop
8500
8501@Main1:
8502
8503MOV EAX,[R10]
8504SBB EAX,[RDX]
8505MOV [R8],EAX
8506
8507LEA R10,[R10 + CLimbSize]
8508LEA RDX,[RDX + CLimbSize]
8509LEA R8,[R8 + CLimbSize]
8510
8511@DoRestLoop:
8512
8513SETC AL // Save Carry Flag
8514
8515XOR EDX,EDX
8516
8517POP RCX
8518MOV R9D,ECX
8519AND R9D,CUnrollMask
8520SHR ECX,CUnrollShift
8521
8522ADD AL,255 // Restore Carry Flag.
8523
8524INC ECX
8525DEC ECX
8526JE @RestLast3 // JECXZ is slower than INC/DEC/JE
8527
8528@RestLoop:
8529
8530MOV RAX,[R10] // Do two limbs at once.
8531SBB RAX,RDX
8532MOV [R8],RAX
8533
8534MOV RAX,[R10 + DLimbSize] // And the next two limbs.
8535SBB RAX,RDX
8536MOV [R8 + DLimbSize],RAX
8537
8538LEA R10,[R10 + 2*DLimbSize]
8539LEA R8,[R8 + 2*DLimbSize]
8540
8541DEC ECX
8542JNE @RestLoop
8543
8544@RestLast3:
8545
8546LEA RCX,[@RestJumps]
8547JMP [RCX + R9*TYPE Pointer]
8548
8549// If necessary, align second jump table with NOPs
8550
8551@RestJumps:
8552
8553DQ @Exit
8554DQ @Rest1
8555DQ @Rest2
8556DQ @Rest3
8557
8558@Rest3:
8559
8560MOV RAX,[R10]
8561SBB RAX,RDX
8562MOV [R8],RAX
8563
8564MOV EAX,[R10 + 2*CLimbSize]
8565SBB EAX,EDX
8566MOV [R8 + 2*CLimbSize],EAX
8567
8568LEA R8,[R8 + 3*CLimbSize]
8569
8570JMP @Exit
8571
8572@Rest2:
8573
8574MOV RAX,[R10]
8575SBB RAX,RDX
8576MOV [R8],RAX
8577
8578LEA R8,[R8 + 2*CLimbSize]
8579
8580JMP @Exit
8581
8582@Rest1:
8583
8584MOV EAX,[R10]
8585SBB EAX,EDX
8586MOV [R8],EAX
8587
8588LEA R8,[R8 + CLimbSize]
8589
8590@Exit:
8591
8592end;
8593{$ENDIF !WIN32}
8594{$ENDIF !PUREPASCAL}
8595
8596{$IFDEF PUREPASCAL}
8597class procedure BigInteger.InternalSubtractPurePascal(Larger, Smaller, Result: PLimb; LSize, SSize: Integer);
8598{$IFDEF CPU64BITS}
8599var
8600LDiff: NativeInt;
8601LTail, LCount: Integer;
8602begin
8603Dec(LSize, SSize);
8604
8605LTail := SSize and CUnrollMask;
8606LCount := SSize shr CUnrollShift;
8607LDiff := 0;
8608
8609while LCount > 0 do
8610begin
8611LDiff := Int64(Larger[0]) - Smaller[0] + Int32(LDiff shr 32);
8612Result[0] := TLimb(LDiff);
8613
8614LDiff := Int64(Larger[1]) - Smaller[1] + Int32(LDiff shr 32);
8615Result[1] := TLimb(LDiff);
8616
8617LDiff := Int64(Larger[2]) - Smaller[2] + Int32(LDiff shr 32);
8618Result[2] := TLimb(LDiff);
8619
8620LDiff := Int64(Larger[3]) - Smaller[3] + Int32(LDiff shr 32);
8621Result[3] := TLimb(LDiff);
8622
8623Inc(Larger, CUnrollIncrement);
8624Inc(Smaller, CUnrollIncrement);
8625Inc(Result, CUnrollIncrement);
8626Dec(LCount);
8627end;
8628
8629while LTail > 0 do
8630begin
8631LDiff := Int64(Larger[0]) - Smaller[0] + Int32(LDiff shr 32);
8632Result[0] := TLimb(LDiff);
8633
8634Inc(Larger);
8635Inc(Smaller);
8636Inc(Result);
8637Dec(LTail);
8638end;
8639
8640LTail := LSize and CUnrollMask;
8641LCount := LSize shr CUnrollShift;
8642
8643while LCount > 0 do
8644begin
8645LDiff := Int64(Larger[0]) + Int32(LDiff shr 32);
8646Result[0] := TLimb(LDiff);
8647
8648LDiff := Int64(Larger[1]) + Int32(LDiff shr 32);
8649Result[1] := TLimb(LDiff);
8650
8651LDiff := Int64(Larger[2]) + Int32(LDiff shr 32);
8652Result[2] := TLimb(LDiff);
8653
8654LDiff := Int64(Larger[3]) + Int32(LDiff shr 32);
8655Result[3] := TLimb(LDiff);
8656
8657Inc(Larger, CUnrollIncrement);
8658Inc(Result, CUnrollIncrement);
8659Dec(LCount);
8660end;
8661
8662while LTail > 0 do
8663begin
8664LDiff := Int64(Larger[0]) + Int32(LDiff shr 32);
8665Result[0] := TLimb(LDiff);
8666
8667Inc(Larger);
8668Inc(Result);
8669Dec(LTail);
8670end;
8671end;
8672{$ELSE}
8673var
8674LDiff: NativeInt;
8675LCount, LTail: Integer;
8676begin
8677Dec(LSize, SSize);
8678LDiff := 0;
8679
8680LTail := SSize and CUnrollMask;
8681LCount := SSize shr CUnrollShift;
8682
8683while LCount > 0 do
8684begin
8685LDiff := Int32(PUInt16(Larger)[0]) - PUInt16(Smaller)[0] + Int16(LDiff shr 16);
8686PUInt16(Result)[0] := UInt16(LDiff);
8687
8688LDiff := Int32(PUInt16(Larger)[1]) - PUInt16(Smaller)[1] + Int16(LDiff shr 16);
8689PUInt16(Result)[1] := UInt16(LDiff);
8690
8691LDiff := Int32(PUInt16(Larger)[2]) - PUInt16(Smaller)[2] + Int16(LDiff shr 16);
8692PUInt16(Result)[2] := UInt16(LDiff);
8693
8694LDiff := Int32(PUInt16(Larger)[3]) - PUInt16(Smaller)[3] + Int16(LDiff shr 16);
8695PUInt16(Result)[3] := UInt16(LDiff);
8696
8697LDiff := Int32(PUInt16(Larger)[4]) - PUInt16(Smaller)[4] + Int16(LDiff shr 16);
8698PUInt16(Result)[4] := UInt16(LDiff);
8699
8700LDiff := Int32(PUInt16(Larger)[5]) - PUInt16(Smaller)[5] + Int16(LDiff shr 16);
8701PUInt16(Result)[5] := UInt16(LDiff);
8702
8703LDiff := Int32(PUInt16(Larger)[6]) - PUInt16(Smaller)[6] + Int16(LDiff shr 16);
8704PUInt16(Result)[6] := UInt16(LDiff);
8705
8706LDiff := Int32(PUInt16(Larger)[7]) - PUInt16(Smaller)[7] + Int16(LDiff shr 16);
8707PUInt16(Result)[7] := UInt16(LDiff);
8708
8709Inc(Larger, CUnrollIncrement);
8710Inc(Smaller, CUnrollIncrement);
8711Inc(Result, CUnrollIncrement);
8712Dec(LCount);
8713end;
8714
8715while LTail > 0 do
8716begin
8717LDiff := Int32(PUInt16(Larger)[0]) - PUInt16(Smaller)[0] + Int16(LDiff shr 16);
8718PUInt16(Result)[0] := UInt16(LDiff);
8719
8720LDiff := Int32(PUInt16(Larger)[1]) - PUInt16(Smaller)[1] + Int16(LDiff shr 16);
8721PUInt16(Result)[1] := UInt16(LDiff);
8722
8723Inc(Larger);
8724Inc(Smaller);
8725Inc(Result);
8726Dec(LTail);
8727end;
8728
8729LTail := LSize and CUnrollMask;
8730LCount := LSize shr CUnrollShift;
8731
8732while LCount > 0 do
8733begin
8734LDiff := Int32(PUInt16(Larger)[0]) + Int16(LDiff shr 16);
8735PUInt16(Result)[0] := UInt16(LDiff);
8736
8737LDiff := Int32(PUInt16(Larger)[1]) + Int16(LDiff shr 16);
8738PUInt16(Result)[1] := UInt16(LDiff);
8739
8740LDiff := Int32(PUInt16(Larger)[2]) + Int16(LDiff shr 16);
8741PUInt16(Result)[2] := UInt16(LDiff);
8742
8743LDiff := Int32(PUInt16(Larger)[3]) + Int16(LDiff shr 16);
8744PUInt16(Result)[3] := UInt16(LDiff);
8745
8746LDiff := Int32(PUInt16(Larger)[4]) + Int16(LDiff shr 16);
8747PUInt16(Result)[4] := UInt16(LDiff);
8748
8749LDiff := Int32(PUInt16(Larger)[5]) + Int16(LDiff shr 16);
8750PUInt16(Result)[5] := UInt16(LDiff);
8751
8752LDiff := Int32(PUInt16(Larger)[6]) + Int16(LDiff shr 16);
8753PUInt16(Result)[6] := UInt16(LDiff);
8754
8755LDiff := Int32(PUInt16(Larger)[7]) + Int16(LDiff shr 16);
8756PUInt16(Result)[7] := UInt16(LDiff);
8757
8758Inc(Larger, CUnrollIncrement);
8759Inc(Result, CUnrollIncrement);
8760Dec(LCount);
8761end;
8762
8763while LTail > 0 do
8764begin
8765LDiff := Int32(PUInt16(Larger)[0]) + Int16(LDiff shr 16);
8766PUInt16(Result)[0] := UInt16(LDiff);
8767
8768LDiff := Int32(PUInt16(Larger)[1]) + Int16(LDiff shr 16);
8769PUInt16(Result)[1] := UInt16(LDiff);
8770
8771Inc(Larger);
8772Inc(Result);
8773Dec(LTail);
8774end;
8775end;
8776{$ENDIF}
8777{$ENDIF}
8778
8779function BigInteger.IsZero: Boolean;
8780begin
8781Result := FData = nil;
8782end;
8783
8784class procedure BigInteger.ShiftLeft(const Value: BigInteger; Shift: Integer; var Result: BigInteger);
8785var
8786LimbShift: Integer;
8787LSign: Integer;
8788begin
8789if Value.FData = nil then
8790begin
8791Result := Zero;
8792Exit;
8793end;
8794LSign := SignBitOf(Value.FSize);
8795LimbShift := Shift div CLimbBits;
8796Shift := Shift mod CLimbBits;
8797if Shift > 0 then
8798begin
8799Result.MakeSize((Value.FSize and SizeMask) + LimbShift + 1);
8800InternalShiftLeft(PLimb(Value.FData), PLimb(Result.FData) + LimbShift, Shift, (Value.FSize and SizeMask));
8801end
8802else
8803begin
8804Result.MakeSize((Value.FSize and SizeMask) + LimbShift);
8805CopyLimbs(PLimb(Value.FData), PLimb(Result.FData) + LimbShift, (Value.FSize and SizeMask));
8806end;
8807Result.FSize := (Result.FSize and SizeMask) or Integer(LSign);
8808Result.Compact;
8809end;
8810
8811class function BigInteger.ShiftLeft(const Value: BigInteger; Shift: Integer): BigInteger;
8812begin
8813ShiftLeft(Value, Shift, Result);
8814end;
8815
8816class operator BigInteger.LeftShift(const Value: BigInteger; Shift: Integer): BigInteger;
8817begin
8818ShiftLeft(Value, Shift, Result);
8819end;
8820
8821class operator BigInteger.LessThan(const Left, Right: BigInteger): Boolean;
8822begin
8823Result := Compare(Left, Right) < 0;
8824end;
8825
8826class operator BigInteger.LessThanOrEqual(const Left, Right: BigInteger): Boolean;
8827begin
8828Result := Compare(left, Right) <= 0;
8829end;
8830
8831function BigInteger.Ln: Double;
8832begin
8833Result := Ln(Self);
8834end;
8835
8836function BigInteger.BitLength: Integer;
8837begin
8838if Self.FData = nil then
8839Result := 0
8840else
8841begin
8842Result := CLimbBits * (Size - 1) + Velthuis.Numerics.BitLength(FData[Size - 1]);
8843
8844// IsPowerOfTwo is expensive, but probably less expensive than a copy and
8845// subsequent decrement, like in BitCount.
8846if (FSize < 0) and Self.IsPowerOfTwo then
8847Dec(Result);
8848end;
8849end;
8850
8851function BigInteger.BitCount: Integer;
8852var
8853Mag: TMagnitude;
8854I: Integer;
8855begin
8856if FData = nil then
8857Exit(0);
8858
8859if FSize > 0 then
8860Mag := FData
8861else
8862begin
8863Mag := Copy(FData);
8864InternalDecrement(PLimb(Mag), FSize and SizeMask);
8865end;
8866
8867Result := 0;
8868for I := 0 to Size - 1 do
8869Result := Result + Velthuis.Numerics.BitCount(Mag[I]);
8870end;
8871
8872// http://stackoverflow.com/a/7982137/95954
8873// Or: ln(a) = ln(a / 2^k) + k * ln(2)
8874class function BigInteger.Ln(const Value: BigInteger): Double;
8875var
8876ExtraBits: Integer;
8877NewInt: BigInteger;
8878begin
8879if Value.IsNegative then
8880Exit(System.Math.NaN)
8881else if Value.IsZero then
8882Exit(System.Math.NegInfinity);
8883ExtraBits := Value.BitLength - 1022;
8884if ExtraBits > 0 then
8885NewInt := Value shr ExtraBits
8886else
8887NewInt := Value;
8888Result := System.Ln(NewInt.AsDouble);
8889if ExtraBits > 0 then
8890Result := Result + ExtraBits * FLog2;
8891end;
8892
8893class function BigInteger.Log(const Value: BigInteger; Base: Double): Double;
8894begin
8895Result := BigInteger.Ln(Value) / System.Ln(Base);
8896end;
8897
8898class function BigInteger.Log10(const Value: BigInteger): Double;
8899begin
8900Result := Log(Value, 10.0);
8901end;
8902
8903class function BigInteger.Log2(const Value: BigInteger): Double;
8904begin
8905Result := Log(Value, 2.0);
8906end;
8907
8908function BigInteger.Log(Base: Double): Double;
8909begin
8910Result := Log(Self, Base);
8911end;
8912
8913function BigInteger.Log10: Double;
8914begin
8915Result := Log(Self, 10.0);
8916end;
8917
8918function BigInteger.Log2: Double;
8919begin
8920Result := Log(Self, 2.0);
8921end;
8922
8923// https://stackoverflow.com/a/7982137/95954
8924class function BigInteger.Exp(const b: Double): BigInteger;
8925var
8926bc, b2, c: Double;
8927t: Integer;
8928begin
8929if IsNan(b) or IsInfinite(b) then
8930Error(ecInvalidArgFloat, ['Double']);
8931bc := 680.0;
8932if b < bc then
8933Exit(BigInteger(System.Exp(b)));
8934// I think this can be simplified:
8935c := b - bc;
8936t := System.Math.Ceil(c / FLog2);
8937c := t * FLog2;
8938b2 := b - c;
8939Result := BigInteger(System.Exp(b2)) shl t;
8940end;
8941
8942class operator BigInteger.LogicalNot(const Value: BigInteger): BigInteger;
8943begin
8944Result := Value;
8945Inc(Result);
8946if Result.FSize <> 0 then
8947Result.FSize := Result.FSize xor SignMask;
8948Result.Compact;
8949end;
8950
8951function BigInteger.LowestSetBit: Integer;
8952var
8953I: Integer;
8954begin
8955if FData = nil then
8956Exit(-1);
8957I := 0;
8958Result := 0;
8959while FData[I] = 0 do
8960begin
8961Inc(Result, CLimbBits);
8962Inc(I);
8963end;
8964Inc(Result, NumberOfTrailingZeros(FData[I]));
8965end;
8966
8967class function BigInteger.Max(const Left, Right: BigInteger): BigInteger;
8968begin
8969if Left > Right then
8970ShallowCopy(Left, Result)
8971else
8972ShallowCopy(Right, Result);
8973end;
8974
8975class function BigInteger.Min(const Left, Right: BigInteger): BigInteger;
8976begin
8977if Left < Right then
8978ShallowCopy(Left, Result)
8979else
8980ShallowCopy(Right, Result);
8981end;
8982
8983// https://www.di-mgt.com.au/euclidean.html#code-modinv
8984class function BigInteger.ModInverse(const Value, Modulus: BigInteger): BigInteger;
8985var
8986u1, u3, v1, v3, temp1, temp3, q: BigInteger;
8987iter: Integer;
8988begin
8989// Step X1. Initialise
8990u1 := One;
8991u3 := Abs(Value);
8992v1 := Zero;
8993v3 := Abs(Modulus);
8994// X mod 1 is nonsense (always 0), but it might still be passed.
8995if (Compare(v3, One) = 0) or Modulus.IsZero then
8996Error(ecNoInverse, []);
8997// Remember odd/even iterations
8998iter := 0;
8999// Step X2. Loop while v3 <> 0
9000while not v3.IsZero do
9001begin
9002// Step X3. Divide and Subtract
9003DivMod(u3, v3, q, temp3);
9004temp1 := Add(u1, BigInteger.Multiply(q, v1));
9005// Swap
9006u1 := v1;
9007v1 := temp1;
9008u3 := v3;
9009v3 := temp3;
9010Inc(iter);
9011end;
9012// Ensure u3, i.e. gcd(Value, Modulus) = 1
9013if u3 <> One then
9014Error(ecNoInverse, []);
9015if Odd(iter) then
9016Result := Subtract(Abs(Modulus), u1)
9017else
9018Result := u1;
9019if Value < 0 then
9020Result := -Result;
9021end;
9022
9023// http://stackoverflow.com/questions/8496182/calculating-powa-b-mod-n
9024class function BigInteger.ModPow(const ABase, AExponent, AModulus: BigInteger): BigInteger;
9025var
9026Base: BigInteger;
9027Exp: BigInteger;
9028begin
9029if not AModulus.IsPositive then
9030Error(ecDivByZero, []);
9031if AModulus.IsOne then
9032Exit(BigInteger.Zero);
9033Result := BigInteger.One;
9034Exp := AExponent;
9035Base := ABase mod AModulus;
9036while Exp > Zero do
9037begin
9038if not Exp.IsEven then
9039Result := (Result * Base) mod AModulus;
9040Exp := Exp shr 1;
9041Base := Sqr(Base) mod AModulus;
9042end;
9043end;
9044
9045class operator BigInteger.Modulus(const Left, Right: BigInteger): BigInteger;
9046begin
9047Result := Remainder(Left, Right);
9048end;
9049
9050class operator BigInteger.Modulus(const Left: BigInteger; Right: UInt32): BigInteger;
9051begin
9052Result := Remainder(Left, Right);
9053end;
9054
9055class operator BigInteger.Modulus(const Left: BigInteger; Right: UInt16): BigInteger;
9056begin
9057Result := Remainder(Left, Right);
9058end;
9059
9060// Note: this can only be used to multiply by a base and add a digit, i.e. ADigit must be < ABase!
9061class procedure BigInteger.InternalMultiplyAndAdd16(Value: PLimb; ABase, ADigit: Word; var Size: Integer);
9062{$IFDEF PUREPASCAL}
9063type
9064TUInt32 = packed record
9065Lo, Hi: UInt16;
9066end;
9067var
9068I: Integer;
9069LProduct: UInt32;
9070LSize: Integer;
9071begin
9072LSize := Size shl 1;
9073I := 0;
9074LProduct := 0;
9075while I < LSize do
9076begin
9077LProduct := UInt32(PUInt16(Value)[I]) * ABase + TUInt32(LProduct).Hi;
9078PUInt16(Value)[I] := TUInt32(LProduct).Lo;
9079Inc(I);
9080end;
9081if TUInt32(LProduct).Hi <> 0 then
9082begin
9083PUInt16(Value)[I] := TUInt32(LProduct).Hi;
9084Inc(Size);
9085end;
9086if ADigit > 0 then
9087begin
9088Inc(Value[0], ADigit);
9089if Size = 0 then
9090Size := 1;
9091end;
9092end;
9093{$ELSEIF DEFINED(Win32)}
9094var
9095LValue: PLimb;
9096LDigit: UInt16;
9097asm
9098PUSH ESI
9099PUSH EDI
9100PUSH EBX
9101
9102MOV ESI,EAX
9103MOV LValue,EAX
9104MOVZX EDI,DX
9105MOV LDigit,CX
9106MOV ECX,Size
9107MOV ECX,[ECX]
9108JECXZ @DoAdd
9109XOR EBX,EBX
9110
9111@MultLoop:
9112
9113MOV EAX,[ESI]
9114MUL EAX,EDI
9115ADD EAX,EBX
9116ADC EDX,0
9117MOV [ESI],EAX
9118MOV EBX,EDX
9119LEA ESI,[ESI + CLimbSize]
9120LEA ECX,[ECX - 1]
9121JECXZ @CheckLastLimb
9122JMP @MultLoop
9123
9124@CheckLastLimb:
9125
9126OR EBX,EBX
9127JE @DoAdd
9128MOV [ESI],EBX // Carry not zero, so increment size and store carry
9129MOV ECX,Size
9130INC DWORD PTR [ECX]
9131
9132@DoAdd:
9133
9134MOVZX EAX,LDigit
9135OR EAX,EAX
9136JZ @Exit // Skip if ADigit is 0 anyway.
9137MOV ECX,Size
9138CMP DWORD PTR [ECX],0 // If Size = 0 and ADigit <> 0, must add 1 to size
9139JNZ @SkipInc
9140INC DWORD PTR [ECX]
9141
9142@SkipInc:
9143
9144MOV ESI,LValue
9145ADD [ESI],EAX // Note that allocated size is always > 1.
9146
9147@Exit:
9148
9149POP EBX
9150POP EDI
9151POP ESI
9152end;
9153{$ELSE WIN64}
9154asm
9155.PUSHNV RSI
9156
9157PUSH RCX // Save Value
9158MOV RSI,RCX // RSI = Value
9159MOV R10D,EDX // R10D = ABase
9160XOR R11D,R11D // Multiplication "carry"
9161MOV RCX,R9 // Size
9162MOV ECX,[RCX] // Size^
9163JECXZ @DoAdd
9164
9165@MultLoop:
9166
9167MOV EAX,[RSI]
9168MUL EAX,R10D
9169ADD EAX,R11D
9170ADC EDX,0
9171MOV [RSI],EAX
9172MOV R11D,EDX
9173LEA RSI,[RSI + CLimbSize]
9174LEA ECX,[ECX - 1]
9175JECXZ @CheckLastLimb
9176JMP @MultLoop
9177
9178@CheckLastLimb:
9179
9180OR EDX,EDX
9181JE @DoAdd
9182MOV [RSI],EDX
9183INC DWORD PTR [R9]
9184
9185@DoAdd:
9186
9187POP RCX // Restore Value
9188OR R8D,R8D // If ADigit is 0 then we are finished
9189JZ @Exit
9190CMP [R9],0 // If Size = 0, and ADigit isn't, then increment size
9191JNE @SkipInc
9192INC DWORD PTR [R9]
9193
9194@SkipInc:
9195
9196ADD [RCX],R8D // Add ADigit
9197
9198@Exit:
9199
9200end;
9201{$IFEND}
9202
9203
9204{ TODO: It dawned to me that if you multiply by Base, and then add a number that is < Base, there can *never* be a
9205carry. Even $FFFFFFFF x 36, the lowest limb will be $FFFFFFFF - 35 ($FFFFFFDC), so adding 35 ($23) can not
9206cause a carry. Tried that with other multiplicators too.
9207
9208This can be applied to MultiplyAndAdd32 too.
9209
9210So just call it InternalMultiplyBaseAndAdd. Addend must be < Base, and there will never be a carry.
9211This means it is possible to pre-allocate and pass the size. This routine must update size if it sets the top
9212limb. So there can be a Size and it must be a var parameter. Just add the addend to the lowest limb. No need
9213to carry.
9214}
9215class procedure BigInteger.InternalMultiply16(const Left: TMagnitude; var Result: TMagnitude; LSize: Integer; Right: Word);
9216{$IF DEFINED(PUREPASCAL)}
9217type
9218TUInt32 = packed record
9219Lo, Hi: UInt16;
9220end;
9221var
9222I: Integer;
9223LProduct: UInt32;
9224LHi16: UInt16;
9225begin
9226LSize := LSize * 2;
9227LHi16 := 0;
9228I := 0;
9229while I < LSize do
9230begin
9231LProduct := UInt32(PUInt16(Left)[I]) * Right + LHi16;
9232PUInt16(Result)[I] := TUInt32(LProduct).Lo;
9233LHi16 := TUInt32(LProduct).Hi;
9234Inc(I);
9235end;
9236if LHi16 <> 0 then
9237begin
9238PUInt16(Result)[I] := LHi16;
9239// var parameter Size := I;
9240// Size should be the fifth parameter, so it can easily be set from 64 bit code.
9241end;
9242end;
9243{$ELSEIF DEFINED(WIN32)}
9244asm
9245PUSH EBX
9246PUSH ESI
9247PUSH EDI
9248
9249JECXZ @Exit
9250MOV ESI,EAX
9251MOV EDI,[EDX]
9252XOR EBX,EBX
9253CMP Right,0
9254JE @Exit
9255
9256@MultLoop:
9257
9258MOV EAX,[ESI]
9259MOVZX EDX,Right
9260MUL EDX
9261ADD EAX,EBX
9262ADC EDX,0
9263MOV EBX,EDX
9264MOV [EDI],EAX
9265LEA ESI,[ESI + CLimbSize]
9266LEA EDI,[EDI + CLimbSize]
9267LEA ECX,[ECX - 1]
9268JECXZ @EndMultLoop
9269JMP @MultLoop
9270
9271@EndMultLoop:
9272
9273MOV [EDI],EBX
9274
9275@Exit:
9276
9277POP EDI
9278POP ESI
9279POP EBX
9280end;
9281{$ELSE WIN64}
9282asm
9283.PUSHNV RBX
9284.PUSHNV RDI
9285.PUSHNV RSI
9286
9287OR R8D,R8D
9288JE @Exit
9289OR R9D,R9D
9290JE @Exit
9291MOV R11,[RDX] // R11 = SaveResult
9292MOV RDI,R11 // RDI = Result
9293MOV RSI,RCX // RSI = Left
9294MOV ECX,R8D // ECX = MSize
9295XOR EBX,EBX // EBX = Carry
9296
9297@MultLoop:
9298
9299MOV EAX,[RSI]
9300MUL EAX,R9D // Unusual syntax, but XE2 otherwise generates MUL R9 instead of MUL R9D
9301ADD EAX,EBX
9302ADC EDX,0
9303MOV [RDI],EAX
9304MOV EBX,EDX
9305LEA RSI,[RSI + CLimbSize]
9306LEA RDI,[RDI + CLimbSize]
9307LEA ECX,[ECX - 1]
9308JECXZ @EndMultLoop
9309JMP @MultLoop
9310
9311@EndMultLoop:
9312
9313MOV [RDI],EBX
9314
9315@Exit:
9316end;
9317{$IFEND}
9318
9319class operator BigInteger.Multiply(const Left: BigInteger; Right: Word): BigInteger;
9320var
9321ResData: TMagnitude;
9322ResSize: Integer;
9323begin
9324if (Right = 0) or ((Left.FSize and SizeMask) = 0) then
9325Exit(Zero);
9326ResSize := (Left.FSize and SizeMask) + 2;
9327SetLength(ResData, ResSize);
9328InternalMultiply16(Left.FData, ResData, (Left.FSize and SizeMask), Right);
9329Assert(Result.FData <> ResData);
9330Result.FData := ResData;
9331Result.FSize := (Left.FSize and SignMask) or ResSize;
9332Result.Compact;
9333end;
9334
9335class operator BigInteger.Multiply(Left: Word; const Right: BigInteger): BigInteger;
9336begin
9337Result := Multiply(Right, Left);
9338end;
9339
9340class procedure BigInteger.MultiplyKaratsuba(const Left, Right: BigInteger; var Result: BigInteger);
9341var
9342k, LSign: Integer;
9343z0, z1, z2: BigInteger;
9344x, y: TArray<BigInteger>;
9345Shift: Integer;
9346begin
9347if ((Left.FSize and SizeMask) < KaratsubaThreshold) or ((Right.FSize and SizeMask) < KaratsubaThreshold) then
9348begin
9349MultiplyBaseCase(Left, Right, Result);
9350Exit;
9351end;
9352
9353//////////////////////////////////////////////////////////////////////////////////////////////////
9354/// This is a so called divide and conquer algorithm, solving a big task by dividing it up ///
9355/// into easier (and hopefully faster, in total) smaller tasks. ///
9356/// ///
9357/// Let's treat a BigInteger as a polynomial, i.e. x = x1 * B + x0, where B is chosen thus, ///
9358/// that the top and the low part of the BigInteger are almost the same in size. ///
9359/// The result R of the multiplication of two such polynomials can be seen as: ///
9360/// ///
9361/// R = (x1 * B + x0) * (y1 * B + y0) = x1 * y1 * B^2 + (x1 * y0 + x0 * y1) * B + x0 * y0 ///
9362/// ///
9363/// say, z0 = x0 * y0 ///
9364/// z1 = x1 * y0 + x0 * y1 ///
9365/// z2 = x1 * y1 ///
9366/// ///
9367/// then ///
9368/// R = z2 * B^2 + z1 * B + z0 ///
9369/// ///
9370/// Karatsuba noted that: ///
9371/// ///
9372/// (x1 + x0) * (y1 + y0) = z2 + z1 + z0, so z1 = (x1 + x0) * (y1 + y0) - (z2 + z0) ///
9373/// ///
9374/// That reduced four multiplications and a few additions to three multiplications, a few ///
9375/// additions and a subtraction. Surely the parts will be multilimb, but this is called ///
9376/// recursively down until the sizes are under a threshold, and then simple base case ///
9377/// (a.k.a. "schoolbook") multiplication is performed. ///
9378//////////////////////////////////////////////////////////////////////////////////////////////////
9379
9380//////////////////////////////////////////////////////////////////////////////////////////////////
9381/// Note: it may look tempting to use pointers into the original operands, to use one large ///
9382/// buffer for all results, and to use InternalMultiply directly, but remember that ///
9383/// InternalMultiply performs a basecase multiplication and it does NOT resurse into a ///
9384/// deeper level of MultiplyKaratsuba, so after one level, the advantage gained by reducing ///
9385/// the number of multiplications would be minimal. ///
9386/// ///
9387/// There is an overhead caused by using complete BigIntegers, but it is not as high as it ///
9388/// may look. ///
9389//////////////////////////////////////////////////////////////////////////////////////////////////
9390
9391LSign := (Left.FSize xor Right.FSize) and SignMask;
9392
9393k := (IntMax(Left.FSize and SizeMask, Right.FSize and SizeMask) + 1) shr 1;
9394
9395x := Left.Split(k, 2);
9396y := Right.Split(k, 2);
9397
9398// Recursion further reduces the number of multiplications!
9399MultiplyKaratsuba(x[1], y[1], z2);
9400MultiplyKaratsuba(x[0], y[0], z0);
9401MultiplyKaratsuba(x[1] - x[0], y[0] - y[1], z1);
9402Add(z1, z2 + z0, z1);
9403
9404Shift := k * CLimbBits;
9405
9406Result := z0 + ((z1 + z2 shl Shift) shl Shift);
9407Result.FSize := (Result.FSize and SizeMask) or LSign;
9408end;
9409
9410// Used by Karatsuba, Toom-Cook and Burnikel-Ziegler algorithms.
9411// Splits Self into BlockCount pieces of (at most) BlockSize limbs, starting with the least significant part.
9412function BigInteger.Split(BlockSize, BlockCount: Integer): TArray<BigInteger>;
9413var
9414I: Integer;
9415begin
9416SetLength(Result, BlockCount);
9417for I := 0 to BlockCount - 1 do
9418begin
9419if (Self.FSize and BigInteger.SizeMask) > I * BlockSize then
9420begin
9421Result[I].MakeSize(IntMin(BlockSize, (Self.FSize and SizeMask) - I * BlockSize));
9422CopyLimbs(PLimb(Self.FData) + I * BlockSize, PLimb(Result[I].FData), IntMin(BlockSize, (Self.FSize and SizeMask) - I * BlockSize));
9423Result[I].Compact;
9424end
9425else
9426ShallowCopy(Zero, Result[I]);
9427end;
9428end;
9429
9430{$IFNDEF PUREPASCAL}
9431class procedure BigInteger.InternalDivideBy3(Value, Result: PLimb; ASize: Integer);
9432const
9433MultConst = $AAAAAAAB;
9434MultConst2 = $55555556;
9435{$IFDEF WIN32}
9436asm
9437PUSH ESI
9438PUSH EDI
9439PUSH EBX
9440
9441MOV ESI,EAX // Value
9442MOV EDI,EDX // Result
9443XOR EBX,EBX // borrow
9444
9445@Loop:
9446
9447MOV EAX,[ESI]
9448SUB EAX,EBX
9449SETC BL
9450
9451MOV EDX,MultConst
9452MUL EAX,EDX
9453MOV [EDI],EAX
9454
9455CMP EAX,MultConst2
9456JB @SkipInc
9457INC EBX
9458CMP EAX,MultConst
9459JB @SkipInc
9460INC EBX
9461
9462@SkipInc:
9463
9464LEA ESI,[ESI + CLimbSize]
9465LEA EDI,[EDI + CLimbSize]
9466DEC ECX
9467JNE @Loop
9468
9469@Exit:
9470
9471POP EBX
9472POP EDI
9473POP ESI
9474end;
9475{$ELSE WIN64}
9476asm
9477XOR R9D,R9D // borrow
9478MOV R10,RDX // Result
9479MOV R11D,MultConst
9480
9481@Loop:
9482
9483MOV EAX,[RCX]
9484SUB EAX,R9D
9485SETC R9B
9486
9487MUL EAX,R11D // MultConst
9488MOV [R10],EAX
9489
9490CMP EAX,MultConst2
9491JB @SkipInc
9492INC R9D
9493CMP EAX,R11D // MultConst
9494JB @SkipInc
9495INC R9D
9496
9497@SkipInc:
9498
9499LEA RCX,[RCX + CLimbSize]
9500LEA R10,[R10 + CLimbSize]
9501DEC R8D
9502JNE @Loop
9503end;
9504{$ENDIF WIN64}
9505{$ENDIF !PUREPASCAL}
9506
9507// Only works if it is known that there is no remainder and A is positive.
9508class function BigInteger.DivideBy3Exactly(const A: BigInteger): BigInteger;
9509const
9510ModInverse3 = $AAAAAAAB; // Modular inverse of 3 modulo $100000000.
9511ModInverse3t2 = $55555556; // ModInverse3 / 2
9512{$IFDEF PUREPASCAL}
9513var
9514i: Integer;
9515ai, w, qi, borrow: Int64;
9516begin
9517if A.FData = nil then
9518Exit(Zero);
9519
9520Result.MakeSize(A.FSize and SizeMask);
9521borrow := 0;
9522for i := 0 to (A.FSize and SizeMask) - 1 do
9523begin
9524ai := A.FData[i];
9525w := ai - borrow;
9526if borrow > ai then
9527borrow := 1
9528else
9529borrow := 0;
9530
9531qi := (w * ModInverse3) and $FFFFFFFF;
9532Result.FData[i] := UInt32(qi);
9533
9534if qi >= ModInverse3t2 then
9535begin
9536Inc(borrow);
9537if qi >= ModInverse3 then
9538Inc(borrow);
9539end;
9540end;
9541
9542Result.Compact;
9543end;
9544{$ELSE !PUREPASCAL}
9545begin
9546if A.FData = nil then
9547begin
9548ShallowCopy(Zero, Result);
9549Exit;
9550end;
9551
9552Result.MakeSize(A.FSize and SizeMask);
9553InternalDivideBy3(PLimb(A.FData), PLimb(Result.FData), A.FSize and SizeMask);
9554Result.Compact;
9555end;
9556{$ENDIF !PUREPASCAL}
9557
9558class function BigInteger.MultiplyToomCook3(const Left, Right: BigInteger): BigInteger;
9559var
9560k, Shift: Integer;
9561a, b: TArray<BigInteger>;
9562a02, b02: BigInteger;
9563v0, v1, vm1, v2, vinf: BigInteger;
9564t1, t2: BigInteger;
9565Sign: Integer;
9566begin
9567// Step 1: if n < threshold then return MultiplyKaratsuba(A, B)
9568if ((Left.FSize and SizeMask) < ToomCook3Threshold) and ((Right.FSize and SizeMask) < ToomCook3Threshold) then
9569begin
9570MultiplyKaratsuba(Left, Right, Result);
9571Exit;
9572end;
9573
9574Sign := (Left.FSize xor Right.FSize) and SignMask;
9575
9576// Richard P. Brent and Paul Zimmermann,
9577// "Modern Computer Arithmetic", version 0.5.1 of April 28, 2010
9578// http://arxiv.org/pdf/1004.4710v1.pdf
9579// Algorithm 1.4
9580
9581// Step 2: write A = a0 + a1*x + a2*x^2, B = b0 + b1*x + b2*x^2, with x = ß^k.
9582k := (IntMax(Left.FSize and SizeMask, Right.FSize and SizeMask) + 2) div 3; // = Ceil(IntMax(...) div 3)
9583
9584a := Left.Split(k, 3);
9585b := Right.Split(k, 3);
9586
9587// Evaluation at x = -1, 0, 1, 2 and +inf.
9588
9589// Step 3: v0 <- ToomCook3(a0, b0)
9590v0 := MultiplyToomCook3(a[0], b[0]);
9591
9592// Step 4a: a02 <- a0 + a2, b02 <- b0 + b2
9593a02 := a[0] + a[2];
9594b02 := b[0] + b[2];
9595
9596// Step 5: v(-1) <- ToomCook3(a02 - a1, b02 - b1) = ToomCook3(a0 + a2 - a1, b0 + b2 - b1)
9597vm1 := MultiplyToomCook3(a02 - a[1], b02 - b[1]);
9598
9599// Intermediate step: a'02 = a02 + a1, b'02 = b02 + b1
9600a02 := a02 + a[1];
9601b02 := b02 + b[1];
9602
9603// Step 4b: v1 <- ToomCook3(a02 + a1, b02 + b1) = ToomCook3(a'02, b'02)
9604v1 := MultiplyToomCook3(a02, b02);
9605
9606// Step 6: v2 <- ToomCook3(a0 + 2*a1 + 4*a2, b0 + 2*b1 + 4*b2)
9607// Note: first operand is a0 + a1 + a1 + a2 + a2 + a2 + a2 = 2*(a0 + a1 + a2 + a2) - a0 = 2*(a'02 + a2) - a0
9608v2 := MultiplyToomCook3((a02 + a[2]) shl 1 - a[0], (b02 + b[2]) shl 1 - b[0]);
9609
9610// Step 7: v(inf) <- ToomCook3(a2, b2)
9611vinf := MultiplyToomCook3(a[2], b[2]);
9612
9613// Step 8: t1 <- (3*v0 + 2*v(−1) + v2)/6 − 2 * v(inf), t2 <- (v1 + v(−1))/2
9614t1 := DivideBy3Exactly(((v0 + vm1) shl 1 + v0 + v2) shr 1) - (vinf shl 1);
9615t2 := (v1 + vm1) shr 1;
9616
9617// Step 9: c0 <- v0, c1 <- v1 - t1, c2 <- t2 - v0 - vinf, c3 <- t1 - t2, c4 <- vinf
9618Shift := k * CLimbBits;
9619
9620Result := (((((((vinf shl Shift) + t1 - t2) shl Shift) + t2 - v0 - vinf) shl Shift) + v1 - t1) shl Shift) + v0;
9621Result.FSize := (Result.FSize and SizeMask) or Sign;
9622end;
9623
9624class function BigInteger.SqrKaratsuba(const Value: BigInteger): BigInteger;
9625var
9626NDiv2Shift, NDiv2: Integer;
9627ValueUpper: BigInteger;
9628ValueLower: BigInteger;
9629Upper, Middle, Lower: BigInteger;
9630LSize: Integer;
9631begin
9632LSize := (Value.FSize and SizeMask);
9633NDiv2Shift := (LSize and $FFFFFFFE) shl 4; // := LSize div 2 * SizeOf(TLimb);
9634NDiv2 := LSize div 2;
9635
9636ValueLower.MakeSize(NDiv2);
9637CopyLimbs(PLimb(Value.FData), PLimb(ValueLower.FData), NDiv2);
9638ValueUpper.MakeSize((Value.FSize and SizeMask) - NDiv2);
9639CopyLimbs(PLimb(Value.FData) + NDiv2, PLimb(ValueUpper.FData), (Value.FSize and SizeMask) - NDiv2);
9640ValueLower.Compact;
9641
9642Upper := Sqr(ValueUpper);
9643Lower := Sqr(ValueLower);
9644Middle := (ValueUpper * ValueLower) shl 1;
9645
9646// Can't simply move these values into place, because they still overlap when shifted.
9647Result := Upper shl (NDiv2Shift + NDiv2Shift) + Middle shl NDiv2Shift + Lower;
9648Result.FSize := Result.FSize and SizeMask;
9649end;
9650
9651class function BigInteger.Multiply(const Left, Right: BigInteger): BigInteger;
9652begin
9653Multiply(Left, Right, Result);
9654end;
9655
9656class procedure BigInteger.Multiply(const Left, Right: BigInteger; var Result: BigInteger);
9657var
9658LResult: BigInteger; // Avoid prematurely overwriting result when it is same as one of the operands.
9659begin
9660if (Left.FData = nil) or (Right.FData = nil) then
9661begin
9662ShallowCopy(BigInteger.Zero, Result);
9663Exit;
9664end;
9665
9666if ((Left.FSize and SizeMask) < KaratsubaThreshold) or ((Right.FSize and SizeMask) < KaratsubaThreshold) then
9667begin
9668// The following block is "Result := MultiplyBaseCase(Left, Right);" written out in full.
9669LResult.MakeSize((Left.FSize and SizeMask) + (Right.FSize and SizeMask) + 1);
9670InternalMultiply(PLimb(Left.FData), PLimb(Right.FData), PLimb(LResult.FData), Left.FSize and SizeMask,
9671Right.FSize and SizeMask);
9672LResult.Compact;
9673LResult.FSize := (LResult.FSize and SizeMask) or ((Left.FSize xor Right.FSize) and SignMask);
9674ShallowCopy(LResult, Result);
9675end
9676else
9677begin
9678if ((Left.FSize and SizeMask) < ToomCook3Threshold) and ((Right.FSize and SizeMask) < ToomCook3Threshold) then
9679MultiplyKaratsuba(Left, Right, Result)
9680else
9681Result := MultiplyToomCook3(Left, Right);
9682end;
9683end;
9684
9685class procedure BigInteger.MultiplyBaseCase(const Left, Right: BigInteger; var Result: BigInteger);
9686var
9687LResult: BigInteger; // Avoid prematurely overwriting result when it is same as one of the operands.
9688begin
9689if (Left.FData = nil) or (Right.FData = nil) then
9690begin
9691ShallowCopy(Zero, Result);
9692Exit;
9693end;
9694
9695//$$RV LResult.MakeSize((Left.FSize and SizeMask) + (Right.FSize and SizeMask) + 1);
9696LResult.MakeSize((Left.FSize and SizeMask) + (Right.FSize and SizeMask) + 256);
9697InternalMultiply(PLimb(Left.FData), PLimb(Right.FData), PLimb(LResult.FData), Left.FSize and SizeMask,
9698Right.FSize and SizeMask);
9699LResult.Compact;
9700LResult.SetSign(SignBitOf(Left.FSize) xor SignBitOf(Right.FSize));
9701ShallowCopy(LResult, Result);
9702end;
9703
9704class operator BigInteger.Multiply(const Left, Right: BigInteger): BigInteger;
9705begin
9706Result := Multiply(Left, Right);
9707end;
9708
9709class procedure BigInteger.SetBase(const Value: TNumberBase);
9710begin
9711FBase := Value;
9712end;
9713
9714procedure BigInteger.SetSign(Value: Integer);
9715begin
9716FSize := (FSize and SizeMask) or (Ord(Value < 0) * SignMask);
9717end;
9718
9719{$IFNDEF BIGINTEGERIMMUTABLE}
9720function BigInteger.Subtract(const Other: BigInteger): PBigInteger;
9721var
9722MinusOther: BigInteger;
9723begin
9724ShallowCopy(Other, MinusOther);
9725MinusOther.FSize := MinusOther.FSize xor SignMask;
9726Result := Add(MinusOther);
9727end;
9728{$ENDIF}
9729
9730class function BigInteger.Subtract(const Left, Right: BigInteger): BigInteger;
9731begin
9732Subtract(Left, Right, Result);
9733end;
9734
9735class procedure BigInteger.Subtract(const Left, Right: BigInteger; var Result: BigInteger);
9736const
9737BoolMasks: array[Boolean] of Integer = (SignMask, 0);
9738var
9739Largest, Smallest: PBigInteger;
9740LSize, SSize: Integer;
9741ResData: TMagnitude;
9742ResSize: Integer;
9743NewSize: Integer;
9744Comparison: Integer;
9745begin
9746if Left.FData = nil then
9747begin
9748Result := Right;
9749if Result.FSize <> 0 then
9750Result.FSize := Result.FSize xor SignMask;
9751Exit;
9752end;
9753if Right.FData = nil then
9754begin
9755Result := Left;
9756Exit;
9757end;
9758
9759Comparison := InternalCompare(PLimb(Left.FData), PLimb(Right.FData), Left.FSize and SizeMask,
9760Right.FSize and SizeMask);
9761if (Comparison = 0) and (Left.Sign = Right.Sign) then
9762begin
9763Result := Zero;
9764Exit;
9765end;
9766
9767if Comparison > 0 then
9768begin
9769Largest := @Left;
9770Smallest := @Right;
9771end
9772else
9773begin
9774Largest := @Right;
9775Smallest := @Left;
9776end;
9777
9778SSize := Smallest^.FSize and SizeMask;
9779LSize := Largest^.FSize and SizeMask;
9780ResSize := LSize + 1;
9781SetLength(ResData, (ResSize + 3) and CapacityMask);
9782
9783if Largest^.Sign = Smallest^.Sign then
9784// Same sign: subtract magnitudes.
9785FInternalSubtract(PLimb(Largest^.FData), PLimb(Smallest^.FData), PLimb(ResData), LSize, SSize)
9786else
9787// Different sign: add magnitudes.
9788FInternalAdd(PLimb(Largest^.FData), PLimb(Smallest^.FData), PLimb(ResData), LSize, SSize);
9789
9790// Compact and set sign.
9791NewSize := ActualSize(PLimb(ResData), ResSize);
9792if NewSize = 0 then
9793begin
9794Result := Zero;
9795Exit;
9796end
9797else
9798begin
9799{$IFDEF RESETSIZE}
9800if NewSize < (ResSize + 3) and CapacityMask then
9801SetLength(ResData, (NewSize + 3) and CapacityMask);
9802{$ENDIF}
9803// Set sign and size.
9804Result.FSize := NewSize or BoolMasks[(Largest^.FSize < 0) xor (Largest = @Left)];
9805Result.FData := ResData;
9806end;
9807end;
9808
9809class operator BigInteger.Subtract(const Left, Right: BigInteger): BigInteger;
9810begin
9811Subtract(Left, Right, Result);
9812end;
9813
9814procedure BigInteger.EnsureSize(RequiredSize: Integer);
9815begin
9816RequiredSize := RequiredSize and SizeMask;
9817if RequiredSize > Length(FData) then
9818SetLength(FData, (RequiredSize + 4) and CapacityMask);
9819FSize := (FSize and SignMask) or RequiredSize;
9820end;
9821
9822procedure BigInteger.MakeSize(RequiredSize: Integer);
9823begin
9824FData := nil;
9825AllocNewMagnitude(FData, RequiredSize);
9826FSize := RequiredSize;
9827end;
9828
9829// In Win32, we keep what we have. In Win64, we switch, depending on Size. At 25 limbs or above,
9830// the unrolled loop version is faster.
9831class procedure BigInteger.InternalNegate(Source, Dest: PLimb; Size: Integer);
9832{$IFDEF PUREPASCAL}
9833var
9834R: TLimb;
9835begin
9836repeat
9837R := (not Source^) + 1;
9838Dest^ := R;
9839Inc(Source);
9840Inc(Dest);
9841Dec(Size);
9842if Size = 0 then
9843Exit;
9844until R <> 0;
9845while Size > 0 do
9846begin
9847Dest^ := not Source^;
9848Inc(Source);
9849Inc(Dest);
9850Dec(Size);
9851end;
9852end;
9853{$ELSE}
9854{$IFDEF WIN32}
9855
9856// This is faster than an unrolled loop with NOT and ADC, especially for smaller BigIntegers.
9857
9858asm
9859PUSH ESI
9860
9861@Loop:
9862
9863MOV ESI,[EAX]
9864NOT ESI
9865INC ESI
9866MOV [EDX],ESI
9867LEA EAX,[EAX + CLimbSize]
9868LEA EDX,[EDX + CLimbSize]
9869DEC ECX
9870JE @Exit
9871TEST ESI,ESI // Only if ESI is 0, a carry occurred.
9872JE @Loop
9873
9874@RestLoop: // No more carry. We can stop incrementing.
9875
9876MOV ESI,[EAX]
9877NOT ESI
9878MOV [EDX],ESI
9879LEA EAX,[EAX + CLimbSize]
9880LEA EDX,[EDX + CLimbSize]
9881DEC ECX
9882JNE @RestLoop
9883
9884@Exit:
9885
9886POP ESI
9887end;
9888{$ELSE WIN64}
9889asm
9890
9891CMP R8D,25
9892JA @Unrolled
9893
9894// Plain version. Faster for small BigIntegers (<= 25 limbs).
9895
9896@Loop:
9897
9898MOV EAX,[RCX]
9899NOT EAX
9900INC EAX
9901MOV [RDX],EAX
9902LEA RCX,[RCX + CLimbSize]
9903LEA RDX,[RDX + CLimbSize]
9904DEC R8D
9905JE @Exit
9906TEST EAX,EAX
9907JE @Loop
9908
9909@RestLoop:
9910
9911MOV EAX,[RCX]
9912NOT EAX
9913MOV [RDX],EAX
9914LEA RCX,[RCX + CLimbSize]
9915LEA RDX,[RDX + CLimbSize]
9916DEC R8D
9917JNE @RestLoop
9918JMP @Exit
9919
9920// Unrolled version. Faster for larger BigIntegers.
9921
9922@Unrolled:
9923
9924TEST RCX,RCX
9925JE @Exit
9926XCHG R8,RCX
9927MOV R9,RDX
9928XOR EDX,EDX
9929MOV R10D,ECX
9930AND R10D,CUnrollMask
9931SHR ECX,CUnrollShift
9932STC
9933JE @Rest
9934
9935@LoopU:
9936
9937MOV RAX,[R8]
9938NOT RAX
9939ADC RAX,RDX
9940MOV [R9],RAX
9941
9942MOV RAX,[R8 + DLimbSize]
9943NOT RAX
9944ADC RAX,RDX
9945MOV [R9 + DLimbSize],RAX
9946
9947LEA R8,[R8 + 2*DLimbSize]
9948LEA R9,[R9 + 2*DLimbSize]
9949LEA ECX,[ECX - 1]
9950JECXZ @Rest
9951JMP @LoopU
9952
9953@Rest:
9954
9955LEA RAX,[@JumpTable]
9956JMP [RAX + R10*TYPE Pointer]
9957
9958.ALIGN 16
9959
9960@JumpTable:
9961
9962DQ @Exit
9963DQ @Rest1
9964DQ @Rest2
9965DQ @Rest3
9966
9967@Rest3:
9968
9969MOV RAX,[R8]
9970NOT RAX
9971ADC RAX,RDX
9972MOV [R9],RAX
9973
9974MOV EAX,[R8 + DLimbSize]
9975NOT EAX
9976ADC EAX,EDX
9977MOV [R9 + DLimbSize],EAX
9978
9979JMP @Exit
9980
9981@Rest2:
9982
9983MOV RAX,[R8]
9984NOT RAX
9985ADC RAX,RDX
9986MOV [R9],RAX
9987
9988JMP @Exit
9989
9990@Rest1:
9991
9992MOV EAX,[R8]
9993NOT EAX
9994ADC EAX,EDX
9995MOV [R9],EAX
9996
9997@Exit:
9998end;
9999{$ENDIF WIN64}
10000{$ENDIF !PUREPASCAL}
10001
10002class procedure BigInteger.InternalBitwise(const Left, Right: BigInteger;
10003var Result: BigInteger; PlainOp, OppositeOp, InversionOp: TBinaryOperator);
10004
10005/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10006/// The code for the bitwise operators AND, OR and XOR does not differ much. ///
10007/// Since the results should be the results for two's complement, two's complement semantics are emulated. ///
10008/// Originally, this meant that the magnitudes of negative bigintegers were negated, then the ///
10009/// operation was performed and if the result had to be negative, the magnitude of the result was negated. ///
10010/// These negation steps were slow, so now this code uses some logical shortcuts. ///
10011/// ///
10012/// The rules used are like follows. ///
10013/// ///
10014/// In the following, A and B represent positive integer values, so -A and -B represent negative values. ///
10015/// Note that, to keep this simple, 0 -- i.e. FData = nil -- is not handled at all. That is handled ///
10016/// by the caller and then this routine is not called. ///
10017/// ///
10018/// Relation between negation and inversion of an integer/magnitude: ///
10019/// -A = not A + 1 => not A = -A - 1 ///
10020/// -A = not (A - 1) ///
10021/// ///
10022/// Note: A and B are magnitudes here. Negating a BigInteger is as simple as flipping the sign bit. That ///
10023/// does not work for magnitudes. ///
10024/// ///
10025/// Boolean (and bitwise) rules followed: ///
10026/// not not A = A ///
10027/// not (A and B) = not A or not B ///
10028/// not (A or B) = not A and not B ///
10029/// not (A xor B) = not A xor B = A xor not B ///
10030/// not A xor not B = A xor B ///
10031/// ///
10032/// Expressions used here: ///
10033/// ///
10034/// A and B = A and B ; both positive, plain operation ///
10035/// A and -B = A and not (B - 1) ; one positive, one negative, result positive ///
10036/// -(-A and -B) = -(not (A - 1) and not (B - 1)) ; both negative, result is negative too ///
10037/// = - not ((A - 1) or (B - 1))) ///
10038/// = (A - 1) or (B - 1) + 1 ///
10039/// ///
10040/// A or B = A or B ; both positive ///
10041/// -(A or -B) = -(A or not (B - 1)) ; one positive, one negative, result is negative too ///
10042/// = - not (not A and (B - 1)) ///
10043/// = ((B - 1) and not A) + 1 ///
10044/// -(-A or -B) = -(not (A - 1) or not (B - 1)) ; both negative, result is negative too ///
10045/// = not (not (A - 1) or not (B - 1) + 1 ///
10046/// = (A - 1) and (B - 1) + 1 ///
10047/// ///
10048/// A xor B = A xor B ; both positive ///
10049/// -(A xor -B) = -(A xor not (B - 1)) ; one positive, one negative, result is negative too ///
10050/// = not (A xor not (B - 1)) + 1 ///
10051/// = A xor (B - 1) + 1 ///
10052/// -A xor -B = not (A - 1) xor not (B - 1) ; both negative, result is positive ///
10053/// = (A - 1) xor (B - 1) ///
10054/// ///
10055/// So the only "primitives" required are routines for AND, OR, XOR and AND NOT. The latter is not really ///
10056/// a primitive, but it is so easy to implement, that it can be considered one. NOT is cheap, does not require ///
10057/// complicated carry handling. ///
10058/// Routines like Inc and Dec are cheap too: you only loop as long as there is a carry (or borrow). Often, that ///
10059/// is only over very few limbs. ///
10060/// ///
10061/// Primitives (InternalAnd(), etc.) routines were optimized too. Loops were unrolled, 64 bit registers used ///
10062/// where possible, both sizes are passed, so the operations can be done on the original data. The latter ///
10063/// reduces the need for copying into internal buffers. ///
10064/// ///
10065/// These optimizations made bitwise operators 2-3 times as fast as with the simple implementations before. ///
10066/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10067
10068var
10069LSize, RSize, MinSize, MaxSize: Integer;
10070LPtr, RPtr: PLimb;
10071begin
10072LSize := Left.FSize and SizeMask;
10073RSize := Right.FSize and SizeMask;
10074MinSize := IntMin(LSize, RSize);
10075MaxSize := IntMax(LSize, RSize);
10076
10077if ((Left.FSize xor Right.FSize) and SignMask) = 0 then
10078begin
10079if (Left.FSize > 0) then
10080begin
10081if Addr(PlainOp) = Addr(InternalAnd) then
10082Result.MakeSize(MinSize)
10083else
10084Result.MakeSize(MaxSize);
10085PlainOp(PLimb(Left.FData), PLimb(Right.FData), PLimb(Result.FData), LSize, RSize);
10086end
10087else
10088begin
10089LPtr := AllocLimbs(LSize + RSize); // LPtr := Copy(Left);
10090RPtr := LPtr + LSize; // RPtr := Coyp(Right);
10091CopyLimbs(PLimb(Left.FData), LPtr, LSize);
10092CopyLimbs(PLimb(Right.FData), RPtr, RSize);
10093InternalDecrement(LPtr, LSize); // LPtr^ := LPtr^ - 1
10094InternalDecrement(RPtr, RSize); // RPtr^ := RPtr^ - 1
10095Result.FSize := 0;
10096Result.MakeSize(MaxSize);
10097OppositeOp(LPtr, RPtr, PLimb(Result.FData), LSize, RSize); // Opposite op: AND --> OR, OR --> AND, XOR --> XOR
10098if Addr(PlainOp) = Addr(InternalXor) then
10099Result.FSize := Result.FSize and SizeMask // Make positive.
10100else
10101begin
10102InternalIncrement(PLimb(Result.FData), MaxSize); // Result := Result + 1
10103Result.FSize := Result.FSize or SignMask; // Make negative.
10104end;
10105FreeMem(LPtr);
10106end;
10107end
10108else
10109begin
10110if (Left.FSize > 0) then
10111begin
10112RPtr := AllocLimbs(RSize);
10113CopyLimbs(PLimb(Right.FData), RPtr, RSize);
10114InternalDecrement(RPtr, RSize);
10115Result.FSize := 0;
10116if Addr(PlainOp) = Addr(InternalOr) then
10117Result.MakeSize(RSize)
10118else
10119Result.MakeSize(MaxSize);
10120// Inversion op: AND --> AND NOT, OR --> NOT AND, XOR --> XOR
10121InversionOp(PLimb(Left.FData), RPtr, PLimb(Result.FData), LSize, RSize);
10122if Addr(PlainOp) = Addr(InternalAnd) then
10123Result.FSize := Result.FSize and SizeMask // Make positive.
10124else
10125begin
10126InternalIncrement(PLimb(Result.FData), (Result.FSize and SizeMask));
10127Result.FSize := Result.FSize or SignMask; // Make negative.
10128end;
10129FreeMem(RPtr);
10130end
10131else
10132begin
10133LPtr := AllocLimbs(LSize);
10134CopyLimbs(PLimb(Left.FData), LPtr, LSize);
10135InternalDecrement(LPtr, LSize);
10136Result.FSize := 0;
10137if Addr(PlainOp) = Addr(InternalOr) then
10138Result.MakeSize(LSize)
10139else
10140Result.MakeSize(MaxSize);
10141InversionOp(PLimb(Right.FData), LPtr, PLimb(Result.FData), RSize, LSize);
10142if Addr(PlainOp) = Addr(InternalAnd) then
10143Result.FSize := Result.FSize and SizeMask
10144else
10145begin
10146InternalIncrement(PLimb(Result.FData), (Result.FSize and SizeMask));
10147Result.FSize := Result.FSize or SignMask;
10148end;
10149FreeMem(LPtr);
10150end;
10151end;
10152Result.Compact;
10153end;
10154
10155class function BigInteger.Negate(const Value: BigInteger): BigInteger;
10156begin
10157Result.FData := Value.FData;
10158Result.FSize := Value.FSize xor SignMask;
10159end;
10160
10161class operator BigInteger.Negative(const Value: BigInteger): BigInteger;
10162begin
10163// Magnitude is not modified, so a shallow copy is enough!
10164ShallowCopy(Value, Result);
10165if Result.FSize <> 0 then
10166Result.FSize := Result.FSize xor SignMask;
10167end;
10168
10169class function BigInteger.Parse(const S: string): BigInteger;
10170var
10171TryResult: BigInteger;
10172begin
10173if TryParse(S, TryResult) then
10174Result := TryResult
10175else
10176Error(ecParse, [S, 'BigInteger']);
10177end;
10178
10179
10180class function BigInteger.Pow(const ABase: BigInteger; AExponent: Integer): BigInteger;
10181begin
10182Pow(ABase, AExponent, Result);
10183end;
10184
10185class procedure BigInteger.Pow(const ABase: BigInteger; AExponent: Integer; var Result: BigInteger);
10186var
10187LBase: BigInteger;
10188LBaseBitLength: Integer;
10189LScaleFactor: Int64;
10190LBigResult: BigInteger;
10191LTrailingZeros: Integer;
10192LShift: Int64;
10193LNewSign: Integer;
10194LIntResult: Int64;
10195IntBase: Int64;
10196LExponent: Integer;
10197LResultIsNegative: Boolean;
10198begin
10199if AExponent < 0 then
10200Error(ecNegativeExponent, ['AExponent']);
10201
10202if ABase.IsZero then
10203if AExponent = 0 then
10204begin
10205ShallowCopy(BigInteger.One, Result);
10206Exit;
10207end
10208else
10209begin
10210ShallowCopy(ABase, Result);
10211Exit;
10212end;
10213
10214LResultIsNegative := ABase.IsNegative and Odd(AExponent);
10215
10216LBase := BigInteger.Abs(ABase);
10217
10218///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10219/// Speed things up by removing any common trailing zero bits. The resulting values will probably be smaller, ///
10220/// so exponentation is done with smaller values, and thus probably faster. The zero bits are added back in ///
10221/// (multiplied by the exponent, of course) at the very end. ///
10222///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10223
10224LTrailingZeros := LBase.LowestSetBit;
10225LShift := Int64(LTrailingZeros) * AExponent;
10226if LShift > High(Integer) then
10227Error(ecOverflow, []);
10228
10229if LTrailingZeros <> 0 then
10230begin
10231LBase := LBase shr LTrailingZeros;
10232LBaseBitLength := LBase.BitLength;
10233if LBaseBitLength = 1 then
10234begin
10235if LResultIsNegative then
10236begin
10237ShallowCopy(BigInteger.MinusOne shl (LTrailingZeros * AExponent), Result);
10238Exit;
10239end
10240else
10241begin
10242ShallowCopy(BigInteger.One shl (LTrailingZeros * AExponent), Result);
10243Exit;
10244end;
10245end;
10246end
10247else
10248begin
10249LBaseBitLength := LBase.BitLength;
10250if LBaseBitLength = 1 then
10251if LResultIsNegative then
10252begin
10253ShallowCopy(BigInteger.MinusOne, Result);
10254Exit;
10255end
10256else
10257begin
10258ShallowCopy(BigInteger.One, Result);
10259Exit;
10260end;
10261end;
10262
10263LScaleFactor := Int64(LBaseBitLength) * AExponent;
10264
10265if (LBase.Size = 1) and (LScaleFactor < 31) then
10266begin
10267// Small values.
10268LNewSign := 1;
10269if LResultIsNegative then
10270LNewSign := -1;
10271LIntResult := 1;
10272IntBase := LBase.Magnitude[0];
10273
10274//////////////////////////////////////////////////////////////////////////////////////////////////////////
10275/// The exponentiation proper: ///
10276/// ///
10277/// 1. Square the power for each iteration. So you get Base^1, Base^2, Base^4, Base^8, Base^16, etc. ///
10278/// 2. For each bit in the exponent, multiply with the corresponding (i.e. current value of) power ///
10279/// ///
10280/// Example: 7^11 = 7 ^ (8 + 2 + 1) = 7^1 * 7^2 * 7^8. ///
10281//////////////////////////////////////////////////////////////////////////////////////////////////////////
10282LExponent := AExponent;
10283while LExponent <> 0 do
10284begin
10285if Odd(LExponent) then
10286LIntResult := LIntResult * IntBase;
10287LExponent := LExponent shr 1;
10288if LExponent <> 0 then
10289IntBase := IntBase * IntBase;
10290end;
10291
10292///////////////////////////////////////////////////////////////////////////////////////////////////////////
10293/// Append the trailing zeroes (times exponent) back in, to get the real result. ///
10294///////////////////////////////////////////////////////////////////////////////////////////////////////////
10295if LTrailingZeros > 0 then
10296begin
10297if LShift + LScaleFactor < 31 then
10298Result := BigInteger(LIntResult shl LShift) // LIntResult shl Shift is in range of Integer.
10299else
10300Result := BigInteger(LIntResult) shl LShift; // slightly slower: BigInteger is shifted, not the integer.
10301if LResultIsNegative then
10302begin
10303ShallowCopy(-Result, Result);
10304Exit;
10305end
10306else
10307Exit;
10308end
10309else
10310begin
10311ShallowCopy(BigInteger(LIntResult * LNewSign), Result);
10312Exit;
10313end;
10314end
10315else
10316begin
10317// True BigIntegers.
10318LBigResult := BigInteger.One;
10319LExponent := AExponent;
10320
10321// The exponentiation proper. See explanation above.
10322while LExponent <> 0 do
10323begin
10324if Odd(LExponent) then
10325BigInteger.Multiply(LBigResult, LBase, LBigResult);
10326LExponent := LExponent shr 1;
10327if LExponent <> 0 then
10328LBase := Sqr(LBase);
10329end;
10330
10331// Append the trailing zeroes (times exponent) back in, to get the real result.
10332if LTrailingZeros > 0 then
10333LBigResult := LBigResult shl (LTrailingZeros * AExponent);
10334
10335if LResultIsNegative then
10336begin
10337ShallowCopy(-LBigResult, Result);
10338Exit;
10339end
10340else
10341begin
10342ShallowCopy(LBigResult, Result);
10343Exit;
10344end;
10345end;
10346end;
10347
10348class operator BigInteger.NotEqual(const Left, Right: BigInteger): Boolean;
10349begin
10350Result := Compare(Left, Right) <> 0;
10351end;
10352
10353class procedure BigInteger.Octal;
10354begin
10355FBase := 8;
10356end;
10357
10358class function BigInteger.Remainder(const Left: BigInteger; Right: UInt16): BigInteger;
10359var
10360LQuotient: TMagnitude;
10361begin
10362if Right = 0 then
10363Error(ecDivByZero, []);
10364Result.MakeSize(1);
10365SetLength(LQuotient, (Left.FSize and SizeMask));
10366InternalDivMod32(PLimb(Left.FData), Right, PLimb(LQuotient), PLimb(Result.FData), (Left.FSize and SizeMask));
10367Result.Compact;
10368if Result.FSize <> 0 then
10369Result.FSize := (Result.FSize and SizeMask) or SignBitOf(Left.FSize);
10370end;
10371
10372class function BigInteger.Remainder(const Left: BigInteger; Right: UInt32): BigInteger;
10373var
10374LQuotient: TMagnitude;
10375begin
10376if Right = 0 then
10377Error(ecDivByZero, []);
10378Result.MakeSize(1);
10379SetLength(LQuotient, (Left.FSize and SizeMask));
10380InternalDivMod32(PLimb(Left.FData), Right, PLimb(LQuotient), PLimb(Result.FData), (Left.FSize and SizeMask));
10381Result.Compact;
10382if Result.FSize <> 0 then
10383Result.FSize := (Result.FSize and SizeMask) or SignBitOf(Left.FSize);
10384end;
10385
10386class function BigInteger.Remainder(const Left, Right: BigInteger): BigInteger;
10387var
10388Quotient: BigInteger;
10389LSize, RSize: Integer;
10390begin
10391if Right.FData = nil then
10392Error(ecDivByZero, []);
10393
10394LSize := Left.FSize and SizeMask;
10395RSize := Right.FSize and SizeMask;
10396
10397case InternalCompare(PLimb(Left.FData), PLimb(Right.FData), LSize, RSize) of
10398-1:
10399begin
10400ShallowCopy(Left, Result);
10401Exit;
10402end;
104030:
10404begin
10405ShallowCopy(Zero, Result);
10406Exit;
10407end;
10408else
10409begin
10410if ShouldUseBurnikelZiegler(LSize, RSize) then
10411DivModBurnikelZiegler(Left, Right, Quotient, Result)
10412else
10413DivModKnuth(Left, Right, Quotient, Result);
10414
10415// In Delphi, sign of remainder is sign of dividend.
10416if Result.FSize <> 0 then
10417Result.FSize := (Result.FSize and SizeMask) or SignBitOf(Left.FSize);
10418end;
10419end;
10420end;
10421
10422{$IFNDEF BIGINTEGERIMMUTABLE}
10423function BigInteger.Remainder(const Other: BigInteger): PBigInteger;
10424begin
10425Result := @Self;
10426Self := Self mod Other;
10427end;
10428{$ENDIF}
10429
10430
10431class procedure BigInteger.ShiftRight(const Value: BigInteger; Shift: Integer; var Result: BigInteger);
10432
10433// Note: this emulates two's complement, more or less like the bitwise operators.
10434
10435/////////////////////////////////////////////////////////////////////////////////////////////////////////
10436// //
10437// If Value is a negative BigInteger, then the following applies: // //
10438// //
10439// - shift magnitude as for positive value //
10440// - if (Value < 0) and (Shift > Abs(Value).LowestSetBit()) then //
10441// Inc(Result.Magnitude); //
10442// - Make Result negative if necessary. //
10443// //
10444// This greatly simplifies the previous code for negative results. //
10445// //
10446/////////////////////////////////////////////////////////////////////////////////////////////////////////
10447
10448var
10449LSize, LShift: Integer;
10450ShiftOffset: Integer;
10451RSize: Integer;
10452Lowest: Integer;
10453// P: PLimb;
10454begin
10455if Value.FData = nil then
10456begin
10457ShallowCopy(Zero, Result);
10458Exit;
10459end;
10460
10461LSize := (Value.FSize and SizeMask);
10462ShiftOffset := Shift shr 5;
10463RSize := LSize - ShiftOffset;
10464
10465if RSize <= 0 then
10466
10467// Shift results in 0. But for negative values, result might be -1.
10468
10469begin
10470if (Value.FSize < 0) and (Shift > Value.LowestSetBit) then
10471ShallowCopy(MinusOne, Result)
10472else
10473ShallowCopy(Zero, Result);
10474Exit;
10475end;
10476
10477LShift := Shift and $1F;
10478Result.MakeSize(RSize);
10479if LShift > 0 then
10480InternalShiftRight(PLimb(Value.FData) + ShiftOffset, PLimb(Result.FData), LShift, RSize)
10481else
10482CopyLimbs(PLimb(Value.FData) + ShiftOffset, PLimb(Result.FData), RSize);
10483
10484// See comment box above. Handle negative values, if necessary.
10485
10486if Value.FSize < 0 then
10487begin
10488
10489// Simulate two's complement.
10490
10491Lowest := Value.LowestSetBit;
10492if Shift > Lowest then
10493InternalIncrement(PLimb(Result.FData), RSize);
10494Result.FSize := Result.FSize or SignMask;
10495end;
10496
10497Result.Compact;
10498end;
10499
10500class function BigInteger.ShiftRight(const Value: BigInteger; Shift: Integer): BigInteger;
10501begin
10502ShiftRight(Value, Shift, Result);
10503end;
10504
10505class operator BigInteger.RightShift(const Value: BigInteger; Shift: Integer): BigInteger;
10506begin
10507ShiftRight(Value, Shift, Result);
10508end;
10509
10510class operator BigInteger.Implicit(const Value: string): BigInteger;
10511begin
10512if not TryParse(Value, Result) then
10513Error(ecParse, [Value, 'BigInteger']);
10514end;
10515
10516{$IFNDEF NoAnsi}
10517class operator BigInteger.Implicit(const Value: PAnsiChar): BigInteger;
10518begin
10519if not TryParse(string(AnsiString(Value)), Result) then
10520Error(ecParse, [string(AnsiString(Value)), 'BigInteger']);
10521end;
10522{$ENDIF}
10523
10524class operator BigInteger.Implicit(const Value: PWideChar): BigInteger;
10525begin
10526if not TryParse(Value, Result) then
10527Error(ecParse, [Value, 'BigInteger']);
10528end;
10529
10530{$IFDEF HasExtended}
10531class operator BigInteger.Explicit(const Value: BigInteger): Extended;
10532begin
10533Result := Value.AsExtended;
10534end;
10535{$ENDIF}
10536
10537class operator BigInteger.Explicit(const Value: BigInteger): Double;
10538begin
10539Result := Value.AsDouble;
10540end;
10541
10542class operator BigInteger.Explicit(const Value: BigInteger): Single;
10543begin
10544Result := Value.AsSingle;
10545end;
10546
10547class operator BigInteger.Explicit(const Value: Double): BigInteger;
10548begin
10549Result.Create(Value);
10550end;
10551
10552class operator BigInteger.Explicit(const Value: BigInteger): string;
10553begin
10554Result := Value.ToString;
10555end;
10556
10557class operator BigInteger.Inc(const Value: BigInteger): BigInteger;
10558begin
10559if Value.FData = nil then
10560begin
10561ShallowCopy(One, Result);
10562Exit;
10563end;
10564Result.FData := Copy(Value.FData);
10565Result.FSize := Value.FSize;
10566if Result.FSize > 0 then
10567begin
10568Result.EnsureSize((Result.FSize and SizeMask) + 1);
10569InternalIncrement(PLimb(Result.FData), (Result.FSize and SizeMask));
10570end
10571else
10572InternalDecrement(PLimb(Result.FData), (Result.FSize and SizeMask));
10573Result.Compact;
10574end;
10575
10576class operator BigInteger.Dec(const Value: BigInteger): BigInteger;
10577begin
10578if Value.FData = nil then
10579begin
10580ShallowCopy(MinusOne, Result);
10581Exit;
10582end;
10583Result.FData := Copy(Value.FData);
10584Result.FSize := Value.FSize;
10585if Result.FSize < 0 then
10586begin
10587Result.EnsureSize((Result.FSize and SizeMask) + 1);
10588InternalIncrement(PLimb(Result.FData), (Result.FSize and SizeMask));
10589end
10590else
10591InternalDecrement(PLimb(Result.FData), (Result.FSize and SizeMask));
10592Result.Compact;
10593end;
10594
10595{$IFNDEF BIGINTEGERIMMUTABLE}
10596function BigInteger.Add(const Other: BigInteger): PBigInteger;
10597var
10598SelfSize, OtherSize: Integer;
10599Comparison: Integer;
10600begin
10601Result := @Self;
10602if Other.IsZero then
10603Exit;
10604if Self.IsZero then
10605begin
10606Self := Other;
10607Exit;
10608end;
10609FData := Copy(FData);
10610SelfSize := FSize and SizeMask;
10611OtherSize := Other.FSize and SizeMask;
10612if Self.IsNegative = Other.IsNegative then
10613begin
10614EnsureSize(IntMax(SelfSize, OtherSize) + 1);
10615FInternalAdd(PLimb(Self.FData), PLimb(Other.FData), PLimb(Self.FData), SelfSize, OtherSize);
10616end
10617else
10618begin
10619// Different signs, so subtract.
10620EnsureSize(IntMax(SelfSize, OtherSize));
10621Comparison := InternalCompare(PLimb(Self.FData), PLimb(Other.FData), (Self.FSize and SizeMask),
10622(Other.FSize and SizeMask));
10623if Comparison = 0 then
10624begin
10625Self := Zero;
10626Exit;
10627end;
10628
10629if Comparison > 0 then
10630begin
10631FInternalSubtract(PLimb(Self.FData), PLimb(Other.FData), PLimb(Self.FData), SelfSize, OtherSize);
10632end
10633else
10634begin
10635FInternalSubtract(PLimb(Other.FData), PLimb(Self.FData), PLimb(Self.FData), OtherSize, SelfSize);
10636Self.FSize := Self.FSize xor SignMask;
10637end;
10638end;
10639Compact;
10640end;
10641{$ENDIF}
10642
10643class procedure BigInteger.AvoidPartialFlagsStall(Value: Boolean);
10644{$IFDEF PUREPASCAL}
10645begin
10646FInternalAdd := InternalAddPurePascal;
10647FInternalSubtract := InternalSubtractPurePascal;
10648end;
10649{$ELSE}
10650begin
10651FAvoidStall := Value;
10652if Value then
10653begin
10654FInternalAdd := InternalAddModified;
10655FInternalSubtract := InternalSubtractModified;
10656end
10657else
10658begin
10659FInternalAdd := InternalAddPlain;
10660FInternalSubtract := InternalSubtractPlain;
10661end;
10662end;
10663{$ENDIF}
10664
10665{$IFNDEF BIGINTEGERIMMUTABLE}
10666function BigInteger.Multiply(const Other: BigInteger): PBigInteger;
10667begin
10668Result := @Self;
10669Self := Self * Other;
10670end;
10671{$ENDIF}
10672
10673procedure FlipBigIntegerBit(var B: BigInteger; Index: Integer); inline;
10674begin
10675B.FData := Copy(B.FData);
10676B.EnsureSize(IntMax(Index shr 5 + 1, B.FSize and BigInteger.SizeMask));
10677B.FData[Index shr 5] := B.FData[Index shr 5] xor (1 shl (Index and $1F));
10678B.Compact;
10679end;
10680
10681function BigInteger.TestBit(Index: Integer): Boolean;
10682
10683///////////////////////////////////////////////////////////////////////
10684/// Two's complement semantics are required. ///
10685/// ///
10686/// Note: -A = not (A - 1) = not A + 1 ///
10687/// ///
10688/// Example, assuming 16 bit limbs, negating goes like follows: ///
10689/// ///
10690/// -$1234 5678 9ABC 0000 0000 -> $EDCB A987 6544 0000 0000 ///
10691/// 0: 0000 -> FFFF + 1 ///
10692/// 1: 0000 -> FFFF + 1 ///
10693/// 2: 9ABC -> 6543 + 1 ///
10694/// 3: 5678 -> A987 ///
10695/// 4: 1234 -> EDCB ///
10696/// ///
10697/// So accessing limb 4 or 3: Data := not Data ///
10698/// accessing limb 2, 1 or 0: Data := not Data + 1 ///
10699///////////////////////////////////////////////////////////////////////
10700
10701var
10702I: Integer;
10703Mask: TLimb;
10704Data: TLimb;
10705begin
10706if FData = nil then
10707
10708// Zero, so no bit set. Return False.
10709Result := False
10710else if Index >= BitLength then
10711
10712// Beyond bit length, so return sign
10713Result := (FSize and SignMask) <> 0
10714else
10715begin
10716Mask := 1 shl (Index and $1F);
10717Index := Index shr 5;
10718Data := FData[Index];
10719
10720// Emulate negation if this BigInteger is negative.
10721// Not necessary if BigInteger is positive.
10722if (FSize and SignMask) <> 0 then
10723begin
10724
10725// -A = not A + 1.
10726Data := not Data; // Wait with the + 1, see below.
10727I := 0;
10728
10729// See if carry propagates from lowest limb to limb containing the bit. If so, increment Data.
10730while (I <= Index) and (FData[I] = 0) do
10731Inc(I);
10732if Index <= I then
10733Inc(Data);
10734end;
10735
10736// Get the bit.
10737Result := (Data and Mask) <> 0;
10738end;
10739end;
10740
10741function BigInteger.SetBit(Index: Integer): BigInteger;
10742var
10743LimbIndex: Integer;
10744BitMask, Borrow, Data: TLimb;
10745begin
10746Result := Self.Clone;
10747LimbIndex := Index shr 5;
10748BitMask := 1 shl (Index and 31);
10749
10750if Self.IsNegative then
10751begin
10752// If negative, every bit beyond the bit length is supposed to be set already (assuming two's complement), so
10753// no change.
10754if Index > Self.BitLength then
10755Exit;
10756
10757// No need to change the limbs below the index, so start at LimbIndex.
10758
10759// Negate this limb, set the bit, negate it again and store it back.
10760Data := Result.FData[LimbIndex];
10761Result.FData[LimbIndex] := -(-Data or BitMask);
10762Inc(LimbIndex);
10763
10764// If there was a borrow, it must be propagated.
10765Borrow := Ord(Data = 0);
10766if Borrow <> 0 then
10767while LimbIndex < (Result.FSize and SizeMask) do
10768begin
10769Data := Result.FData[LimbIndex];
10770Result.FData[LimbIndex] := Data - 1;
10771
10772// We can stop if the limb *wasn't* 0, since then there will be no borrow anymore.
10773if Data <> 0 then
10774Break
10775else
10776Inc(LimbIndex);
10777end;
10778end
10779else
10780begin
10781// If the bit is beyond the bit length, the size must be expanded.
10782if (Index > Self.BitLength) or Self.IsZero then
10783Result.EnsureSize(LimbIndex + 1);
10784
10785// Set the bit.
10786Result.FData[LimbIndex] := Result.FData[LimbIndex] or BitMask;
10787end;
10788Result.Compact;
10789end;
10790
10791function BigInteger.ClearBit(Index: Integer): BigInteger;
10792var
10793LimbIndex: Integer;
10794BitMask, Borrow, Data: TLimb;
10795begin
10796Result := Self.Clone;
10797LimbIndex := Index shr 5;
10798BitMask := 1 shl (Index and 31);
10799
10800if Self.IsNegative then
10801begin
10802if Index > Self.BitLength then
10803begin
10804Result.EnsureSize(LimbIndex + 1);
10805Result.FData[LimbIndex] := Result.FData[LimbIndex] or BitMask;
10806end
10807else
10808begin
10809Data := Result.FData[LimbIndex];
10810Result.FData[LimbIndex] := TLimb(-(-Data and not BitMask));
10811Inc(LimbIndex);
10812
10813// Propagate borrow
10814Borrow := Ord(Data = 0);
10815if Borrow > 0 then
10816while LimbIndex < Result.FSize and SizeMask do
10817begin
10818Data := Result.FData[LimbIndex];
10819Dec(Result.FData[LimbIndex]);
10820if Data <> 0 then
10821Break
10822else
10823Inc(LimbIndex);
10824end;
10825end;
10826end
10827else
10828begin
10829if Index > BitLength then
10830Exit;
10831Result.FData[LimbIndex] := Result.FData[LimbIndex] and not BitMask;
10832end;
10833Result.Compact;
10834end;
10835
10836function BigInteger.FlipBit(Index: Integer): BigInteger;
10837var
10838LimbIndex: Integer;
10839BitMask, Borrow, Data: TLimb;
10840begin
10841Result := Self.Clone;
10842LimbIndex := Index shr 5;
10843BitMask := 1 shl (Index and 31);
10844
10845if Self.IsNegative then
10846begin
10847if Index > Self.BitLength then
10848begin
10849Result.EnsureSize(LimbIndex + 1);
10850Result.FData[LimbIndex] := Result.FData[LimbIndex] xor BitMask;
10851end
10852else
10853begin
10854Data := Result.FData[LimbIndex];
10855Result.FData[LimbIndex] := -(-Data xor BitMask);
10856Inc(LimbIndex);
10857
10858// Propagate borrow
10859Borrow := Ord(Data = 0);
10860if Borrow > 0 then
10861while LimbIndex < Result.FSize and SizeMask do
10862begin
10863Data := Result.FData[LimbIndex];
10864Dec(Result.FData[LimbIndex]);
10865if Data <> 0 then
10866Break
10867else
10868Inc(LimbIndex);
10869end;
10870end;
10871end
10872else
10873begin
10874if (Index > BitLength) or Self.IsZero then
10875Result.EnsureSize(LimbIndex + 1);
10876Result.FData[LimbIndex] := Result.FData[LimbIndex] xor BitMask;
10877end;
10878Result.Compact;
10879end;
10880
10881class function BigInteger.NthRoot(const Radicand: BigInteger; Index: Integer): BigInteger;
10882var
10883PredIndex: Integer;
10884BigIndex, BigPredIndex: Integer;
10885Newestimate, PrevEstimate: BigInteger;
10886begin
10887if Radicand.IsZero or Radicand.IsOne then
10888Exit(Radicand);
10889if Radicand.IsNegative then
10890Error(ecNegativeRadicand, ['NthRoot']);
10891case Index of
108920: Exit(BigInteger.Zero);
108931: Exit(Radicand);
108942: Exit(BaseCaseSqrt(Radicand));
10895end;
10896if Index < 0 then
10897Error(ecNegativeExponent, ['NthRoot']);
10898PredIndex := System.Pred(Index);
10899Result := BigInteger.Zero.SetBit(Radicand.BitLength div Index);
10900PrevEstimate := Result;
10901
10902// Loop invariants
10903BigIndex := Index;
10904BigPredIndex := PredIndex;
10905
10906// Newton-Raphson approximation loop, similar to code in Sqrt().
10907repeat
10908NewEstimate := (Result * BigPredIndex + Radicand div BigInteger.Pow(Result, PredIndex)) div BigIndex;
10909// Loop until no difference with previous value or detect end of a cycle.
10910
10911////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10912/// Unfortunately, the true root is only detected when the cycle starts repeating, i.e. at the end of the cycle. ///
10913/// That means that this routine can be slower if there is a cycle. Otherwise, it is fast. ///
10914////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10915if (Result = NewEstimate) or ((Result < NewEstimate) and (Result < PrevEstimate)) then
10916Exit(Result);
10917PrevEstimate := Result;
10918Result := NewEstimate;
10919until False;
10920end;
10921
10922class procedure BigInteger.NthRootRemainder(const Radicand: BigInteger; Index: Integer; var Root, Remainder: BigInteger);
10923begin
10924Root := NthRoot(Radicand, Index);
10925Remainder := Radicand - Pow(Root, Index);
10926end;
10927
10928class function BigInteger.Sqr(const Value: BigInteger): BigInteger;
10929begin
10930if (Value.FSize and SizeMask) < KaratsubaSqrThreshold then
10931Result := Value * Value
10932else
10933Result := SqrKaratsuba(Value);
10934end;
10935
10936//////////////////////////////////////////////////////////////////////////////////////////////////
10937/// A Newton-Raphson algorithm is *much* faster than the previous binary-search like ///
10938/// algorithm. ///
10939/// ///
10940/// This new N-R algorithm is even faster than the previous and correct. The previous one ///
10941/// could go into an endless loop when the estimate flipped continuously between the root and ///
10942/// root+1, which is something that doesn't happen often, but can occur, e.g. for a radicand ///
10943/// with the value 8. ///
10944/// ///
10945/// https://stackoverflow.com/questions/4407839#16804098 ///
10946//////////////////////////////////////////////////////////////////////////////////////////////////
10947class function BigInteger.BaseCaseSqrt(const Radicand: BigInteger): BigInteger;
10948var
10949PrevEstimate, NewEstimate: BigInteger;
10950begin
10951if Radicand.IsOne or Radicand.IsZero then
10952Exit(Radicand);
10953if Radicand.IsNegative then
10954Error(ecNegativeRadicand, ['Sqrt']); // Do not translate!
10955Result := Radicand shr (Radicand.BitLength shr 1);
10956PrevEstimate := Result;
10957// Loop until we hit the same value twice in a row, or wind up alternating.
10958repeat
10959NewEstimate := (Result + Radicand div Result) shr 1;
10960if (NewEstimate = Result) or // normal case
10961(NewEstimate = PrevEstimate) then // alternating case
10962Exit(BigInteger.Min(Result, NewEstimate));
10963PrevEstimate := Result;
10964Result := NewEstimate;
10965until False;
10966end;
10967
10968class procedure BigInteger.BaseCaseSqrtRemainder(const Radicand: BigInteger; var Root, Remainder: BigInteger);
10969begin
10970Root := BaseCaseSqrt(Radicand);
10971Remainder := Radicand - Sqr(Root);
10972end;
10973
10974class function BigInteger.Sqrt(const Radicand: BigInteger): BigInteger;
10975var
10976Rem: BigInteger;
10977begin
10978SqrtRemainder(Radicand, Result, Rem);
10979end;
10980
10981// Richard P. Brent and Paul Zimmermann, "Modern Computer Arithmetic", Algorithm 1.12
10982// Produces square root and square root remainder in one go.
10983// Extremely fast, much faster than Newton-Raphson (as used in BaseCaseSqrtRemainder), even for relatively
10984// small sizes.
10985class procedure BigInteger.SqrtRemainder(const Radicand: BigInteger; var Root, Remainder: BigInteger);
10986var
10987RadCopy: BigInteger;
10988Limbs: Integer;
10989BaseToL, BaseMask: BigInteger;
10990A3, A2, A1, A0: BigInteger;
10991RootQ, RemQ: BigInteger;
10992Quot, Rem: BigInteger;
10993begin
10994// Note: if the threshold is too small, a stack overflow will occur.
10995if Radicand.Size < 10 then
10996begin
10997BaseCaseSqrtRemainder(Radicand, Root, Remainder);
10998Exit;
10999end;
11000
11001// l = trunc((n - 1) / 4)
11002Limbs := (Radicand.Size - 1) div 4;
11003
11004// if l = 0 then return BaseCaseSqrtRem(m) <-- See above: there is a threshold > 0
11005
11006BaseToL := BigInteger.One shl (CLimbBits * Limbs);
11007BaseMask := BaseToL - 1;
11008
11009// Write m = a3*beta^3*l + a2*beta^2*l + a1*beta^l + a0 with 0 <= a2, a1, a0 < beta^l
11010A0 := Radicand and BaseMask;
11011RadCopy := Radicand shr (CLimbBits * Limbs);
11012A1 := RadCopy and BaseMask;
11013RadCopy := RadCopy shr (CLimbBits * Limbs);
11014A2 := RadCopy and BaseMask;
11015A3 := RadCopy shr (CLimbBits * Limbs);
11016
11017// (s^', r') <-- SqrtRem(a3*beta^l + a2)
11018BigInteger.SqrtRemainder(A3 * BaseToL + A2, RootQ, RemQ);
11019
11020// (q, u) <-- DivRem(r'*beta^l + a1, 2*s')
11021BigInteger.DivMod(RemQ * BaseToL + A1, RootQ shl 1, Quot, Rem);
11022
11023// s <-- s'*beta^l + q
11024Root := RootQ * BaseToL + Quot;
11025
11026// r <-- u*beta^l + a0 - q^2
11027Remainder := Rem * BaseToL + A0 - BigInteger.Sqr(Quot);
11028
11029// if r < 0 then
11030if Remainder < 0 then
11031begin
11032// r <-- r + 2*s - 1
11033Remainder := Remainder + 2 * Root - 1;
11034
11035// s <-- s - 1
11036Root := Root - 1;
11037end;
11038
11039// return (s, r)
11040end;
11041
11042class procedure BigInteger.DivThreeHalvesByTwo(const LeftUpperMid, LeftLower, Right, RightUpper: BigInteger;
11043const RightLower: BigInteger; N: Integer;
11044var Quotient, Remainder: BigInteger);
11045var
11046Q, R: BigInteger;
11047begin
11048if RightLower.FData <> nil then
11049;
11050Q := BigInteger.Zero;
11051R := BigInteger.Zero;
11052if (LeftUpperMid shr N) = RightUpper then
11053begin
11054Q := (BigInteger.One shl N) - BigInteger.One;
11055R := LeftUpperMid - (RightUpper shl N) + RightUpper;
11056end
11057else
11058DivTwoDigitsByOne(LeftUpperMid, RightUpper, N, Q, R);
11059
11060Quotient := Q;
11061Remainder := ((R shl N) or LeftLower) - Q * RightLower;
11062while Remainder < 0 do
11063begin
11064Dec(Quotient);
11065Remainder := Remainder + Right;
11066end;
11067end;
11068
11069
11070class procedure BigInteger.DivTwoDigitsByOne(const Left, Right: BigInteger; N: Integer;
11071var Quotient, Remainder: BigInteger);
11072var
11073NIsOdd: Boolean;
11074LeftCopy, RightCopy: BigInteger;
11075HalfN: Integer;
11076HalfMask: BigInteger;
11077RightUpper, RightLower: BigInteger;
11078QuotientUpper, QuotientLower: BigInteger;
11079Quot, Rem: BigInteger;
11080begin
11081Quot := BigInteger.Zero;
11082Rem := BigInteger.Zero;
11083if N <= BigInteger.BurnikelZieglerThreshold * CLimbBits then
11084begin
11085BigInteger.DivModKnuth(Left, Right, Quot, Rem);
11086Quotient := Quot;
11087Remainder := Rem;
11088Exit;
11089end;
11090
11091NIsOdd := Odd(N);
11092if NIsOdd then
11093begin
11094LeftCopy := Left shl 1;
11095RightCopy := Right shl 1;
11096Inc(N);
11097end
11098else
11099begin
11100LeftCopy := Left;
11101RightCopy := Right;
11102end;
11103HalfN := N shr 1;
11104HalfMask := (BigInteger.One shl HalfN) - BigInteger.One;
11105
11106RightUpper := RightCopy shr HalfN;
11107RightLower := RightCopy and HalfMask;
11108
11109DivThreeHalvesByTwo(LeftCopy shr N, (LeftCopy shr HalfN) and HalfMask, RightCopy, RightUpper,
11110RightLower, HalfN, QuotientUpper, Rem);
11111DivThreeHalvesByTwo(Rem, LeftCopy and HalfMask, RightCopy, RightUpper,
11112RightLower, HalfN, QuotientLower, Rem);
11113
11114/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11115/// ///
11116/// Grade school division, but with (very) large digits, dividing [a1,a2,a3,a4] by [b1,b2]: ///
11117/// ///
11118/// +----+----+----+----+ +----+----+ +----+ ///
11119/// | a1 | a2 | a3 | a4 | / | b1 | b2 | = | q1 | DivideThreeHalvesByTwo(a1a2, a3, b1b2, n, q1, r1r2) ///
11120/// +----+----+----+----+ +----+----+ +----+ ///
11121/// +--------------+ | | ///
11122/// | b1b2 * q1 | | | ///
11123/// +--------------+ | | ///
11124/// - ================ v | ///
11125/// +----+----+----+ +----+----+ | +----+ ///
11126/// | r1 | r2 | a4 | / | b1 | b2 | = | | q2 | DivideThreeHalvesByTwo(r1r2, a4, b1b2, n, q1, r1r2) ///
11127/// +----+----+----+ +----+----+ | +----+ ///
11128/// +--------------+ | | ///
11129/// | b1b2 * q2 | | | ///
11130/// +--------------+ | | ///
11131/// - ================ v v ///
11132/// +----+----+ +----+----+ ///
11133/// | r1 | r2 | | q1 | q2 | r1r2 = a1a2a3a4 mod b1b2, q1q2 = a1a2a3a4 div b1b2 ///
11134/// +----+----+ +----+----+ , ///
11135/// ///
11136/// Note: in the diagram above, a1, b1, q1, r1 etc. are the most significant "digits" of their numbers. ///
11137/// ///
11138/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11139
11140if NIsOdd then
11141Rem := Rem shr 1;
11142Remainder := Rem;
11143Quotient := (QuotientUpper shl HalfN) or QuotientLower;
11144end;
11145
11146class procedure BigInteger.InternalDivModBurnikelZiegler(const Left, Right: BigInteger;
11147var Quotient, Remainder: BigInteger);
11148var
11149LCopy: BigInteger;
11150N: Integer;
11151DigitMask: BigInteger;
11152LeftDigits: TArray<BigInteger>;
11153NumDigits: Integer;
11154QuotientDigit: BigInteger;
11155DigitIndex: Integer;
11156begin
11157LCopy := Left;
11158N := Right.BitLength;
11159
11160// A digit has the same bit length as right has, so this is the number of digits that must be allocated.
11161NumDigits := (Left.BitLength + N - 1) div N;
11162SetLength(LeftDigits, NumDigits);
11163
11164// Split Left into a number of digits of the same bitsize as Right, and collect them in LeftDigits.
11165DigitIndex := 0;
11166DigitMask := (BigInteger.One shl N) - BigInteger.One;
11167while not LCopy.IsZero do
11168begin
11169LeftDigits[DigitIndex] := LCopy and DigitMask;
11170LCopy := LCopy shr N;
11171Inc(DigitIndex);
11172end;
11173// Make DigitIndex point to top of "stack"
11174Dec(DigitIndex);
11175
11176// Remainder is the top digit of the "two digits" that are to be divided by "one".
11177if LeftDigits[DigitIndex] >= Right then
11178Remainder := BigInteger.Zero
11179else
11180begin
11181Remainder := LeftDigits[DigitIndex];
11182Dec(DigitIndex);
11183end;
11184
11185// Clear QuotientDigit and Quotient.
11186QuotientDigit := BigInteger.Zero;
11187Quotient := BigInteger.Zero;
11188
11189// Repeatedly divide two digits by the right digit and shift the resulting quotient digit into the final quotient.
11190while DigitIndex >= 0 do
11191begin
11192DivTwoDigitsByOne((Remainder shl N) + LeftDigits[DigitIndex], Right, N, QuotientDigit, Remainder);
11193Dec(DigitIndex);
11194Quotient := (Quotient shl N) + QuotientDigit;
11195end;
11196end;
11197
11198class procedure BigInteger.DivModBurnikelZiegler(const Left, Right: BigInteger; var Quotient, Remainder: BigInteger);
11199var
11200Q, R: BigInteger;
11201begin
11202
11203if Right.IsZero then
11204raise Exception.Create('Division by zero')
11205else if Right.IsNegative then
11206begin
11207DivModBurnikelZiegler(-Left, -Right, Q, R);
11208Quotient := Q;
11209Remainder := -R;
11210Exit;
11211end
11212else if Left.IsNegative then
11213begin
11214DivModBurnikelZiegler(not Left, Right, Q, R);
11215Quotient := not Q;
11216Remainder := Right + not R;
11217Exit;
11218end
11219else if Left.IsZero then
11220begin
11221Quotient := BigInteger.Zero;
11222Remainder := BigInteger.Zero;
11223Exit;
11224end
11225else
11226begin
11227InternalDivModBurnikelZiegler(Left, Right, Q, R);
11228Quotient := Q;
11229Remainder := R;
11230Exit;
11231end;
11232end;
11233
11234end.
11235