FastReport_2022_VCL/LibD28x64/frxDelphiZXIngCode.pas
2024-01-01 16:13:08 +01:00

852 lines
20 KiB
ObjectPascal

unit frxDelphiZXIngCode;
(*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*)
interface
uses Contnrs;
{$I frx.inc}
type
TIntegerArray = array of Integer;
TGenericGFPoly = class;
TGenericGF = class
private
FExpTable: TIntegerArray;
FLogTable: TIntegerArray;
FZero: TGenericGFPoly;
FOne: TGenericGFPoly;
FSize: Integer;
FPrimitive: Integer;
FGeneratorBase: Integer;
FInitialized: Boolean;
FPolyList: array of TGenericGFPoly;
procedure CheckInit;
procedure Initialize;
public
class function CreateQRCodeField256: TGenericGF;
class function CreateAztecData12: TGenericGF; // x^12 + x^6 + x^5 + x^3 + 1
class function CreateAztecData10: TGenericGF; // x^10 + x^3 + 1
class function CreateAztecData8: TGenericGF;
class function CreateMatrixField256: TGenericGF; // x^8 + x^5 + x^3 + x^2 + 1
class function CreateAztecData6: TGenericGF;
class function CreateMaxicodeField64: TGenericGF; // x^6 + x + 1
class function CreateAztecParam: TGenericGF; // x^4 + x + 1
class function AddOrSubtract(A, B: Integer): Integer;
constructor Create(Primitive, Size, B: Integer);
destructor Destroy; override;
function GetZero: TGenericGFPoly;
function Exp(A: Integer): Integer;
function GetGeneratorBase: Integer;
function Inverse(A: Integer): Integer;
function Multiply(A, B: Integer): Integer;
function BuildMonomial(Degree, Coefficient: Integer): TGenericGFPoly;
end;
TGenericGFPolyArray = array of TGenericGFPoly;
TGenericGFPoly = class
private
FField: TGenericGF;
FCoefficients: TIntegerArray;
public
constructor Create(AField: TGenericGF; ACoefficients: TIntegerArray);
destructor Destroy; override;
function Coefficients: TIntegerArray;
function Multiply(Other: TGenericGFPoly): TGenericGFPoly;
function MultiplyByMonomial(Degree, Coefficient: Integer): TGenericGFPoly;
function Divide(Other: TGenericGFPoly): TGenericGFPolyArray;
function GetCoefficients: TIntegerArray;
function IsZero: Boolean;
function GetCoefficient(Degree: Integer): Integer;
function GetDegree: Integer;
function AddOrSubtract(Other: TGenericGFPoly): TGenericGFPoly;
end;
TReedSolomonEncoder = class
private
FField: TGenericGF;
FCachedGenerators: TObjectList;
public
constructor Create(AField: TGenericGF);
destructor Destroy; override;
procedure Encode(ToEncode: TIntegerArray; ECBytes: Integer);
function BuildGenerator(Degree: Integer): TGenericGFPoly;
end;
TByteArray = array of Byte;
TBitArray = class
private
FSize: Integer;
Bits: array of Integer;
procedure SetItem(i: Integer; const Value: Boolean);
protected
procedure EnsureCapacity(Size: Integer);
public
constructor Create; overload;
constructor Create(const Size: Integer); overload;
procedure Clear;
function GetSizeInBytes: Integer;
function GetSize: Integer;
function Get(i: Integer): Boolean;
procedure SetBit(Index: Integer);
procedure AppendBit(Bit: Boolean);
procedure AppendBits(Value, NumBits: Integer);
procedure AppendBitArray(NewBitArray: TBitArray);
procedure ToBytes(BitOffset: Integer; Source: TByteArray;
Offset, NumBytes: Integer);
procedure XorOperation(Other: TBitArray);
function ToString: string; {$IFDEF Delphi12} override; {$ENDIF}
property Size: Integer read FSize;
property Item[i: Integer]: Boolean read Get write SetItem; default;
end;
function IfValue(IsTrue: Boolean; TrueValue, FalseValue: Integer)
: Integer; overload;
function IfValue(IsTrue: Boolean; TrueValue, FalseValue: string)
: string; overload;
function IfValue(IsTrue: Boolean; TrueValue, FalseValue: AnsiChar)
: AnsiChar; overload;
//function ToBinaryString(x: LongInt): string;
procedure FreeAndSetBitArray(var BitArray1: TBitArray; BitArray2: TBitArray);
implementation
(***************************************************************)
uses
Math, Classes, SysUtils, frxUnicodeUtils{$IFNDEF FPC}, Windows{$ENDIF};
procedure FreeAndSetBitArray(var BitArray1: TBitArray; BitArray2: TBitArray);
var
Temp: TBitArray;
begin
Temp := BitArray2;
BitArray1.Free;
BitArray1 := Temp;
end;
//function ToBinaryString(x: LongInt): string;
//var
// i: integer;
//begin
// Result := '';
// for i := 0 to 32 - 1 do
// begin
// Result := IfValue(Odd(x), '1', '0') + Result;
// x := x div 2;
// end;
//end;
{ IfValue }
function IfValue(IsTrue: Boolean; TrueValue, FalseValue: AnsiChar): AnsiChar;
begin
if IsTrue then
Result := TrueValue
else
Result := FalseValue;
end;
function IfValue(IsTrue: Boolean; TrueValue, FalseValue: string): string;
begin
if IsTrue then
Result := TrueValue
else
Result := FalseValue;
end;
function IfValue(IsTrue: Boolean; TrueValue, FalseValue: Integer): Integer;
begin
if IsTrue then
Result := TrueValue
else
Result := FalseValue;
end;
{ TBitArray }
procedure TBitArray.AppendBit(Bit: Boolean);
begin
EnsureCapacity(Size + 1);
if (Bit) then
begin
Bits[Size shr 5] := Bits[Size shr 5] or (1 shl (Size and $1F));
end;
Inc(FSize);
end;
procedure TBitArray.AppendBitArray(NewBitArray: TBitArray);
var
OtherSize: Integer;
i: Integer;
begin
OtherSize := NewBitArray.GetSize;
EnsureCapacity(Size + OtherSize);
for i := 0 to OtherSize - 1 do
begin
AppendBit(NewBitArray.Get(i));
end;
end;
procedure TBitArray.AppendBits(Value, NumBits: Integer);
var
NumBitsLeft: Integer;
begin
if ((NumBits < 0) or (NumBits > 32)) then
begin
end;
EnsureCapacity(Size + NumBits);
for NumBitsLeft := NumBits downto 1 do
begin
AppendBit(((Value shr (NumBitsLeft - 1)) and $01) = 1);
end;
end;
constructor TBitArray.Create;
begin
FSize := 0;
SetLength(Bits, 1);
end;
procedure TBitArray.Clear;
begin
FSize := 0;
SetLength(Bits, 1);
Bits[0] := 0;
end;
constructor TBitArray.Create(const Size: Integer);
begin
FSize := Size;
SetLength(Bits, (Size + 31) shr 5);
end;
procedure TBitArray.EnsureCapacity(Size: Integer);
begin
if (Size > (Length(Bits) shl 5)) then
begin
SetLength(Bits, (Size + 31) shr 5);
end;
end;
function TBitArray.Get(i: Integer): Boolean;
begin
Result := (Bits[i shr 5] and (1 shl (i and $1F))) <> 0;
end;
function TBitArray.GetSize: Integer;
begin
Result := Size;
end;
function TBitArray.GetSizeInBytes: Integer;
begin
Result := (Size + 7) shr 3;
end;
procedure TBitArray.SetBit(Index: Integer);
begin
Bits[Index shr 5] := Bits[Index shr 5] or (1 shl (Index and $1F));
end;
procedure TBitArray.SetItem(i: Integer; const Value: Boolean);
begin
if Value then
SetBit(i);
end;
procedure TBitArray.ToBytes(BitOffset: Integer; Source: TByteArray;
Offset, NumBytes: Integer);
var
i: Integer;
J: Integer;
TheByte: Integer;
begin
for i := 0 to NumBytes - 1 do
begin
TheByte := 0;
for J := 0 to 7 do
begin
if (Get(BitOffset)) then
begin
TheByte := TheByte or (1 shl (7 - J));
end;
Inc(BitOffset);
end;
Source[Offset + i] := TheByte;
end;
end;
function TBitArray.ToString: string;
var
i: Integer;
begin
Result := '';
for i := 0 to Size - 1 do
begin
if i mod 8 = 0 then
Result := Result + ' ';
if Item[i] then
Result := Result + 'X'
else
Result := Result + '^';
end;
end;
procedure TBitArray.XorOperation(Other: TBitArray);
var
i: Integer;
begin
if (Length(Bits) = Length(Other.Bits)) then
begin
for i := 0 to Length(Bits) - 1 do
begin
// The last byte could be incomplete (i.e. not have 8 bits in
// it) but there is no problem since 0 XOR 0 == 0.
Bits[i] := Bits[i] xor Other.Bits[i];
end;
end;
end;
{ TReedSolomonEncoder }
function TReedSolomonEncoder.BuildGenerator(Degree: Integer): TGenericGFPoly;
var
LastGenerator: TGenericGFPoly;
NextGenerator: TGenericGFPoly;
Poly: TGenericGFPoly;
D: Integer;
CA: TIntegerArray;
begin
if (Degree >= FCachedGenerators.Count) then
begin
LastGenerator := TGenericGFPoly
(FCachedGenerators[FCachedGenerators.Count - 1]);
for D := FCachedGenerators.Count to Degree do
begin
SetLength(CA, 2);
CA[0] := 1;
CA[1] := FField.Exp(D - 1 + FField.GetGeneratorBase);
Poly := TGenericGFPoly.Create(FField, CA);
NextGenerator := LastGenerator.Multiply(Poly);
FCachedGenerators.Add(NextGenerator);
LastGenerator := NextGenerator;
end;
end;
Result := TGenericGFPoly(FCachedGenerators[Degree]);
end;
constructor TReedSolomonEncoder.Create(AField: TGenericGF);
var
GenericGFPoly: TGenericGFPoly;
IntArray: TIntegerArray;
begin
FField := AField;
// Contents of FCachedGenerators will be freed by FGenericGF.Destroy
FCachedGenerators := TObjectList.Create(False);
SetLength(IntArray, 1);
IntArray[0] := 1;
GenericGFPoly := TGenericGFPoly.Create(AField, IntArray);
FCachedGenerators.Add(GenericGFPoly);
end;
destructor TReedSolomonEncoder.Destroy;
begin
FCachedGenerators.Free;
inherited;
end;
procedure TReedSolomonEncoder.Encode(ToEncode: TIntegerArray; ECBytes: Integer);
var
DataBytes: Integer;
Generator: TGenericGFPoly;
InfoCoefficients: TIntegerArray;
Info: TGenericGFPoly;
Remainder: TGenericGFPoly;
Coefficients: TIntegerArray;
NumZeroCoefficients: Integer;
i: Integer;
begin
SetLength(Coefficients, 0);
if (ECBytes > 0) then
begin
DataBytes := Length(ToEncode) - ECBytes;
if (DataBytes > 0) then
begin
Generator := BuildGenerator(ECBytes);
SetLength(InfoCoefficients, DataBytes);
InfoCoefficients := Copy(ToEncode, 0, DataBytes);
Info := TGenericGFPoly.Create(FField, InfoCoefficients);
Info := Info.MultiplyByMonomial(ECBytes, 1);
Remainder := Info.Divide(Generator)[1];
Coefficients := Remainder.GetCoefficients;
NumZeroCoefficients := ECBytes - Length(Coefficients);
for i := 0 to NumZeroCoefficients - 1 do
begin
ToEncode[DataBytes + i] := 0;
end;
Move(Coefficients[0], ToEncode[DataBytes + NumZeroCoefficients],
Length(Coefficients) * SizeOf(Integer));
end;
end;
end;
{ TGenericGFPoly }
function TGenericGFPoly.AddOrSubtract(Other: TGenericGFPoly): TGenericGFPoly;
var
SmallerCoefficients: TIntegerArray;
LargerCoefficients: TIntegerArray;
Temp: TIntegerArray;
SumDiff: TIntegerArray;
LengthDiff: Integer;
i: Integer;
begin
SetLength(SmallerCoefficients, 0);
SetLength(LargerCoefficients, 0);
SetLength(Temp, 0);
SetLength(SumDiff, 0);
Result := nil;
if (Assigned(Other)) then
begin
if (FField = Other.FField) then
begin
if (IsZero) then
begin
Result := Other;
Exit;
end;
if (Other.IsZero) then
begin
Result := Self;
Exit;
end;
SmallerCoefficients := FCoefficients;
LargerCoefficients := Other.Coefficients;
if (Length(SmallerCoefficients) > Length(LargerCoefficients)) then
begin
Temp := SmallerCoefficients;
SmallerCoefficients := LargerCoefficients;
LargerCoefficients := Temp;
end;
SetLength(SumDiff, Length(LargerCoefficients));
LengthDiff := Length(LargerCoefficients) - Length(SmallerCoefficients);
// Copy high-order terms only found in higher-degree polynomial's coefficients
if (LengthDiff > 0) then
begin
//SumDiff := Copy(LargerCoefficients, 0, LengthDiff);
Move(LargerCoefficients[0], SumDiff[0], LengthDiff * SizeOf(Integer));
end;
for i := LengthDiff to Length(LargerCoefficients) - 1 do
begin
SumDiff[i] := TGenericGF.AddOrSubtract
(SmallerCoefficients[i - LengthDiff], LargerCoefficients[i]);
end;
Result := TGenericGFPoly.Create(FField, SumDiff);
end;
end;
end;
function TGenericGFPoly.Coefficients: TIntegerArray;
begin
Result := FCoefficients;
end;
constructor TGenericGFPoly.Create(AField: TGenericGF;
ACoefficients: TIntegerArray);
var
CoefficientsLength: Integer;
FirstNonZero: Integer;
begin
FField := AField;
SetLength(FField.FPolyList, Length(FField.FPolyList) + 1);
FField.FPolyList[Length(FField.FPolyList) - 1] := Self;
CoefficientsLength := Length(ACoefficients);
if ((CoefficientsLength > 1) and (ACoefficients[0] = 0)) then
begin
// Leading term must be non-zero for anything except the constant polynomial "0"
FirstNonZero := 1;
while ((FirstNonZero < CoefficientsLength) and
(ACoefficients[FirstNonZero] = 0)) do
begin
Inc(FirstNonZero);
end;
if (FirstNonZero = CoefficientsLength) then
begin
FCoefficients := AField.GetZero.Coefficients;
end
else
begin
SetLength(FCoefficients, CoefficientsLength - FirstNonZero);
FCoefficients := Copy(ACoefficients, FirstNonZero, Length(FCoefficients));
end;
end
else
begin
FCoefficients := ACoefficients;
end;
end;
destructor TGenericGFPoly.Destroy;
begin
Self.FField := FField;
inherited;
end;
function TGenericGFPoly.Divide(Other: TGenericGFPoly): TGenericGFPolyArray;
var
Quotient: TGenericGFPoly;
Remainder: TGenericGFPoly;
DenominatorLeadingTerm: Integer;
InverseDenominatorLeadingTerm: Integer;
DegreeDifference: Integer;
Scale: Integer;
Term: TGenericGFPoly;
IterationQuotient: TGenericGFPoly;
begin
SetLength(Result, 0);
if ((FField = Other.FField) and (not Other.IsZero)) then
begin
Quotient := FField.GetZero;
Remainder := Self;
DenominatorLeadingTerm := Other.GetCoefficient(Other.GetDegree);
InverseDenominatorLeadingTerm := FField.Inverse(DenominatorLeadingTerm);
while ((Remainder.GetDegree >= Other.GetDegree) and
(not Remainder.IsZero)) do
begin
DegreeDifference := Remainder.GetDegree - Other.GetDegree;
Scale := FField.Multiply(Remainder.GetCoefficient(Remainder.GetDegree),
InverseDenominatorLeadingTerm);
Term := Other.MultiplyByMonomial(DegreeDifference, Scale);
IterationQuotient := FField.BuildMonomial(DegreeDifference, Scale);
Quotient := Quotient.AddOrSubtract(IterationQuotient);
Remainder := Remainder.AddOrSubtract(Term);
end;
SetLength(Result, 2);
Result[0] := Quotient;
Result[1] := Remainder;
end;
end;
function TGenericGFPoly.GetCoefficient(Degree: Integer): Integer;
begin
Result := FCoefficients[Length(FCoefficients) - 1 - Degree];
end;
function TGenericGFPoly.GetCoefficients: TIntegerArray;
begin
Result := FCoefficients;
end;
function TGenericGFPoly.GetDegree: Integer;
begin
Result := Length(FCoefficients) - 1;
end;
function TGenericGFPoly.IsZero: Boolean;
begin
Result := FCoefficients[0] = 0;
end;
function TGenericGFPoly.Multiply(Other: TGenericGFPoly): TGenericGFPoly;
var
ACoefficients: TIntegerArray;
BCoefficients: TIntegerArray;
Product: TIntegerArray;
ALength: Integer;
BLength: Integer;
i: Integer;
J: Integer;
ACoeff: Integer;
begin
SetLength(ACoefficients, 0);
SetLength(BCoefficients, 0);
Result := nil;
if (FField = Other.FField) then
begin
if (IsZero or Other.IsZero) then
begin
Result := FField.GetZero;
Exit;
end;
ACoefficients := FCoefficients;
ALength := Length(ACoefficients);
BCoefficients := Other.Coefficients;
BLength := Length(BCoefficients);
SetLength(Product, ALength + BLength - 1);
for i := 0 to ALength - 1 do
begin
ACoeff := ACoefficients[i];
for J := 0 to BLength - 1 do
begin
Product[i + J] := TGenericGF.AddOrSubtract(Product[i + J],
FField.Multiply(ACoeff, BCoefficients[J]));
end;
end;
Result := TGenericGFPoly.Create(FField, Product);
end;
end;
function TGenericGFPoly.MultiplyByMonomial(Degree, Coefficient: Integer)
: TGenericGFPoly;
var
i: Integer;
Size: Integer;
Product: TIntegerArray;
begin
Result := nil;
if (Degree >= 0) then
begin
if (Coefficient = 0) then
begin
Result := FField.GetZero;
Exit;
end;
Size := Length(Coefficients);
SetLength(Product, Size + Degree);
for i := 0 to Size - 1 do
begin
Product[i] := FField.Multiply(FCoefficients[i], Coefficient);
end;
Result := TGenericGFPoly.Create(FField, Product);
end;
end;
{ TGenericGF }
class function TGenericGF.AddOrSubtract(A, B: Integer): Integer;
begin
Result := A xor B;
end;
function TGenericGF.BuildMonomial(Degree, Coefficient: Integer): TGenericGFPoly;
var
Coefficients: TIntegerArray;
begin
CheckInit();
if (Degree >= 0) then
begin
if (Coefficient = 0) then
begin
Result := FZero;
Exit;
end;
SetLength(Coefficients, Degree + 1);
Coefficients[0] := Coefficient;
Result := TGenericGFPoly.Create(Self, Coefficients);
end
else
begin
Result := nil;
end;
end;
procedure TGenericGF.CheckInit;
begin
if (not FInitialized) then
begin
Initialize;
end;
end;
constructor TGenericGF.Create(Primitive, Size, B: Integer);
begin
FInitialized := False;
FPrimitive := Primitive;
FSize := Size;
FGeneratorBase := B;
if (FSize < 0) then
begin
Initialize;
end;
end;
class function TGenericGF.CreateAztecData10: TGenericGF;
begin
Result := TGenericGF.Create($0409, 1024, 1);
end;
class function TGenericGF.CreateAztecData12: TGenericGF;
begin
Result := TGenericGF.Create($1069, 4096, 1);
end;
class function TGenericGF.CreateAztecData6: TGenericGF;
begin
Result := TGenericGF.Create($0043, 64, 1);
end;
class function TGenericGF.CreateAztecData8: TGenericGF;
begin
Result := TGenericGF.Create($012D, 256, 1);
end;
class function TGenericGF.CreateAztecParam: TGenericGF;
begin
Result := TGenericGF.Create($0013, 16, 1);
end;
class function TGenericGF.CreateMatrixField256: TGenericGF;
begin
Result := CreateAztecData8;
end;
class function TGenericGF.CreateMaxicodeField64: TGenericGF;
begin
Result := CreateAztecData6;
end;
class function TGenericGF.CreateQRCodeField256: TGenericGF;
begin
Result := TGenericGF.Create($011D, 256, 0);
end;
destructor TGenericGF.Destroy;
var
X, Y: Integer;
begin
for X := 0 to Length(FPolyList) - 1 do
if (Assigned(FPolyList[X])) then
begin
for Y := X + 1 to Length(FPolyList) - 1 do
if FPolyList[Y] = FPolyList[X] then
FPolyList[Y] := nil;
FPolyList[X].Free;
end;
inherited;
end;
function TGenericGF.Exp(A: Integer): Integer;
begin
CheckInit;
Result := FExpTable[A];
end;
function TGenericGF.GetGeneratorBase: Integer;
begin
Result := FGeneratorBase;
end;
function TGenericGF.GetZero: TGenericGFPoly;
begin
CheckInit;
Result := FZero;
end;
procedure TGenericGF.Initialize;
var
X: Integer;
i: Integer;
CA: TIntegerArray;
begin
SetLength(FExpTable, FSize);
SetLength(FLogTable, FSize);
X := 1;
for i := 0 to FSize - 1 do
begin
FExpTable[i] := X;
X := X shl 1; // x = x * 2; we're assuming the generator alpha is 2
if (X >= FSize) then
begin
X := X xor FPrimitive;
X := X and (FSize - 1);
end;
end;
for i := 0 to FSize - 2 do
begin
FLogTable[FExpTable[i]] := i;
end;
// logTable[0] == 0 but this should never be used
SetLength(CA, 1);
CA[0] := 0;
FZero := TGenericGFPoly.Create(Self, CA);
SetLength(CA, 1);
CA[0] := 1;
FOne := TGenericGFPoly.Create(Self, CA);
FInitialized := True;
end;
function TGenericGF.Inverse(A: Integer): Integer;
begin
CheckInit;
if (A <> 0) then
begin
Result := FExpTable[FSize - FLogTable[A] - 1];
end
else
begin
Result := 0;
end;
end;
function TGenericGF.Multiply(A, B: Integer): Integer;
begin
CheckInit;
if ((A <> 0) and (B <> 0)) then
begin
Result := FExpTable[(FLogTable[A] + FLogTable[B]) mod (FSize - 1)];
end
else
begin
Result := 0;
end;
end;
initialization
end.