MathgeomGLS
809 строк · 20.0 Кб
1{---------------------------------------------------------------------------}
2{ }
3{ File: Velthuis.Numerics.pas }
4{ Function: Integer tool functions. }
5{ Language: Delphi version XE3 or later }
6{ Author: Rudy Velthuis }
7{ Copyright: (c) 2016 Rudy Velthuis }
8{ }
9{ License: Redistribution and use in source and binary forms, with or }
10{ without modification, are permitted provided that the }
11{ following conditions are met: }
12{ }
13{ * Redistributions of source code must retain the above }
14{ copyright notice, this list of conditions and the following }
15{ disclaimer. }
16{ * Redistributions in binary form must reproduce the above }
17{ copyright notice, this list of conditions and the following }
18{ disclaimer in the documentation and/or other materials }
19{ provided with the distribution. }
20{ }
21{ Disclaimer: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" }
22{ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT }
23{ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND }
24{ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO }
25{ EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE }
26{ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, }
27{ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
28{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, }
29{ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED }
30{ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT }
31{ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) }
32{ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF }
33{ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. }
34{ }
35{---------------------------------------------------------------------------}
36
37unit Velthuis.Numerics;38
39interface
40
41// For Delphi XE3 and up:
42{$IF CompilerVersion >= 24.0 }
43{$LEGACYIFEND ON}44{$IFEND}
45
46// For Delphi XE and up:
47{$IF CompilerVersion >= 22.0}
48{$CODEALIGN 16}49{$ALIGN 16}50{$IFEND}
51
52{$INLINE AUTO}
53
54uses
55System.Math;56
57// Return the number of set (1) bits in the given integers.
58function BitCount(U: UInt8): Integer; overload;59function BitCount(U: UInt16): Integer; overload;60function BitCount(S: Int32): Integer; overload;61function BitCount(U: UInt32): Integer; overload;62function BitCount(S: Int64): Integer; overload;63function BitCount(S: UInt64): Integer; overload;64
65// Return the number of significant bits, excluding the sign bit.
66function BitLength(S: Int32): Integer; overload;67function BitLength(U: UInt32): Integer; overload;68function BitLength(S: Int64): Integer; overload;69function BitLength(U: UInt64): Integer; overload;70
71// Return the number of significant digits.
72function DigitCount(S: Int32): Int32; overload;73function DigitCount(U: UInt32): UInt32; overload;74
75// Return an integer value with at most a single one-bit, in the position
76// of the most significant one-bit in the specified integer value.
77function HighestOneBit(S: Int32): Int32; overload;78function HighestOneBit(U: UInt32): UInt32; overload;79
80// Checks if the given integer is a power of two.
81function IsPowerOfTwo(S: Int32): Boolean; overload;82function IsPowerOfTwo(U: UInt32): Boolean; overload;83
84// Return an integer value with at most a single one-bit, in the position
85// of the least significant one-bit in the given integers value.
86function LowestOneBit(S: Int32): Int32; overload;87function LowestOneBit(U: UInt32): UInt32; overload;88
89// Return the number of leading (high order) zero-bits (excluding the sign bit) of
90// the given integers.
91function NumberOfLeadingZeros(U: UInt16): Integer; overload;92function NumberOfLeadingZeros(S: Int32): Integer; overload;93function NumberOfLeadingZeros(U: UInt32): Integer; overload;94function NumberOfLeadingZeros(S: Int64): Integer; overload;95function NumberOfLeadingZeros(U: UInt64): Integer; overload;96
97// Return the number of trailing (low order) zero-bits of the given integers.
98function NumberOfTrailingZeros(U: UInt32): Integer; overload;99function NumberOfTrailingZeros(U: UInt64): Integer; overload;100
101// Reverse the bits of the given integers.
102function Reverse(U: UInt8): UInt8; overload;103function Reverse(U: UInt16): UInt16; overload;104function Reverse(S: Int32): Int32; overload;105function Reverse(U: UInt32): UInt32; overload;106
107// Reverse the bytes of the given integers.
108function ReverseBytes(S: Int32): Int32; overload;109function ReverseBytes(U: UInt32): UInt32; overload;110
111// Rotate the given integers left by Distance bits.
112function RotateLeft(S: Int32; Distance: Integer): Int32; overload;113function RotateLeft(U: UInt32; Distance: Integer): UInt32; overload;114
115// Rotate the given integers right by Distance bits.
116function RotateRight(S: Int32; Distance: Integer): Int32; overload;117function RotateRight(U: UInt32; Distance: Integer): UInt32; overload;118
119// Returns the sign of the integer: -1 for negative, 0 for zero and 1 for positive.
120function Sign(S: Int32): TValueSign;121
122// Return a binary representation of the given integers.
123function ToBinaryString(S: Int32): string; overload;124function ToBinaryString(U: UInt32): string; overload;125
126// Return a hexadecimal representation of the given integers.
127function ToHexString(S: Int32): string; overload;128function ToHexString(U: UInt32): string; overload;129
130// Return an octal representation of the given integers.
131function ToOctalString(S: Int32): string; overload;132function ToOctalString(U: UInt32): string; overload;133
134// Return a string representation of the given integers, in the given numerical base.
135function ToString(S: Int32; Base: Byte): string; overload;136function ToString(U: UInt32; Base: Byte): string; overload;137function ToString(S: Int32): string; overload;138function ToString(U: UInt32): string; overload;139
140// Compare the given integers and return -1 for less, 0 for equal and 1 for greater.
141function Compare(Left, Right: Int32): Integer; overload;142function Compare(Left, Right: UInt32): Integer; overload;143function Compare(Left, Right: Int64): Integer; overload;144function Compare(Left, Right: UInt64): Integer; overload;145
146// Calculate a hash code for the given integers.
147function HashCode(Value: Int32): UInt32; overload;148function HashCode(Value: UInt32): UInt32; overload;149function HashCode(Value: Int64): UInt32; overload;150function HashCode(Value: UInt64): UInt32; overload;151
152implementation
153
154
155uses
156System.SysUtils, Velthuis.StrConsts;157
158// https://en.wikipedia.org/wiki/Find_first_set
159
160const
161// Currently not used.162NLZDeBruijn32Mult = $07C4ACDD;163NLZDeBruijn32: array[0..31] of Byte =164(16531, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1,16623, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0167);168
169NTZDeBruijn32Mult = $077CB531;170NTZDeBruijn32: array[0..31] of Byte =171(1720, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,17331, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9174);175
176BitCounts: array[0..15] of Byte = (0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);177
178function BitCount(U: UInt8): Integer;179begin
180Result := BitCounts[U and $0F] + BitCounts[U shr 4];181end;182
183function BitCount(U: UInt16): Integer;184{$IF DEFINED(WIN32)}
185asm
186MOV DX,AX187SHR DX,1188AND DX,$5555189SUB AX,DX190MOV DX,AX191AND AX,$3333192SHR DX,2193AND DX,$3333194ADD AX,DX195MOV DX,AX196SHR DX,4197ADD AX,DX198AND AX,$0F0F199MOV DX,AX200SHR AX,8201ADD AX,DX202AND EAX,$7F203end;204{$ELSEIF DEFINED(WIN64)}
205asm
206.NOFRAME207
208MOV AX,CX209SHR CX,1210AND CX,$5555211SUB AX,CX212MOV CX,AX213AND AX,$3333214SHR CX,2215AND CX,$3333216ADD AX,CX217MOV CX,AX218SHR CX,4219ADD AX,CX220AND AX,$0F0F221MOV CX,AX222SHR AX,8223ADD AX,CX224AND EAX,$7F225end;226{$ELSE PUREPASCAL}
227begin
228U := U - ((U shr 1) and $5555);229U := (U and $3333) + ((U shr 2) and $3333);230U := (U + (U shr 4)) and $0F0F;231U := U + (U shr 8);232Result := U and $7F;233end;234{$IFEND PUREPASCAL}
235
236function BitCount(S: Int32): Integer;237begin
238Result := BitCount(UInt32(S));239end;240
241// Faster than 16 bit table lookups
242function BitCount(U: UInt32): Integer;243{$IF DEFINED(WIN32)}
244asm
245MOV EDX,EAX246SHR EDX,1247AND EDX,$55555555248SUB EAX,EDX249MOV EDX,EAX250AND EAX,$33333333251SHR EDX,2252AND EDX,$33333333253ADD EAX,EDX254MOV EDX,EAX255SHR EDX,4256ADD EAX,EDX257AND EAX,$0F0F0F0F258MOV EDX,EAX259SHR EAX,8260ADD EAX,EDX261MOV EDX,EAX262SHR EDX,16263ADD EAX,EDX264AND EAX,$7F265end;266{$ELSEIF DEFINED(WIN64)}
267asm
268.NOFRAME269
270MOV EAX,ECX271SHR ECX,1272AND ECX,$55555555273SUB EAX,ECX274MOV ECX,EAX275AND EAX,$33333333276SHR ECX,2277AND ECX,$33333333278ADD EAX,ECX279MOV ECX,EAX280SHR ECX,4281ADD EAX,ECX282AND EAX,$0F0F0F0F283MOV ECX,EAX284SHR EAX,8285ADD EAX,ECX286MOV ECX,EAX287SHR ECX,16288ADD EAX,ECX289AND EAX,$7F290end;291{$ELSE PUREPASCAL}
292begin
293U := U - ((U shr 1) and $55555555);294U := (U and $33333333) + ((U shr 2) and $33333333);295U := (U + (U shr 4)) and $0F0F0F0F;296U := U + (U shr 8);297U := U + (U shr 16);298Result := U and $7F;299end;300{$IFEND PUREPASCAL}
301
302function BitCount(S: Int64): Integer; overload;303begin
304Result := BitCount(UInt32(S)) + BitCount(Int32(S shr 32));305end;306
307function BitCount(S: UInt64): Integer; overload;308begin
309Result := BitCount(UInt32(S)) + BitCount(UInt32(S shr 32));310end;311
312function BitLength(S: Int32): Integer;313begin
314Result := BitLength(UInt32(S));315end;316
317function BitLength(U: UInt32): Integer;318begin
319Result := 32 - NumberOfLeadingZeros(U);320end;321
322function BitLength(S: Int64): Integer;323begin
324Result := 64 - NumberOfLeadingZeros(S);325end;326
327function BitLength(U: UInt64): Integer;328begin
329Result := 64 - NumberOfLeadingZeros(U);330end;331
332function DigitCount(S: Int32): Int32; overload;333begin
334if S <> Low(Int32) then335Result := DigitCount(UInt32(Abs(S)))336else337Result := 9;338end;339
340function DigitCount(U: UInt32): UInt32; overload;341begin
342Result := 1;343if U >= 100000000 then344begin345Inc(Result, 8);346U := U div 100000000;347end;348if U >= 10000 then349begin350Inc(Result, 4);351U := U div 10000;352end;353if U >= 100 then354begin355Inc(Result, 2);356U := U div 100;357end;358if U >= 10 then359Inc(Result);360end;361
362function IsPowerOfTwo(S: Int32): Boolean;363begin
364if S <> Low(Int32) then365Result := IsPowerofTwo(UInt32(Abs(S)))366else367Result := True;368end;369
370function IsPowerOfTwo(U: UInt32): Boolean;371begin
372Result := (U and (U - 1)) = 0;373end;374
375function HighestOneBit(S: Int32): Int32;376begin
377Result := Int32(HighestOneBit(UInt32(S)));378end;379
380function HighestOneBit(U: UInt32): UInt32;381begin
382if U = 0 then383Result := 0384else385Result := UInt32(1) shl (31 - NumberOfLeadingZeros(U));386end;387
388function LowestOneBit(S: Int32): Int32;389begin
390Result := Int32(LowestOneBit(UInt32(S)));391end;392
393function LowestOneBit(U: UInt32): UInt32;394begin
395Result := U and -Int32(U);396end;397
398function NumberOfLeadingZeros(U: UInt16): Integer;399{$IF DEFINED(WIN32)}
400asm
401MOVZX EAX,AX402BSR EDX,EAX403JNZ @Invert
404MOV EAX,16405RET
406
407@Invert:408
409MOV EAX,15410SUB EAX,EDX411end;412{$ELSEIF DEFINED(WIN64)}
413asm
414.NOFRAME415
416MOVZX EAX,CX417BSR ECX,EAX418JNZ @Invert
419MOV EAX,16420RET
421
422@Invert:423
424MOV EAX,15425SUB EAX,ECX426end;427{$ELSE PUREPASCAL}
428begin
429if U = 0 then430Result := 16431else432begin433Result := 0;434if U <= High(Word) shr 8 then435begin436Result := Result + 8;437U := U shl 8;438end;439if U <= High(Word) shr 4 then440begin441Result := Result + 4;442U := U shl 4;443end;444if U <= High(Word) shr 2 then445begin446Result := Result + 2;447U := U shl 2;448end;449if U <= High(Word) shr 1 then450Result := Result + 1;451end;452end;453{$IFEND PUREPASCAL}
454
455function NumberOfLeadingZeros(S: Int32): Integer;456begin
457Result := NumberOfLeadingZeros(UInt32(Abs(S)));458end;459
460function NumberOfLeadingZeros(U: UInt32): Integer;461{$IF DEFINED(WIN32)}
462asm
463BSR EDX,EAX464JNZ @Invert
465MOV EAX,32466RET
467
468@Invert:469
470MOV EAX,31471SUB EAX,EDX472
473@Exit:474end;475{$ELSEIF DEFINED(WIN64)}
476asm
477.NOFRAME478
479BSR EDX,ECX480JNZ @Invert
481MOV EAX,32482RET
483
484@Invert:485
486MOV EAX,31487SUB EAX,EDX488
489@Exit:490end;491{$ELSE PUREPASCAL}
492
493// Faster than X := X or X shr 1..16; Result := NLZDeBruijn32[...];
494
495begin
496if U = 0 then497Result := 32498else499begin500Result := 0;501if U <= High(Cardinal) shr 16 then502begin503Result := Result + 16;504U := U shl 16;505end;506if U <= High(Cardinal) shr 8 then507begin508Result := Result + 8;509U := U shl 8;510end;511if U <= High(Cardinal) shr 4 then512begin513Result := Result + 4;514U := U shl 4;515end;516if U <= High(Cardinal) shr 2 then517begin518Result := Result + 2;519U := U shl 2;520end;521if U <= High(Cardinal) shr 1 then522Result := Result + 1;523end;524end;525{$IFEND PUREPASCAL}
526
527function NumberOfLeadingZeros(S: Int64): Integer;528begin
529Result := NumberOfLeadingZeros(UInt64(Abs(S)));530end;531
532function NumberOfLeadingZeros(U: UInt64): Integer;533begin
534if U = 0 then535Exit(1);536if U <= High(UInt32) then537Result := NumberOfLeadingZeros(UInt32(U)) + 32538else539Result := NumberOfLeadingZeros(UInt32(U shr 32));540end;541
542// Faster than NumberOfTrailingZeros2().
543function NumberOfTrailingZeros(U: UInt32): Integer;544{$IF DEFINED(WIN32)}
545asm
546BSF EAX,EAX547JNZ @Exit
548MOV EAX,32549
550@Exit:551end;552{$ELSEIF DEFINED(WIN64)}
553asm
554.NOFRAME555
556BSF EAX,ECX557JNZ @Exit
558MOV EAX,32559
560@Exit:561end;562{$ELSE PUREPASCAL}
563begin
564if U = 0 then565Result := 32566else567Result := NTZDeBruijn32[((U and (-Integer(U))) * NTZDeBruijn32Mult) shr 27];568end;569{$IFEND PUREPASCAL}
570
571function NumberOfTrailingZeros(U: UInt64): Integer;572{$IF DEFINED(WIN32)}
573asm
574BSF EAX,DWORD PTR [U]575JNZ @Exit
576BSF EAX,DWORD PTR [U+TYPE DWORD]577JZ @Ret64
578ADD EAX,32579JMP @Exit
580@Ret64:581MOV EAX,64582@Exit:583end;584{$ELSEIF DEFINED(WIN64)}
585asm
586.NOFRAME587
588BSF RAX,RCX589JNZ @Exit
590MOV EAX,64591@Exit:592end;593{$ELSE PUREPASCAL}
594type
595TUInt64 = packed record596Lo, Hi: UInt32;597end;598begin
599if UInt32(U) = 0 then600Result := 32 + NumberOfTrailingZeros(TUInt64(U).Hi)601else602Result := NumberOfTrailingZeros(UInt32(U));603end;604{$IFEND PUREPASCAL}
605
606function Reverse(U: UInt8): UInt8;607begin
608U := ((U shr 1) and $55) or ((U and $55) shl 1);609U := ((U shr 2) and $33) or ((U and $33) shl 2);610U := (U shr 4) or (U shl 4);611Result := U;612end;613
614function Reverse(U: UInt16): UInt16;615begin
616U := ((U shr 1) and $5555) or ((U and $5555) shl 1);617U := ((U shr 2) and $3333) or ((U and $3333) shl 2);618U := ((U shr 4) and $0F0F) or ((U and $0F0F) shl 4);619U := Swap(U);620Result := U;621end;622
623function Reverse(S: Int32): Int32;624begin
625Result := Int32(Reverse(UInt32(S)));626end;627
628// See http://stackoverflow.com/questions/746171/best-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c too.
629// http://stackoverflow.com/a/9144870/95954
630function Reverse(U: UInt32): UInt32;631begin
632U := ((U shr 1) and $55555555) or ((U and $55555555) shl 1); // Swap adjacent bits.633U := ((U shr 2) and $33333333) or ((U and $33333333) shl 2); // Swap adjacent bit pairs.634U := ((U shr 4) and $0F0F0F0F) or ((U and $0F0F0F0F) shl 4); // Swap nibbles.635U := ((U shr 8) and $00FF00FF) or ((U and $00FF00FF) shl 8); // Swap bytes.636U := (U shr 16) or (U shl 16); // Swap words.637Result := U;638end;639
640function ReverseBytes(S: Int32): Int32;641begin
642Result := Int32(ReverseBytes(UInt32(S)));643end;644
645// Byte and word swaps of Reverse(U).
646function ReverseBytes(U: UInt32): UInt32;647begin
648U := ((U shr 8) and $00FF00FF) or ((U and $00FF00FF) shl 8); // Swap bytes.649U := (U shr 16) or (U shl 16); // Swap words.650Result := U;651end;652
653function RotateLeft(S: Int32; Distance: Integer): Int32;654begin
655Result := Int32(RotateLeft(UInt32(S), Distance));656end;657
658function RotateLeft(U: UInt32; Distance: Integer): UInt32;659begin
660Distance := Distance and 31;661Result := (U shl Distance) or (U shr (32 - Distance));662end;663
664function RotateRight(S: Int32; Distance: Integer): Int32;665begin
666Result := Int32(RotateRight(UInt32(S), Distance));667end;668
669function RotateRight(U: UInt32; Distance: Integer): UInt32;670begin
671Distance := Distance and 31;672Result := (U shr Distance) or (U shl (32- Distance));673end;674
675function Sign(S: Int32): TValueSign;676begin
677Result := System.Math.Sign(S);678end;679
680function ToBinaryString(S: Int32): string;681begin
682Result := ToString(S, 2);683end;684
685function ToBinaryString(U: UInt32): string;686begin
687Result := ToString(U, 2);688end;689
690function ToHexString(S: Int32): string;691begin
692Result := ToString(S, 16);693end;694
695function ToHexString(U: UInt32): string;696begin
697Result := ToString(U, 16);698end;699
700function ToOctalString(S: Int32): string;701begin
702Result := ToString(S, 8);703end;704
705function ToOctalString(U: UInt32): string;706begin
707Result := ToString(U, 8);708end;709
710const
711Digits: array[0..35] of Char = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';712
713function ToString(S: Int32; Base: Byte): string;714begin
715if S < 0 then716Result := '-' + ToString(UInt32(Abs(S)), Base)717else718Result := ToString(UInt32(S), Base);719end;720
721function ToString(U: UInt32; Base: Byte): string;722begin
723if not (Base in [2..36]) then724raise EInvalidArgument.Create(SInvalidArgumentBase);725
726if U = 0 then727Result := '0'728else729begin730Result := '';731while U > 0 do732begin733Result := Digits[U mod Base] + Result;734U := U div Base;735end;736end;737end;738
739function ToString(S: Int32): string;740begin
741Result := ToString(S, 10);742end;743
744function ToString(U: UInt32): string;745begin
746Result := ToString(U, 10);747end;748
749function Compare(Left, Right: Int32): Integer;750begin
751if Left > Right then752Exit(1)753else if Left < Right then754Exit(-1)755else756Exit(0);757end;758
759function Compare(Left, Right: UInt32): Integer;760begin
761if Left > Right then762Exit(1)763else if Left < Right then764Exit(-1)765else766Exit(0);767end;768
769function Compare(Left, Right: Int64): Integer;770begin
771if Left > Right then772Exit(1)773else if Left < Right then774Exit(-1)775else776Exit(0);777end;778
779function Compare(Left, Right: UInt64): Integer;780begin
781if Left > Right then782Exit(1)783else if Left < Right then784Exit(-1)785else786Exit(0);787end;788
789function HashCode(Value: Int32): UInt32;790begin
791Result := UInt32(Value);792end;793
794function HashCode(Value: UInt32): UInt32;795begin
796Result := Value;797end;798
799function HashCode(Value: Int64): UInt32;800begin
801Result := UInt32(Value) xor UInt32(Value shr 32);802end;803
804function HashCode(Value: UInt64): UInt32;805begin
806Result := UInt32(Value) xor UInt32(Value shr 32);807end;808
809end.810