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