program app;
// on WINDOWS use QueryPerformanceCounter
{$ifdef WINDOWS}
uses SysUtils, Windows, math;
function GetTickCount64: QWord;
const
freq: int64 = 0;
begin
if freq = 0 then QueryPerformanceFrequency(@freq);
QueryPerformanceCounter(@result);
result := trunc((result / freq) * 1000);
end;
{$else}
uses SysUtils;
{$endif}
function IsAnagramALLIGATOR(const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
type
TFreq = array [32..127+1] of Int16;
const
FZero: TFreq = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0
);
var
i: SizeInt;
freq: TFreq;
ch: uint8;
begin
Result := False;
FillQWord(freq, SizeOf(freq) div 8, 0);
i:=0;
while i<Length(S1) do
begin
inc(i);
ch:=ord(S1[i]);
case ch of
32..64, 91..127: Inc(freq[ch]);
65..90: Inc(freq[ch or $20]);
else
if ExceptionOnError then Raise ERangeError.CreateFmt('IsAnagram: illegal character in S1 at position %d',[i]);
end;
end;
i:=0;
while i<Length(S2) do
begin
inc(i);
ch:=ord(S2[i]);
case ch of
32..64, 91..127: Dec(freq[ch]);
65..90: Dec(freq[ch or $20]);
else
if ExceptionOnError then Raise ERangeError.CreateFmt('IsAnagram: illegal character in S1 at position %d',[i]);
end;
end;
if IgnoreSpaces then
begin
Result := CompareDWord(freq[Low(freq)+1], FZero[Low(FZero)], SizeOf(TFreq) div 4)=0;
end else
begin
Result := CompareDWord(freq[Low(freq)], FZero[Low(FZero)], SizeOf(TFreq) div 4)=0;
end;
end;
function IsAnagram_avk(const s1, s2: string; aIgnoreSpaces: Boolean = True; aExceptionOnError: Boolean = False): Boolean;
type
{$IFDEF CPU64}
TChunk = QWord;
{$ELSE}
TChunk = DWord;
{$ENDIF}
PChunk = ^TChunk;
const
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
{$IFDEF CPU64}
MASK = 7;
{$ELSE}
MASK = 3;
{$ENDIF}
{$ENDIF}
{$IFDEF CPU64}
BITS5 = QWord($2020202020202020);
BITS6 = QWord($4040404040404040);
BITS7 = QWord($8080808080808080);
{$ELSE}
BITS5 = DWord($20202020);
BITS6 = DWord($40404040);
BITS7 = DWord($80808080);
{$ENDIF}
ERR_FMT = 'Illegal character in %s, position %d(#%d)';
function Invalid(aOfs: Integer; aFirstArg: Boolean): Boolean;
begin
Invalid := False;
if aExceptionOnError then
if aFirstArg then
raise ERangeError.CreateFmt(ERR_FMT, ['s1', aOfs, Ord(s1[aOfs])])
else
raise ERangeError.CreateFmt(ERR_FMT, ['s2', aOfs, Ord(s2[aOfs])])
end;
function InvalidUp(aFlags: TChunk; aOfs: Integer; aFirstArg: Boolean): Boolean;
begin
{$IFDEF ENDIAN_BIG}aFlags := SwapEndian(aFlags);{$ENDIF}
Inc(aOfs,{$IFDEF CPU64}BsfQWord{$ELSE}BsfDWord{$ENDIF}(aFlags) div 8);
Result := Invalid(aOfs, aFirstArg);
end;
function InvalidLo(aFlags: TChunk; aOfs: Integer; aFirstArg: Boolean): Boolean;
begin
{$IFDEF ENDIAN_BIG}aFlags := SwapEndian(aFlags);{$ENDIF}
Inc(aOfs,{$IFDEF CPU64}BsfQWord{$ELSE}BsfDWord{$ENDIF}(aFlags xor BITS5) div 8);
Result := Invalid(aOfs, aFirstArg);
end;
type
{$IFDEF CPU64}
TShortBuffer = array[0..8] of QWord;
{$ELSE}
TShortBuffer = array[0..17] of DWord;
{$ENDIF}
const
{$PUSH}{$J-}
{$IFDEF CPU64}
ZERO_BUF: TShortBuffer = (0,0,0,0,0,0,0,0,0);
{$ELSE}
ZERO_BUF: TShortBuffer = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
{$ENDIF}
CI_MAP: array[32..127] of Byte = (
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101
);
{$POP}
function TestShort(const s1, s2: string): Boolean;
var
Buf: TShortBuffer;
Counter: array[32..101] of ShortInt absolute Buf;
p, pEnd: PByte;
begin
Buf := ZERO_BUF;
p := PByte(s1);
PEnd := p + Length(s1);
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
while (p < pEnd) and (SizeUInt(p) and MASK <> 0) do begin
if p^ in [32..127] then Inc(Counter[CI_MAP[p^]])
else exit(Invalid(Succ(p - PByte(s1)), True));
Inc(p);
end;
{$ENDIF}
while p < pEnd - SizeOf(TChunk) do begin
if PChunk(p)^ and BITS7 <> 0 then
exit(InvalidUp(PChunk(p)^ and BITS7, p-PByte(s1)+1, True));
if (PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1) < BITS5 then
exit(InvalidLo((PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1), p-PByte(s1)+1, True));
Inc(Counter[CI_MAP[p[0]]]); Inc(Counter[CI_MAP[p[1]]]);
Inc(Counter[CI_MAP[p[2]]]); Inc(Counter[CI_MAP[p[3]]]);
{$IFDEF CPU64}
Inc(Counter[CI_MAP[p[4]]]); Inc(Counter[CI_MAP[p[5]]]);
Inc(Counter[CI_MAP[p[6]]]); Inc(Counter[CI_MAP[p[7]]]);
{$ENDIF}
Inc(p, SizeOf(TChunk));
end;
while p < pEnd do begin
if p^ in [32..127] then Inc(Counter[CI_MAP[p^]])
else exit(Invalid(Succ(p - PByte(s1)), True));
Inc(p);
end;
p := PByte(s2);
PEnd := p + Length(s2);
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
while (p < pEnd) and (SizeUInt(p) and MASK <> 0) do begin
if p^ in [32..127] then Dec(Counter[CI_MAP[p^]])
else exit(Invalid(Succ(p - PByte(s2)), False));
Inc(p);
end;
{$ENDIF}
while p < pEnd - SizeOf(TChunk) do begin
if PChunk(p)^ and BITS7 <> 0 then
exit(InvalidUp(PChunk(p)^ and BITS7, p-PByte(s2)+1, False));
if (PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1) < BITS5 then
exit(InvalidLo((PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1), p-PByte(s2)+1, False));
Dec(Counter[CI_MAP[p[0]]]); Dec(Counter[CI_MAP[p[1]]]);
Dec(Counter[CI_MAP[p[2]]]); Dec(Counter[CI_MAP[p[3]]]);
{$IFDEF CPU64}
Dec(Counter[CI_MAP[p[4]]]); Dec(Counter[CI_MAP[p[5]]]);
Dec(Counter[CI_MAP[p[6]]]); Dec(Counter[CI_MAP[p[7]]]);
{$ENDIF}
Inc(p, SizeOf(TChunk));
end;
while p < pEnd do begin
if p^ in [32..127] then Dec(Counter[CI_MAP[p^]])
else exit(Invalid(Succ(p - PByte(s2)), False));
Inc(p);
end;
if aIgnoreSpaces then Counter[32] := 0;
{$IFDEF CPU64}
if Buf[0] or Buf[1] or Buf[2] or Buf[3] or Buf[4] or
Buf[5] or Buf[6] or Buf[7] or Buf[8] <> 0 then exit(False);
{$ELSE}
if Buf[0] or Buf[1] or Buf[2] or Buf[3] or Buf[4] or Buf[5] or Buf[6] or Buf[7] or
Buf[8] or Buf[9] or Buf[10] or Buf[11] or Buf[12] or Buf[13] or Buf[14] or
Buf[15] or Buf[16] or Buf[17] <> 0 then exit(False);
{$ENDIF}
Result := True;
end;
const
SHORT = 126;
var
Counter: array[32..127] of Integer;
p, pEnd: PByte;
I: Integer;
begin
if (s1 = '') or (s2 = '') then
exit(False);
if not aIgnoreSpaces and not aExceptionOnError and (Length(s1) <> Length(s2)) then
exit(False);
if Math.Max(Length(s1), Length(s2)) <= SHORT then
exit(TestShort(s1, s2));
FillChar(Counter, SizeOf(Counter), 0);
p := PByte(s1);
PEnd := p + Length(s1);
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
while (p < pEnd) and (SizeUInt(p) and MASK <> 0) do begin
if p^ in [32..127] then Inc(Counter[p^])
else exit(Invalid(Succ(p - PByte(s1)), True));
Inc(p);
end;
{$ENDIF}
while p < pEnd - SizeOf(TChunk) do begin
if PChunk(p)^ and BITS7 <> 0 then
exit(InvalidUp(PChunk(p)^ and BITS7, p-PByte(s1)+1, True));
if (PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1) < BITS5 then
exit(InvalidLo((PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1), p-PByte(s1)+1, True));
Inc(Counter[p[0]]); Inc(Counter[p[1]]);
Inc(Counter[p[2]]); Inc(Counter[p[3]]);
{$IFDEF CPU64}
Inc(Counter[p[4]]); Inc(Counter[p[5]]);
Inc(Counter[p[6]]); Inc(Counter[p[7]]);
{$ENDIF}
Inc(p, SizeOf(TChunk));
end;
while p < pEnd do begin
if p^ in [32..127] then Inc(Counter[p^])
else exit(Invalid(Succ(p - PByte(s1)), True));
Inc(p);
end;
for I := 97 to 122 do
if Counter[I] <> 0 then begin
Inc(Counter[I-32], Counter[I]);
Counter[I] := 0;
end;
p := PByte(s2);
PEnd := p + Length(s2);
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
while (p < pEnd) and (SizeUInt(p) and MASK <> 0) do begin
if p^ in [32..127] then Dec(Counter[p^])
else exit(Invalid(Succ(p - PByte(s2)), False));
Inc(p);
end;
{$ENDIF}
while p < pEnd - SizeOf(TChunk) do begin
if PChunk(p)^ and BITS7 <> 0 then
exit(InvalidUp(PChunk(p)^ and BITS7, p-PByte(s2)+1, False));
if (PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1) < BITS5 then
exit(InvalidLo((PChunk(p)^ and BITS5) or ((PChunk(p)^ and BITS6) shr 1), p-PByte(s2)+1, False));
Dec(Counter[p[0]]); Dec(Counter[p[1]]);
Dec(Counter[p[2]]); Dec(Counter[p[3]]);
{$IFDEF CPU64}
Dec(Counter[p[4]]); Dec(Counter[p[5]]);
Dec(Counter[p[6]]); Dec(Counter[p[7]]);
{$ENDIF}
Inc(p, SizeOf(TChunk));
end;
while p < pEnd do begin
if p^ in [32..127] then Dec(Counter[p^])
else exit(Invalid(Succ(p - PByte(s2)), False));
Inc(p);
end;
for I := 97 to 122 do
if Counter[I] <> 0 then
Inc(Counter[I-32], Counter[I]);
if aIgnoreSpaces then Counter[32] := 0;
I := 32;
while I < 95 do begin
if Counter[I] or Counter[I+1] or Counter[I+2] or Counter[I+3] <> 0 then
exit(False);
Inc(I, 4);
end;
if Counter[96] or Counter[123] or Counter[124] or Counter[125] or Counter[126] or Counter[127] <> 0 then
exit(False);
Result := True;
end;
procedure main;
const
ITERATIONS = 1000*1000*5;
var
i, c: integer;
s, d: string;
u: ptruint;
begin
//{$IFDEF kaka}
s:='1234567890';
d:='0 1 2 3 4 5 6 7 8 9 ';
//s := 'St a te';
//d := 'tas t e';
writeln('*** ROUND 1 ***');
writeln('s1 = ', s);
writeln('s2 = ', d);
writeln;
// test with invalid characters
//s += #1; d += #2;
// -------------------------------------
write('ALLIGATOR: IgnoreSpaces + ExceptionOnError':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, true, true) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk: IgnoreSpaces + ExceptionOnError':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, true, true) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('ALLIGATOR: IgnoreSpaces':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, true, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk: IgnoreSpaces':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, true, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('ALLIGATOR':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, false, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, false, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
writeln;
//s := 'night';
//d := 'THING';
writeln('*** ROUND 2 ***');
writeln('s1 = ', s);
writeln('s2 = ', d);
writeln;
// -------------------------------------
write('ALLIGATOR: IgnoreSpaces + ExceptionOnError':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, true, true) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk: IgnoreSpaces + ExceptionOnError':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, true, true) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('ALLIGATOR: IgnoreSpaces':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, true, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk: IgnoreSpaces':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, true, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('ALLIGATOR':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, false, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, false, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
writeln;
//{$ENDIF}
//s := 'Invalid';
//d := 'Diff length';
writeln('*** ROUND 3: Invalid chars ***');
writeln('s1 = ', s);
writeln('s2 = ', d);
writeln;
// -------------------------------------
write('ALLIGATOR: IgnoreSpaces + ExceptionOnError':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, true, true) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk: IgnoreSpaces + ExceptionOnError':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, true, true) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('ALLIGATOR: IgnoreSpaces':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, true, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk: IgnoreSpaces':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, true, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('ALLIGATOR':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagramALLIGATOR(s, d, false, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
write('avk':50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if IsAnagram_avk(s, d, false, false) then c += 1;
write(' | ', (GetTickCount64-u):4, ' ms');
write(' | result ', c);
writeln;
// -------------------------------------
readln;
end;
begin
main;
end.