I need to convert a hexadecimal value to a decimal integer. Is there some unit that can do this?
On the web I have found something about it but it is not helping me much. I understood that using inline asm it is possible to represent it as a packed array of four 32 bit Integer. I have found as convert a int32 or int64 to int128 and viceversa, but not found nothing for take for example two int64 and to do a int128 and too i have found issue searching in asm inline something that emule div operator, mul operator and sum operator.
So I ask if someone can help me to solve this problem. My objective is to take a string in hexadecimal and convert it in decimal and after that calculate the equivalent value in base 35 (0..9, A..Z).
Thanks very much.
If you convert the hexadecimal string into an array of bytes (see SysUtils for that) you can use the follewing code to convert it into base 35:
function EncodeBaseX( const Values: array of Byte; var Dest: array of Byte; Radix: Integer ): Boolean;
var
i,j,Carry: Integer;
begin
// We're unsuccesful up to now
Result := False;
// Check if we have an output buffer and clear it
if Length( Dest ) = 0 then Exit;
System.FillChar( Dest[ 0 ], Length( Dest ), 0 );
// fill in the details
for i := 0 to High( Values ) do begin
Carry := Values[ i ];
for j := 0 to High( Dest ) do begin
Inc( Carry, Radix * Dest[ j ] );
Dest[ j ] := Carry and $ff;
Carry := Carry shr 8;
end;
if Carry <> 0 then Exit; // overflow
end;
// We're succesful
Result := True;
end;
{Bytes: array of byte (0..255); Dest: array of Byte(0..Radix-1)}
function DecodeBaseX( const Bytes: array of Byte; var Dest: array of Byte; Radix: Integer ): Boolean;
var
i,j,Carry: Integer;
B: array of Byte;
begin
// We're unsuccesful up to now
Result := False;
// Copy data
if Length( Bytes ) = 0 then Exit;
SetLength( B, Length( Bytes ) );
System.Move( Bytes[ 0 ], B[ 0 ], Length( B ) );
// fill in the details
for i := High( Dest ) downto 0 do begin
Carry := 0;
for j := High( Bytes ) downto 0 do begin
Carry := Carry shl 8 + B[ j ];
B[ j ] := Carry div Radix; Carry := Carry mod Radix;
end;
Dest[ i ] := Carry;
end;
// Check if we collected all the bits
Carry := 0;
for i := 0 to High( B ) do Carry := Carry or B[ i ];
// We're succesful if no bits stayed pending.
Result := ( Carry = 0 );
end;
Then transform the base 35 bytes into characters:
function EncodeKeyToString( const Num128Bits: array of Byte ): Ansistring;
var
Src: array [0..15] of Byte; // your 128 bits
Dest: array [0..24] of Byte;
i: Integer;
const
EncodeTable: AnsiString = '0123456789ABCDEFGHIJKLMNPQRSTUVWXYZ';
// O is not present to make base 35. If you want a different code, be my guest.
begin
// Convert to an array of 25 values between 0-35
System.Move( Num128Bits[ 0 ], Src[ 0 ], Length( Src ) ); // Copy data in our private placeholder
DecodeBaseX( Src, Dest, 35 );
// Convert to a representable string
SetLength( Result, Length( Dest ) );
for i := 0 to High( Dest ) do begin
Assert( Dest[ i ] < Length( EncodeTable ) );
Result[ i + 1 ] := EncodeTable[ 1 + Dest[ i ] ];
end;
end;
I don't think you need 128 bit math..
Good luck!
Related
I have a string like '10011011001', And I wish to convert this string into Hex string, what is the best way to do that.
The OP clarified that the input string's length is <= 32. Then the problem becomes simpler.
There are many possible solutions. One of them is this:
function BinStrToHex32(const S: string): string;
begin
var LValue: UInt32 := 0;
for var i := 1 to S.Length do
case S[i] of
'0', '1':
LValue := LValue shl 1 or Ord(S[i] = '1');
else
raise Exception.CreateFmt('Invalid binary number: %s', [S]);
end;
Result := IntToHex(LValue);
end;
which IMHO is quite readable and performs some validation. (For bonus points, you can add overflow checking.)
If there were no restriction to the input string length, then I'd do something like this:
function BinStrToHexStr(const S: string): string;
const
HexDigits: array[0..$F] of Char = '0123456789ABCDEF';
begin
if S.Length mod 8 <> 0 then
raise Exception.Create('Invalid binary string.');
SetLength(Result, S.Length div 4);
var LNibble: Byte := 0;
var c := 0;
for var i := 1 to S.Length do
begin
LNibble := LNibble shl 1 or Ord(S[i] = '1');
if i mod 4 = 0 then
begin
Inc(c);
Result[c] := HexDigits[LNibble];
LNibble := 0;
end;
end;
end;
In my project I receive data from a tcp connection with a custom protocol in packets of 1095 bytes, then I must look for a sync word and try to show gray scale image.
At first step I read data and save them in a TStringList fifo
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
rowFrame : string;
data: TIdBytes;
begin
offReCStatus := false;
repeat
AContext.Connection.IOHandler.ReadBytes(data, 1099, False);
rowFrame :='';
for I := 0 to length(data)-1 do
begin
rowFrame := rowFrame + (data[i].ToHexString);
end;
tcpFrameList.Append( rowFrame );
until offReCStatus = true;
end;
Then in a separated thread, I try the data from the list.
{I added some comments in code}
Get first string from string list
Convert it to binary and append to previous data
Find sync word and copy data after sync word
Split image data to 1024 * 10 bits to load image
Draw image from data
Find new sync word(number 3)
Note: one very important thing is the sync-word is not byte,its bits and can start from middle of a byte for example 10 101011-00010101-00001100-10011001-01111111-00 111111 in this case 10 at first and 111111 at the end merged to sync word and its not AC543265FC any more.in the past in fpga I wrote code that shift the bits until find the 40 bits sync word but i don't know how this can be done in Delphi!
procedure TMyThread.Execute;
var
str3,str4,frameStr,frameId,strData, str6 : string;
iPos,y ,imageBit , frameIdNum :integer;
imageRol : TStringList;
begin
while not Terminated do
begin
FTermEvent.WaitFor( 500 );
if not Terminated then
begin
while tcpFrameList.Count >0 do //process que
begin
try
dta := dta + HexStrToBinStr(tcpFrameList[0]);//convert hex data to binary string and append to olddata
tcpFrameList.Delete(0);//delete converted thread
str3 := '1010110001010100001100100110010111111100';//sync word "AC543265FC"
iPos := pos( str3 , dta );//find 1st sync word in binary data
while dta.Length>20000 do //process data to find sync words
begin
Delete(dta,1, iPos-1 );//delete data until first sync word
str4 := copy( dta , 1, 12240);//copy image frame data after sync word
Delete(dta,1, 12240 );//delete image frame data that copied
strData := copy(BinToHex(str4),11); //hex image data
frameId := copy( strData , 1, 6 ); //get image column id from data
frameStr := copy( strData , 107, 330 );//get image color data as protocol
frameStr := frameStr + copy( strData , 501, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 1011, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 1521, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 2031, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 2541, 446 );//get image data as in protocol
imageBin := HexStrToBinStr( frameStr );
//now we have 10240 bit that for one frame column .10240 is 1024 of 10 bits for each pixel
imageRol := TstringList.Create;
imageRol := spliToLength( imageBin,10);//split 10240 to 1024 *10
frameIdNum := HexToDec(frameId);//frame id to show image
//application.ProcessMessages;
TThread.Synchronize (TThread.CurrentThread,
procedure () var y,n:integer;
begin
form1.Image1.Width := frameIdNum+1;//set TImage width
for y := 0 to imageRol.Count-1 do //process imageRol to grab 1024 pixel color of new column
begin
str6 := imageRol[y];
imageBit := trunc( BinToDec( str6 ) /4 );//div 10bit(1024) to 4 to get a number 0-255 for color
form1.Image1.Canvas.Pixels[frameIdNum ,y)] := RGB( imageBit , imageBit , imageBit );//gray scale image
end;
end);
iPos := pos( str3 , dta );
end;
except
on E : Exception do
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
form1.Memo1.Lines.Add(E.ClassName+' , message: '+E.Message);
end);
end;
end;
end;
end;
end;
The code above is working good but its slow..
I don't know how can process data as bits so try to convert data between hex and string to complete the process. Is there a way to do this job without any hex converting from tcp layer!?
I commented the code to explain what happening.but tell me to add some more data where necessary.
Here is an example how you could process the Binary data.
DISCLAMER
This code sample is far from optimized as I tried to keep it simple so one can grasp the concept how to process binary data.
The main concept here is that we have a 40 bit sync word (marker) but since we are dealing with individual bits, it can be on a non byte boundary. So all we need to do is read at least 48 bits (6 bytes) into a 64 bit integer and shift the bits to the right until we find our marker. I did not include the RGB pixel extraction logic, I leave that as an exercise for you :), I think you can decode it with WIC as GUID_WICPixelFormat32bppBGR101010
program SO59584303;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Classes,
System.SysUtils;
type ImageArray = TArray<Byte>;
const FrameSync : UInt64 = $AC543265FC; // we need Int64 as our marker is > 32 bits
function GetByte(const Value : UInt64; const ByteNum : Byte) : Byte; inline;
begin
Result := (Value shr ((ByteNum-1)*8)) and $FF ;
end;
procedure WriteInt64BigEndian(const Value: UInt64; NumberOfBytes : Integer; var Stream : TBytes; var Ps : Integer);
var
I : Integer;
begin
for I := NumberOfBytes downto 1 do
begin
Stream[Ps] := GetByte(Value, I);
Inc(Ps);
end;
end;
function ReadInt64BigEndian(const NumberOfBytes : Integer; const Stream : TBytes; var Ps : Integer) : UInt64;
var
I : Integer;
B : Byte;
begin
Result := 0;
for I := NumberOfBytes downto 1 do
begin
B := Stream[Ps];
Result := Result or (UInt64(B) shl ((I-1)* 8));
Inc(Ps);
// sanity check
if Ps >= Length(Stream) then
Exit;
end;
end;
procedure ReadPixelData(const Stream : TBytes; Var Ps : Integer; const Shift : Byte; var Buffer : ImageArray);
// our buffer
var
I : UInt64;
BPos : Integer;
begin
BPos := 0;
// 1024 * 10 bit pixel = 10240 bits = 1280 bytes // initialize buffer
SetLength(Buffer, 1280);
// fill with 0's
FillChar(Buffer[0], Length(Buffer), 0);
if Shift = 0 then
begin
// if we are byte boundary, we can just copy our data
Move(Stream[Ps], Buffer[0], Length(Buffer));
Inc(Ps, Length(Buffer));
end
else
while Bpos < Length(Buffer) do
begin
// Read 8 bytes at a time and shift x bits to the right, mask off highest byte
// this means we can get max 7 bytes at a time
I := (ReadInt64BigEndian(8, Stream, Ps) shr Shift) and $00FFFFFFFFFFFFFF;
// Write 7 bytes to our image data buffer
WriteInt64BigEndian(I, 7, Buffer, BPos);
// go one position back for the next msb bits
Dec(Ps);
end;
end;
procedure WritePixelData(var Stream : TBytes; Var Ps : Integer; var Shift : Byte);
var
Count : Integer;
ByteNum : Byte;
Data : UInt64;
begin
for Count := 1 to 160 do
begin
// write four bytes at a time, due to the shifting we get 5 bytes in total
Data := $F1F2F3F4;
if (Shift > 0) then
begin
// special case, we need to fillup shift bits on last written byte in the buffer with highest byte from our UInt64
Data := Data shl Shift;
Stream[Ps-1] := Stream[Ps-1] or GetByte(Data, 5);
end;
WriteInt64BigEndian(Data, 4, Stream, Ps);
Data := $F5F6F7F8;
if (Shift > 0) then
begin
// special case, we need to fillup shift bits on last written byte in the buffer with highest byte from our UInt64
Data := Data shl Shift;
Stream[Ps-1] := Stream[Ps-1] or GetByte(Data, 5);
end;
WriteInt64BigEndian(Data, 4, Stream, Ps);
end;
end;
procedure GenerateData(var Stream : TBytes);
var
Count : Integer;
I : UInt64;
Ps : Integer;
Shift : Byte;
begin
Count := 1285*4+10;
SetLength(Stream, Count); // make room for 4 Imageframes (1280 bytes or 10240 bits) and 5 byte marker (40 bits) + 10 bytes extra room
FillChar(Stream[0], Count, 0);
Ps := 1;
// first write some garbage
Stream[0] := $AF;
// our first marker will be shifted 3 bits to the left
Shift := 3;
I := FrameSync shl Shift;
// write our Framesync (40+ bits = 6 bytes)
WriteInt64BigEndian(I, 6, Stream, Ps);
// add our data, 1280 bytes or 160 times 8 bytes, we use $F1 F2 F3 F4 F5 F6 F7 F8 as sequence
// (fits in Int 64) so that we can verify our decoding stage later on
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AE;
Inc(Ps);
// our second marker will be shifted 2 bits to the left
Shift := 2;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AD;
Inc(Ps);
// our third marker will be shifted 1 bit to the left
Shift := 1;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AC;
Inc(Ps);
// our third marker will be shifted 5 bits to the left
Shift := 5;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
SetLength(Stream, Ps-1)
end;
procedure DecodeData(const Stream : TBytes);
var
Ps : Integer;
OrgPs : Integer;
BPos : Integer;
I : UInt64;
Check : UInt64;
Shift : Byte;
ByteNum : Byte;
ImageData : ImageArray;
begin
Ps := 0;
Shift := 0;
while Ps < Length(Stream) do
begin
// try to find a marker
// determine the number of bytes we need to read, 40bits = 5 bytes,
// when we have shifted bits this will require 6 bytes
if Shift = 0 then
ByteNum := 5
else
ByteNum := 6;
// save initial position in the stream
OrgPs := Ps;
// read our marker
I := ReadInt64BigEndian(ByteNum, Stream, Ps);
// if we have shifted bits, shift them on byte boundary and make sure we only have the 40 lower bits
if Shift > 0 then
I := (I shr Shift) and $FFFFFFFFFF;
if I = FrameSync then
begin
// we found our marker, process pixel data (ie read next 10240 bits, taking shift into account)
// If we have shift, our first bits will be found in the last marker byte, so go back one position in the stream
if Shift > 0 then
Dec(Ps);
ReadPixelData(Stream, Ps, Shift, ImageData);
// process Image array accordingly, here we will just check that we have our written data back
BPos := 0;
Check := $F1F2F3F4F5F6F7F8;
for ByteNum := 1 to 160 do
begin
I := ReadInt64BigEndian(8, ImageData, BPos);
// if our data is not correct, raise error
Assert(I = Check, 'error decoding image data');
end;
end
else
begin
Ps := OrgPs;
// we did not find our marker, advance 1 bit
Inc(Shift);
if Shift > 7 then
begin
// reset shift value
Shift := 0;
// advance to next byte boundary
Inc(Ps);
end;
end;
end;
end;
Var
AStream : TBytes;
begin
try
GenerateData(AStream);
DecodeData(AStream);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
I have a n number of strings which I need to merge into n number of strings in multiple combinations/permutations. the string cannot repeat itself and combination in single merge doesnt matter ( S1, S2 ) = ( S2, S1 )...
This is used when building a tree model and it decided which combination of characteristics is the best to merge...
This code is what I've wrote for permutations. List contains characteristic attributes, Splits is the number of splits I want to make , SplitList return which attributes need to be merge together... for binary tree I input "2" for Splits and for non-binary tree I run a cycle to return the best value for each of the splits .
I.E.
I have "A", "B", "C", "D", "E", "F".
If i Need to merge into 2 string
2. "A,B,C" and "D,E,F" or "A,C,E" and "B,D,F" or "A,E,F" and "B,C,D"
3. "A,B, and "C,D" and "E,F" or "A,D" and "C,B" and "E,F"
Also minimum number in a string is 1, maximum n-1.
I.E
2. "A" and "B,C,D,E,F" or "C" and "A,B,D,E,F" is a valid merging
function TSplitEngine.doTest(List: TList; Splits: Integer; var SplitList : TArray<Integer>): Double;
var
i, j, SplitNo, Pointer : Integer;
tmpNode : TDTreeNode;
CurRes, CurOut : Double;
TestArr : RTestArr;
ResArr: TArray<double>;
SplitStr : String;
DoSplit, FindSplit : Boolean;
TestList : TArray<Integer>;
begin
Result := DefaultVal;
SetLength( TestList, Splits );
for i := 0 to Length( TestList ) - 1 do
TestList[ i ] := i + 1;
TestArr.Size := Splits + 1;
DoSplit := True;
while DoSplit do
begin
Inc(Iteration);
TestArr.Clear;
for i := 0 to List.Count - 1 do
begin
tmpNode := TDTreeNode( List[ i ] );
j := 0;
FindSplit := True;
While ( j < Length( TestList ) ) and ( FindSplit ) do
begin
if i < TestList[ j ] then
begin
Combine Characteristics
FindSplit := False;
end
else if ( i >= TestList[ Length( TestList ) - 1 ] ) then
begin
Combine last split characteristics
FindSplit := False;
end;
inc( j );
end;
TestArr.AllTotal := TestArr.AllTotal + ( tmpNode.Goods + tmpNode.Bads );
end;
//CalcNode returns the result of this particular splits
CurRes := CalcNode( TestArr );
SetLength( ResArr, 2 );
ResArr[ 1 ] := CurRes;
if IsBetter( CurRes, Result ) then
begin
Result := CurRes;
SplitList := Copy( TestList, 0, Length( TestList ) );
end;
SplitNo := 1;
FindSplit := True;
//Move the split like a pointer...
i := Length( TestList ) - 1;
while ( i >= 0 ) and FindSplit do
begin
if ( TestList[ i ] < ( List.Count - SplitNo ) ) then
begin
Pointer := TestList[ i ] + 1;
for j := i to Length( TestList ) - 1 do
begin
TestList[ j ] := Pointer;
inc( Pointer );
end;
FindSplit := False;
end
else if ( i = 0 ) then
DoSplit := False;
inc ( SplitNo );
Dec( i );
end;
end;
end;
the permutation code seems to be working and the only thing to do would be to tidy it up.
I've tried a few times to convert this code to do combinations but never seemed to work.
I have old code for generation of set partitions with set size <= 10 (due to set comparison implemented through strings). Note that number of partitions for n=10 is 115975 (Bell number).
Procedure generates non-repeating partitions of set into KP parts, so you have to go through all KP values.
Part of output including some two-parts and some three-parts partitions:
1,4 | 2,3,5 |
1,4,5 | 2,3 |
1,5 | 2,3,4 |
1 | 2 | 3,4,5 |
1 | 2,3 | 4,5 |
1 | 2,3,4 | 5 |
procedure generate_multi_partitions(values: array of Integer; KP: Integer);
var
n, i: Integer;
avail: array of Boolean;
output: array of TStringList;
procedure foo(k: Integer); forward;
procedure bar(k, i: Integer);
var
j: Integer;
begin
output[k].add(IntToStr(values[i]));
avail[i] := False;
foo(k + 1);
for j := i + 1 to n - 1 do
if avail[j] and ((j = 0) or (values[j - 1] <> values[j]) or
(not avail[j - 1])) then
bar(k, j);
output[k].Delete(output[k].Count - 1);
avail[i] := True;
end;
procedure foo(k: Integer);
var
i, j: Integer;
s: string;
begin
if (k >= 2) and (output[k - 2].CommaText > output[k - 1].CommaText) then
Exit;
if k = KP - 1 then begin
output[k].Clear;
for i := 0 to n - 1 do
if avail[i] then
output[k].add(IntToStr(values[i]));
if (output[k].Count > 0) and
((k = 0) or (output[k - 1].CommaText <= output[k].CommaText)) then
begin
s := '';
for j := 0 to KP - 1 do
s := s + output[j].CommaText + ' | ';
Memo1.Lines.add(s);
end;
output[k].Clear;
end
else
for i := 0 to n - 1 do
if avail[i] then begin
bar(k, i);
Exit;
end;
end;
begin
n := length(values);
SetLength(avail, n);
SetLength(output, KP);
for i := 0 to KP - 1 do
output[i] := TStringList.Create;
for i := 0 to n - 1 do
avail[i] := True;
foo(0);
for i := 0 to KP - 1 do
output[i].Free;
end;
var
parts: Integer;
begin
for parts := 1 to 5 do
generate_multi_partitions([1, 2, 3, 4, 5], parts);
end;
I'm working on program that shows all narcissistic numbers from 0 to max. Where max is value typed by user. I got all code already now I am trying to improve it. So due this I have few questions.
I need to check every digit of number and make them to the n-th power. So I decided to create tab[0..9] which contains indexOfTab to n-th power and then when I sum all digits from number it works like that:
sum := sum + tab[x]; //where x is the digit that is currently checked
Now i am wondering if comparing is faster than this:
sum:= sum + power(x,n);
I also want to catch if sum overflow. And I know how to do this by if .. then. But I am wondering if there is a way to NOT checking on every operation if sum change sign, only that program will CATCH that this variable overflowed and then it will do some code.
EDIT:
while (tmpNum>0) do //tmpNum - digit of currenlty checked number(curNum)
begin
try
suma:= suma+tab[tmpNum mod 10]; //suma =0
//tab[x] = x^(curNum.Length);
Except
suma:= 0;
tmpNum:=0;
//here do something more
end;
tmpNum:= tmpNum div 10; //divide number to get next modulo
end;
First of all your method of using a table of values will be by far the fastest method to use. As far as overflow is concerned, I would like to come back to that shortly.
If you are serious about running as fast possible, there are ways of making things go faster by analysing the problem a little. It is very tempting to assume that to find all values when you know how to find one, you just need to iterate through all values up to your max. It is rarely true that this is efficient, I am afraid, and this is such a case.
To see that this is true, just think about calculating the numbers 1034, 1304, 1403 and 1340. All the calculations are actually exactly the same, so we can reduce our calculations very substantially by only examining digits going in descending order (in our case 4310). Having calculated 4^4 + 3^4 + 1^ 4 + 0^4 we then simply need to check that the result contains the digits 4,3,1 and 0. If it does then the number calculated is narcissistic. This notion also helps with minimising overflow tests, because it means that if 8000, for example overflows, there is no point in even checking larger numbers. On the other hand there is a balance between short circuiting, and introducing complexity through if statement.
The down side to this is that numbers are not generated in order, so some sort of sorting may be required at the end. (I have not done this). On the upside, though, it allows for a parallel for loop to be used. In practice this did not save much time (maybe 10% on my machine) because the overheads of using multiple threads largely offset the gains in doing things in parallel. The code below shows both ways.
The program below allows the user to input a number of digits (rather than a maximum value) to test and also deals with overflows. I did that for simplicity of coding. On my machine calculation all 19 digit narcissistic < 2^63 took about 6 seconds.
unit UnitNarcisistCalc;
interface
uses
System.Classes,
System.SysUtils,
System.Threading,
System.SyncObjs;
type
TCalcArray = array[ 0..9] of int64;
TNarcisistCalc = class
(* Calculated narcisistic number of a certain size *)
private
class function CheckResult( const pSum : int64; const DigitsUsed : TCalcArray; const DigitCount : integer ) : boolean;
class procedure AddADigit( const pDigit, pDigitsLeft : integer; const pSumSoFar : int64;
const pPowers, DigitsUsed : TCalcArray;
const pResults : TStrings; const DigitCount : integer );
protected
public
class procedure CalcNos( const pOfSize : integer; const pResults : TStrings;
pParallel : boolean );
end;
implementation
{ TNarcisistCalc }
class procedure TNarcisistCalc.AddADigit(const pDigit, pDigitsLeft: integer;
const pSumSoFar: int64; const pPowers, DigitsUsed: TCalcArray;
const pResults: TStrings; const DigitCount : integer );
var
iNewSum : int64;
i : integer;
iDigitsUsed : TCalcArray;
iOverflowMsg : string;
j: Integer;
begin
{
This recursive function builds the sum progressively until
pDigitsLeft = 0; We are careful to make all parameters const
so we don't accidently reuse anything.
}
iDigitsUsed := DigitsUsed;
iNewSum := pSumSoFar + pPowers[ pDigit ];
inc( iDigitsUsed[ pDigit ]);
if iNewSum < 0 then
begin
// overflow - so ditch this strand.
iOverflowMsg := 'Overflowed while evaluating ';
for i := 9 downto 0 do
begin
for j := 1 to iDigitsUsed[ i ] do
begin
iOverflowMsg := iOverflowMsg+ IntToStr( i );
end;
end;
pResults.Add( iOverflowMsg );
exit;
end;
if pDigitsLeft > 1 then // because we are not descrementing pDigitsLeft left even though logically we should
begin
for i := 0 to pDigit do
begin
AddADigit( i, pDigitsLeft - 1, iNewSum, pPowers, iDigitsUsed, pResults, DigitCount + 1 );
end;
end
else
begin
// lowest level
if CheckResult( pSumSoFar, iDigitsUsed, DigitCount + 1 ) then
begin
pResults.Add( IntToStr( pSumSoFar ));
end;
end;
end;
class procedure TNarcisistCalc.CalcNos(const pOfSize: integer;
const pResults: TStrings; pParallel : boolean);
var
fPowers : TCalcArray;
fUsed : TCalcArray;
i: Integer;
j: Integer;
iMaxDigit : integer;
iOverflow : Boolean;
iSum : int64;
iOverflowMsg : string;
iStrings : array[ 0.. 9 ] of TStringList;
begin
// calculate the powwers
pResults.Clear;
iOverFlow := FALSE;
iMaxDigit := 0;
for i := 0 to 9 do
begin
fPowers[ i ] := i;
fUsed[ i ] := 0;
for j := 2 to pOfSize do
begin
fPowers[ i ] := fPowers[ i ] * i;
if fPowers[ i ] < 0 then
begin
// overflow
iOverflow := TRUE;
iOverflowMsg := 'Overflowed while evaluating ' + IntToStr( i ) + '^' + IntToStr( pOfSize );
pResults.Add( iOverflowMsg );
break;
end;
end;
if iOverflow then
begin
break;
end
else
begin
iMaxDigit := i;
end;
end;
// we have set up our tabs and also prepared to not test any digits that
// would automatically give an overflow
if pParallel then
begin
TParallel.&For( 1, iMaxDigit, procedure(I : Integer )
var
iSum : int64;
begin
iStrings[ i ] := TStringList.Create;
iSum := 0;
AddADigit( i, pOfSize, iSum, fPowers, fUsed, iStrings[ i ], 0 );
end);
for i := 1 to iMaxDigit do
begin
pResults.AddStrings( iStrings[ i ]);
iStrings[ i ].Free;
end;
end
else
begin
for i := 1 to iMaxDigit do
begin
iSum := 0;
AddADigit( i, pOfSize, iSum, fPowers, fUsed, pResults, 0 );
end;
end;
end;
class function TNarcisistCalc.CheckResult(const pSum: int64;
const DigitsUsed: TCalcArray; const DigitCount : integer): boolean;
var
iDigitsUsed : TCalcArray;
iDigit, iSum : int64;
iDigitCount : integer;
begin
{ what we are doing here is checking if pSum contains the
same digits that were used to create it in the first place. }
iDigitsUsed := DigitsUsed;
iDigitCount := DigitCount;
iSum := pSum;
while iSum > 0 do
begin
iDigit := iSum mod 10;
iSum := iSum Div 10;
if iDigitsUsed[ iDigit ] > 0 then
begin
dec( iDigitsUsed[ iDigit ]);
dec( iDigitCount );
end
else
begin
Result := FALSE;
exit;
end;
end;
Result := iDigitCount = 0;
end;
end.
It would be interesting to know how this approach compares with yours.
The result for 19 digit numbers is shown below:
(Non parallel)
and
(Parallel)
source array(4 bytes)
[$80,$80,$80,$80] =integer 0
[$80,$80,$80,$81] = 1
[$80,$80,$80,$FF] = 127
[$80,$80,$81,$01] = 128
need to convert this to integer.
below is my code and its working at the moment.
function convert(b: array of Byte): Integer;
var
i, st, p: Integer;
Negative: Boolean;
begin
result := 0;
st := -1;
for i := 0 to High(b) do
begin
if b[i] = $80 then Continue // skip leading 80
else
begin
st := i;
Negative := b[i] < $80;
b[i] := abs(b[i] - $80);
Break;
end;
end;
if st = -1 then exit;
for i := st to High(b) do
begin
p := round(Power(254, High(b) - i));
result := result + b[i] * p;
result := result - (p div 2);
end;
if Negative then result := -1 * result
end;
i'm looking for a better function?
Update:
file link
https://drive.google.com/file/d/0ByBA4QF-YOggZUdzcXpmOS1aam8/view?usp=sharing
in uploaded file ID field offset is from 5 to 9
NEW:
Now i got into new problem which is decoding date field
Date field hex [$80,$8F,$21,$C1] -> possible date 1995-12-15
* in uploaded file date field offset is from 199 to 203
Just an example of some improvements as outlined by David.
The array is passed by reference as a const.
The array is fixed in size.
The use of floating point calculations are converted directly into a constant array.
Const
MaxRange = 3;
Type
TMySpecial = array[0..MaxRange] of Byte;
function Convert(const b: TMySpecial): Integer;
var
i, j: Integer;
Negative: Boolean;
Const
// Pwr[i] = Round(Power(254,MaxRange-i));
Pwr: array[0..MaxRange] of Cardinal = (16387064,64516,254,1);
begin
for i := 0 to MaxRange do begin
if (b[i] <> $80) then begin
Negative := b[i] < $80;
Result := Abs(b[i] - $80)*Pwr[i] - (Pwr[i] shr 1);
for j := i+1 to MaxRange do
Result := Result + b[j]*Pwr[j] - (Pwr[j] shr 1);
if Negative then
Result := -Result;
Exit;
end;
end;
Result := 0;
end;
Note that less code lines is not always a sign of good performance.
Always measure performance before optimizing the code in order to find real bottlenecks.
Often code readability is better than optimizing over the top.
And for future references, please tell us what the algorithm is supposed to do.
Code for testing:
const
X : array[0..3] of TMySpecial =
(($80,$80,$80,$80), // =integer 0
($80,$80,$80,$81), // = 1
($80,$80,$80,$FF), // = 127
($80,$80,$81,$01)); // = 128
var
i,j: Integer;
sw: TStopWatch;
begin
sw := TStopWatch.StartNew;
for i := 1 to 100000000 do
for j := 0 to 3 do
Convert(X[j]);
WriteLn(sw.ElapsedMilliseconds);
ReadLn;
end.