From adcc0a063e2df430b1838d84c6517b686eac0285 Mon Sep 17 00:00:00 2001 From: liuxiao Date: Wed, 26 Sep 2018 23:57:38 +0800 Subject: [PATCH] * [CnECC] Update Demo to add a Lucas Int64 V routine. --- Examples/ECC/UnitEcc.dfm | 12 ++++- Examples/ECC/UnitEcc.pas | 88 ++++++++++++++++++++++++---------- Source/Common/CnBigNumber.pas | 4 +- Source/Common/CnNativeDecl.pas | 29 +++++++++++ 4 files changed, 105 insertions(+), 28 deletions(-) diff --git a/Examples/ECC/UnitEcc.dfm b/Examples/ECC/UnitEcc.dfm index 727f1abe0..089c045d0 100644 --- a/Examples/ECC/UnitEcc.dfm +++ b/Examples/ECC/UnitEcc.dfm @@ -20,7 +20,7 @@ object FormEcc: TFormEcc Top = 16 Width = 737 Height = 465 - ActivePage = tsWrapData + ActivePage = tsLucas TabOrder = 0 object tsSimpleECC: TTabSheet Caption = 'Simple ECC' @@ -1228,10 +1228,18 @@ object FormEcc: TFormEcc Top = 56 Width = 153 Height = 21 - Caption = 'Lucas Mod' + Caption = 'Lucas 2' TabOrder = 6 OnClick = btnLucasModClick end + object chkLucasMod: TCheckBox + Left = 296 + Top = 28 + Width = 49 + Height = 17 + Caption = 'Mod' + TabOrder = 7 + end end object grpLegendre: TGroupBox Left = 392 diff --git a/Examples/ECC/UnitEcc.pas b/Examples/ECC/UnitEcc.pas index 1ba2e367f..aa20421a4 100644 --- a/Examples/ECC/UnitEcc.pas +++ b/Examples/ECC/UnitEcc.pas @@ -5,7 +5,7 @@ interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, CnECC, ExtCtrls, Buttons, TeEngine, Series, TeeProcs, - Chart, CnPrimeNumber, CnBigNumber; + Chart, CnPrimeNumber, CnBigNumber, CnNativeDecl; type TFormEcc = class(TForm) @@ -173,6 +173,7 @@ TFormEcc = class(TForm) btnBNTS: TButton; mmoTSData: TMemo; btnRandomTS: TButton; + chkLucasMod: TCheckBox; procedure btnTest1Click(Sender: TObject); procedure btnTest0Click(Sender: TObject); procedure btnTestOnClick(Sender: TObject); @@ -973,6 +974,7 @@ procedure TFormEcc.btnBNGXtoPointClick(Sender: TObject); procedure TFormEcc.btnInt64GXtoPtClick(Sender: TObject); var +// I: Integer; P, A, B, X, Y, N: Int64; Ecc: TCnInt64Ecc; Pt: TCnInt64EccPoint; @@ -989,6 +991,11 @@ procedure TFormEcc.btnInt64GXtoPtClick(Sender: TObject); // 15/194/64403/41589/5579 是 4u3 型的测试通过 // 12/199/73/21/21/61 是 8u1 型的也基本测试通过 +//P := GetTickCount; +//for I := 0 to 100000 do // 一万次要 453/469 毫秒,十万次要 4484 毫秒 +// Ecc.PlainToPoint(X, Pt); +//ShowMessage(IntToStr(GetTickCount - P)); + if Ecc.PlainToPoint(X, Pt) then begin ShowMessage('Convert to ' + CnInt64EccPointToString(Pt)); @@ -1003,11 +1010,12 @@ procedure TFormEcc.btnInt64GXtoPtClick(Sender: TObject); procedure TFormEcc.btnLucasRecurClick(Sender: TObject); var - X, Y, U, V, U_2, V_2, U_1, V_1: Int64; + X, Y, P, U, V, U_2, V_2, U_1, V_1: Int64; I: Integer; begin X := StrToInt(edtLucasX.Text); Y := StrToInt(edtLucasY.Text); + P := StrToInt(edtLucasP.Text); mmoLucasRes.Lines.Clear; mmoLucasRes.Lines.Add(Format('%d: %d, %d', [0, 0, 2])); @@ -1025,10 +1033,14 @@ procedure TFormEcc.btnLucasRecurClick(Sender: TObject); U_1 := U; V_1 := V; - mmoLucasRes.Lines.Add(Format('%d: %d, %d', [I, U, V])); + if chkLucasMod.Checked then + mmoLucasRes.Lines.Add(Format('%d: %d, %d', [I, U mod P, V mod P])) + else + mmoLucasRes.Lines.Add(Format('%d: %d, %d', [I, U, V])); end; end; +// 按定义递归计算 Lucas 序列,照理慢但准确 procedure TFormEcc.CalcLucas(X, Y, U_2, V_2, U_1, V_1: Int64; var U, V: Int64); begin U := X * U_1 - Y * U_2; @@ -1051,7 +1063,7 @@ function Int64MultipleMod(A, B, C: Int64): Int64; end; // 计算 Lucas 序列以及模的两种不同实现函数,但结果对不上,弃用。 -procedure CalcLucasSequence(X, Y, K, P: Int64; out U, V: Int64); +procedure CalcLucasSequenceBad(X, Y, K, P: Int64; out U, V: Int64); var I: Integer; U_2, V_2, U_1, V_1: Int64; @@ -1090,33 +1102,58 @@ procedure CalcLucasSequence(X, Y, K, P: Int64; out U, V: Int64); end; end; -procedure CalcLucasSequenceMod(X, Y, K, P: Int64; out U, V: Int64); +// 另一种 Lucas 计算,V 对得上号但 U 不靠谱 +procedure CalcLucasSequence2(X, Y, K, P: Int64; out U, V: Int64); var C, I: Integer; - D, UT, VT: Int64; + V0, V1, Q0, Q1: Int64; +begin + if K < 0 then + raise ECnEccException.Create('Invalid K for Lucas Sequence'); - function GetInt64BitSet(B: Int64; Index: Integer): Boolean; // 返回 Int64 的第几位是否是 1,0 开始 + if K = 0 then + begin + U := 0; + V := 2; + Exit; + end + else if K = 1 then begin - B := B and (Int64(1) shl Index); - Result := B <> 0; + U := 1; + V := X; + Exit; end; - // 返回 Int64 的最高二进制位是第几位,0 开始 - function GetInt64HighBits(B: Int64): Integer; - var - J: Integer; + V0 := 2; + V1 := X; + Q0 := 1; + Q1 := 1; + + C := GetUInt64HighBits(K); + for I := C downto 0 do begin - for J := 63 downto 0 do + Q0 := Q0 * Q1; + if GetUInt64BitSet(K, I) then begin - if GetInt64BitSet(B, J) then - begin - Result := J; - Exit; - end; + Q1 := Q0 * Y; + V0 := V0 * V1 - X * Q0; + V1 := V1 * V1 - 2 * Q1; + end + else + begin + Q1 := Q0; + V1 := V0 * V1 - X * Q0; + V0 := V0 * V0 - 2 * Q0; end; - Result := 0; end; + U := Q0; + V := V0; +end; +procedure CalcLucasSequenceMod(X, Y, K, P: Int64; out U, V: Int64); +var + C, I: Integer; + D, UT, VT: Int64; begin if K < 0 then raise ECnEccException.Create('Invalid K for Lucas Sequence'); @@ -1139,7 +1176,7 @@ procedure CalcLucasSequenceMod(X, Y, K, P: Int64; out U, V: Int64); U := 1; V := X; - C := GetInt64HighBits(K); + C := GetUInt64HighBits(K); for I := C - 1 downto 0 do begin UT := Int64MultipleMod(U, V, P); @@ -1147,7 +1184,7 @@ procedure CalcLucasSequenceMod(X, Y, K, P: Int64; out U, V: Int64); U := UT; V := VT; - if GetInt64BitSet(K, I) then + if GetUInt64BitSet(K, I) then begin UT := ((X * U + V) div 2) mod P; VT := ((X * V + D * U) div 2) mod P; @@ -1174,8 +1211,11 @@ procedure TFormEcc.btnLucasModClick(Sender: TObject); for I := 0 to 100 do begin - CalcLucasSequence(X, Y, I, P, U, V); - mmoLucasMod.Lines.Add(Format('%d: %d, %d', [I, U, V])); + CalcLucasSequence2(X, Y, I, P, U, V); + if chkLucasMod.Checked then + mmoLucasMod.Lines.Add(Format('%d: %d, %d', [I, U mod P, V mod P])) + else + mmoLucasMod.Lines.Add(Format('%d: %d, %d', [I, U, V])); end; end; diff --git a/Source/Common/CnBigNumber.pas b/Source/Common/CnBigNumber.pas index 12065a3e5..f238e183a 100644 --- a/Source/Common/CnBigNumber.pas +++ b/Source/Common/CnBigNumber.pas @@ -471,8 +471,8 @@ function BigNumberLegendre2(A, P: TCnBigNumber): Integer; {* 用欧拉判别法计算勒让德符号 ( A / P) 的值,较慢} function BigNumberTonelliShanks(const Res: TCnBigNumber; A, P: TCnBigNumber): Boolean; -{* 使用 Tonelli Shanks 算法进行模素数二次剩余求解,也就是求 Res^2 mod P = A - 调用者需自行保证 P 为奇素数或奇素数的整数次方,并且保证 A/P 的勒让德符号为 1 也就是有解} +{* 使用 Tonelli Shanks 算法进行模素数二次剩余求解,也就是求 Res^2 mod P = A,返回是否有解 + 调用者需自行保证 P 为奇素数或奇素数的整数次方} procedure BigNumberFindFactors(Num: TCnBigNumber; Factors: TCnBigNumberList); {* 找出大数的质因数列表} diff --git a/Source/Common/CnNativeDecl.pas b/Source/Common/CnNativeDecl.pas index 5ae03ceed..bf1fa15ff 100644 --- a/Source/Common/CnNativeDecl.pas +++ b/Source/Common/CnNativeDecl.pas @@ -110,6 +110,12 @@ function UInt32IsNegative(N: Cardinal): Boolean; function UInt64IsNegative(N: TUInt64): Boolean; {* 该 UInt64 被当成 Int64 时是否小于 0} +function GetUInt64BitSet(B: TUInt64; Index: Integer): Boolean; +{* 返回 Int64 的某一位是否是 1,位 Index 从 0 开始} + +function GetUInt64HighBits(B: TUInt64): Integer; +{* 返回 Int64 的最高二进制位是第几位,最低位是 0} + implementation { @@ -291,4 +297,27 @@ function UInt64IsNegative(N: TUInt64): Boolean; {$ENDIF} end; +// 返回 UInt64 的第几位是否是 1,0 开始 +function GetUInt64BitSet(B: TUInt64; Index: Integer): Boolean; +begin + B := B and (TUInt64(1) shl Index); + Result := B <> 0; +end; + +// 返回 UInt64 的最高二进制位是第几位,0 开始 +function GetUInt64HighBits(B: TUInt64): Integer; +var + J: Integer; +begin + for J := 63 downto 0 do + begin + if GetUInt64BitSet(B, J) then + begin + Result := J; + Exit; + end; + end; + Result := 0; +end; + end.