hash set + hash map 2

git-svn-id: trunk@17299 -
This commit is contained in:
vladob 2011-04-10 20:31:01 +00:00
parent cda5fa8f32
commit ae8a0c44bb
5 changed files with 602 additions and 0 deletions

4
.gitattributes vendored
View File

@ -2318,6 +2318,8 @@ packages/fcl-stl/doc/vector.tex svneol=native#text/plain
packages/fcl-stl/doc/vectorexample.pp svneol=native#text/plain
packages/fcl-stl/src/garrayutils.pp svneol=native#text/plain
packages/fcl-stl/src/gdeque.pp svneol=native#text/plain
packages/fcl-stl/src/ghashmap.pp svneol=native#text/plain
packages/fcl-stl/src/ghashset.pp svneol=native#text/plain
packages/fcl-stl/src/gmap.pp svneol=native#text/plain
packages/fcl-stl/src/gpriorityqueue.pp svneol=native#text/plain
packages/fcl-stl/src/gqueue.pp svneol=native#text/plain
@ -2328,6 +2330,8 @@ packages/fcl-stl/src/gvector.pp svneol=native#text/plain
packages/fcl-stl/tests/clean svneol=native#text/plain
packages/fcl-stl/tests/garrayutilstest.pp svneol=native#text/plain
packages/fcl-stl/tests/gdequetest.pp svneol=native#text/plain
packages/fcl-stl/tests/ghashmaptest.pp svneol=native#text/plain
packages/fcl-stl/tests/ghashsettest.pp svneol=native#text/plain
packages/fcl-stl/tests/gmaptest.pp svneol=native#text/plain
packages/fcl-stl/tests/gmaptestzal.pp svneol=native#text/plain
packages/fcl-stl/tests/gpriorityqueuetest.pp svneol=native#text/plain

View File

@ -0,0 +1,216 @@
{
This file is part of the Free Pascal FCL library.
BSD parts (c) 2011 Vlado Boza
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY;without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$mode objfpc}
unit ghashmap;
interface
uses gvector, gutil, garrayutils;
const baseFDataSize = 8;
{Thash should have one class function hash(a:TKey, n:longint):longint which return uniformly distributed
value in range <0,n-1> base only on arguments, n will be always power of 2}
type
generic THashmapIterator<T, TTable>=class
public
var
Fh,Fp:SizeUInt;
FData:TTable;
function Next:boolean;
function GetValue:T;
end;
generic THashmap<TKey, TValue, Thash>=class
public
type
TPair=record
Value:TValue;
Key:TKey;
end;
var
private
type
TContainer = specialize TVector<TPair>;
TTable = specialize TVector<TContainer>;
var
FData:TTable;
FDataSize:SizeUInt;
procedure EnlargeTable;
public
type
TIterator = specialize THashmapIterator<TPair, TTable>;
constructor create;
destructor destroy;override;
procedure insert(key:TKey;value:TValue);inline;
function contains(key:TKey):boolean;inline;
function size:SizeUInt;inline;
procedure delete(key:TKey);inline;
function IsEmpty:boolean;inline;
function GetValue(key:TKey):TValue;inline;
property Items[i : TKey]: TValue read GetValue write Insert; default;
function Iterator:TIterator;
end;
implementation
function THashmap.Size:SizeUInt;inline;
begin
Size:=FDataSize;
end;
destructor THashmap.Destroy;
var i:SizeUInt;
begin
for i:=0 to FData.size do
(FData[i]).Destroy;
FData.Destroy;
end;
function THashmap.IsEmpty():boolean;inline;
begin
if Size()=0 then
IsEmpty:=true
else
IsEmpty:=false;
end;
procedure THashmap.EnlargeTable;
var i,j,h,oldDataSize:SizeUInt;
value:TPair;
begin
oldDataSize:=FData.size;
FData.resize(FData.size*2);
for i:=oldDataSize to FData.size-1 do
FData[i] := TContainer.create;
for i:=oldDataSize-1 downto 0 do begin
j := 0;
while j < (FData[i]).size do begin
value := (FData[i])[j];
h:=Thash.hash(value.key,FData.size);
if (h <> i) then begin
(FData[i])[j] := (FData[i]).back;
(FData[i]).popback;
(FData[h]).pushback(value);
end else
inc(j);
end;
end;
end;
constructor THashmap.create;
var i:longint;
begin
FDataSize:=0;
FData:=TTable.create;
FData.resize(baseFDataSize);
for i:=0 to baseFDataSize-1 do
FData[i]:=TContainer.create;
end;
function THashmap.contains(key:TKey):boolean;inline;
var i,h,bs:longint;
begin
h:=Thash.hash(key,FData.size);
bs:=(FData[h]).size;
for i:=0 to bs-1 do begin
if (((FData[h])[i]).Key=key) then exit(true);
end;
exit(false);
end;
function THashmap.GetValue(key:TKey):TValue;inline;
var i,h,bs:longint;
begin
h:=Thash.hash(key,FData.size);
bs:=(FData[h]).size;
for i:=0 to bs-1 do begin
if (((FData[h])[i]).Key=key) then exit(((FData[h])[i]).Value);
end;
end;
procedure THashmap.insert(key:TKey;value:TValue);inline;
var pair:TPair; i,h,bs:longint;
begin
h:=Thash.hash(key,FData.size);
bs:=(FData[h]).size;
for i:=0 to bs-1 do begin
if (((FData[h])[i]).Key=key) then begin
((FData[h]).mutable[i])^.value := value;
exit;
end;
end;
pair.Key := key;
pair.Value := value;
inc(FDataSize);
(FData[h]).pushback(pair);
if (FDataSize > 2*FData.size) then
EnlargeTable;
end;
procedure THashmap.delete(key:TKey);inline;
var h,i:SizeUInt;
begin
h:=Thash.hash(key,FData.size);
i:=0;
while i < (FData[h]).size do begin
if (((FData[h])[i]).key=key) then begin
(FData[h])[i] := (FData[h]).back;
(FData[h]).popback;
dec(FDataSize);
exit;
end;
inc(i);
end;
end;
function THashmapIterator.Next:boolean;
begin
inc(Fp);
if (Fp = (FData[Fh]).size) then begin
Fp:=0; inc(Fh);
while Fh < FData.size do begin
if ((FData[Fh]).size > 0) then break;
inc(Fh);
end;
if (Fh = FData.size) then exit(false);
end;
Next := true;
end;
function THashmapIterator.GetValue:T;
begin
GetValue:=(FData[Fh])[Fp];
end;
function THashmap.Iterator:TIterator;
var h,p:SizeUInt;
begin
h:=0;
p:=0;
while h < FData.size do begin
if ((FData[h]).size > 0) then break;
inc(h);
end;
if (h = FData.size) then exit(nil);
Iterator := TIterator.create;
Iterator.Fh := h;
Iterator.Fp := p;
Iterator.FData := FData;
end;
end.

View File

@ -0,0 +1,186 @@
{
This file is part of the Free Pascal FCL library.
BSD parts (c) 2011 Vlado Boza
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY;without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$mode objfpc}
unit ghashset;
interface
uses gvector, gutil, garrayutils;
const baseFDataSize = 8;
{Thash should have one class function hash(a:T, n:longint):longint which return uniformly distributed
value in range <0,n-1> base only on arguments, n will be always power of 2}
type
generic THashSetIterator<T, TTable>=class
public
var
Fh,Fp:SizeUInt;
FData:TTable;
function Next:boolean;
function GetValue:T;
end;
generic THashSet<T, Thash>=class
private
type
TContainer = specialize TVector<T>;
TTable = specialize TVector<TContainer>;
var
FData:TTable;
FDataSize:SizeUInt;
procedure EnlargeTable;
public
type
TIterator = specialize THashSetIterator<T, TTable>;
constructor create;
destructor destroy;override;
procedure insert(value:T);inline;
function contains(value:T):boolean;inline;
function size:SizeUInt;inline;
procedure delete(value:T);inline;
function IsEmpty:boolean;inline;
function Iterator:TIterator;
end;
implementation
function THashSet.Size:SizeUInt;inline;
begin
Size:=FDataSize;
end;
destructor THashSet.Destroy;
var i:SizeUInt;
begin
for i:=0 to FData.size do
(FData[i]).Destroy;
FData.Destroy;
end;
function THashSet.IsEmpty():boolean;inline;
begin
if Size()=0 then
IsEmpty:=true
else
IsEmpty:=false;
end;
procedure THashSet.EnlargeTable;
var i,j,h,oldDataSize:SizeUInt;
value:T;
begin
oldDataSize:=FData.size;
FData.resize(FData.size*2);
for i:=oldDataSize to FData.size-1 do
FData[i] := TContainer.create;
for i:=oldDataSize-1 downto 0 do begin
j := 0;
while j < (FData[i]).size do begin
value := (FData[i])[j];
h:=Thash.hash(value,FData.size);
if (h <> i) then begin
(FData[i])[j] := (FData[i]).back;
(FData[i]).popback;
(FData[h]).pushback(value);
end else
inc(j);
end;
end;
end;
constructor THashSet.create;
var i:longint;
begin
FDataSize:=0;
FData:=TTable.create;
FData.resize(baseFDataSize);
for i:=0 to baseFDataSize-1 do
FData[i]:=TContainer.create;
end;
function THashSet.contains(value:T):boolean;inline;
var i,h,bs:longint;
begin
h:=Thash.hash(value,FData.size);
bs:=(FData[h]).size;
for i:=0 to bs-1 do begin
if ((FData[h])[i]=value) then exit(true);
end;
exit(false);
end;
procedure THashSet.insert(value:T);inline;
begin
if (contains(value)) then exit;
inc(FDataSize);
(FData[Thash.hash(value,FData.size)]).pushback(value);
if (FDataSize > 2*FData.size) then
EnlargeTable;
end;
procedure THashSet.delete(value:T);inline;
var h,i:SizeUInt;
begin
h:=Thash.hash(value,FData.size);
i:=0;
while i < (FData[h]).size do begin
if ((FData[h])[i]=value) then begin
(FData[h])[i] := (FData[h]).back;
(FData[h]).popback;
dec(FDataSize);
exit;
end;
inc(i);
end;
end;
function THashSetIterator.Next:boolean;
begin
inc(Fp);
if (Fp = (FData[Fh]).size) then begin
Fp:=0; inc(Fh);
while Fh < FData.size do begin
if ((FData[Fh]).size > 0) then break;
inc(Fh);
end;
if (Fh = FData.size) then exit(false);
end;
Next := true;
end;
function THashSetIterator.GetValue:T;
begin
GetValue:=(FData[Fh])[Fp];
end;
function THashSet.Iterator:TIterator;
var h,p:SizeUInt;
begin
h:=0;
p:=0;
while h < FData.size do begin
if ((FData[h]).size > 0) then break;
inc(h);
end;
if (h = FData.size) then exit(nil);
Iterator := TIterator.create;
Iterator.Fh := h;
Iterator.Fp := p;
Iterator.FData := FData;
end;
end.

View File

@ -0,0 +1,99 @@
{$mode objfpc}
unit ghashmaptest;
interface
uses fpcunit, testregistry, ghashmap;
type hint=class
class function hash(a,n:SizeUInt):SizeUInt;
end;
type THashmaplli=specialize THashMap<longint, longint, hint>;
type TGHashmapTest = class(TTestCase)
Published
procedure HashmapTest1;
procedure HashmapTest2;
procedure HashmapTest3;
public
procedure Setup;override;
private
data:THashmaplli;
end;
implementation
class function hint.hash(a,n:SizeUInt):SizeUInt;
begin
hash:= (a xor (a shr 5) xor (a shl 7)) and (n-1);
end;
procedure TGHashmapTest.HashMapTest1;
var i:longint;
begin
AssertEquals('Not IsEmpty', true, data.IsEmpty);
data.insert(47, 42);
AssertEquals('47 not found', true, data.contains(47));
AssertEquals('39 found', false, data.contains(39));
data[39]:=33;
data[47]:=22;
AssertEquals('bad size', 2, data.size);
AssertEquals('bad 47', 22, data[47]);
for i:=0 to 10000 do
data[20*i+42] := 47+i;
for i:=0 to 10000 do
AssertEquals('bad number found', false, data.contains(i*5+101));
for i:=0 to 10000 do
AssertEquals('bad number', i+47, data[i*20+42]);
AssertEquals('IsEmpty', false, data.IsEmpty);
end;
procedure TGHashmapTest.HashMapTest2;
var i:longint;
begin
for i:=0 to 1000 do
data[3*i] := 7*i;
for i:=0 to 1000 do
data.delete(3*i+1);
AssertEquals('bad size before delete', 1001, data.size);
for i:=500 to 1000 do
data.delete(3*i);
AssertEquals('bad size after delete', 500, data.size);
for i:=0 to 499 do
AssertEquals('element not found', true, data.contains(3*i));
for i:=500 to 1000 do
AssertEquals('deleted element found', false, data.contains(3*i));
end;
procedure TGHashmapTest.HashMapTest3;
var i:longint;
x:array[0..1000] of longint;
it:THashmaplli.TIterator;
begin
it:=data.Iterator;
if it <> nil then
AssertEquals('it not null', 0, 1);
for i:=0 to 1000 do begin
data[i]:=47*i;
x[i]:=0;
end;
it:=data.Iterator;
repeat
inc(x[it.GetValue.key]);
AssertEquals('bad value', it.GetValue.key*47, it.GetValue.value);
until not it.next;
for i:=0 to 1000 do begin
AssertEquals('som not 1', 1, x[i]);
end;
end;
procedure TGHashmapTest.Setup;
begin
data:=THashmaplli.create;
end;
initialization
RegisterTest(TGHashmapTest);
end.

View File

@ -0,0 +1,97 @@
{$mode objfpc}
unit ghashsettest;
interface
uses fpcunit, testregistry, ghashset;
type hint=class
class function hash(a,n:SizeUInt):SizeUInt;
end;
type THashsetlli=specialize THashSet<longint, hint>;
type TGHashSetTest = class(TTestCase)
Published
procedure HashSetTest1;
procedure HashSetTest2;
procedure HashSetTest3;
public
procedure Setup;override;
private
data:THashsetlli;
end;
implementation
class function hint.hash(a,n:SizeUInt):SizeUInt;
begin
hash:= (a xor (a shr 5) xor (a shl 7)) and (n-1);
end;
procedure TGHashSetTest.HashSetTest1;
var i:longint;
begin
AssertEquals('Not IsEmpty', true, data.IsEmpty);
data.insert(47);
AssertEquals('47 not found', true, data.contains(47));
AssertEquals('39 found', false, data.contains(39));
data.insert(39);
data.insert(47);
AssertEquals('bad size', 2, data.size);
for i:=0 to 10000 do
data.insert(20*i+42);
for i:=0 to 10000 do
AssertEquals('bad number found', false, data.contains(i*5+101));
for i:=0 to 10000 do
AssertEquals('number not found', true, data.contains(i*20+42));
AssertEquals('IsEmpty', false, data.IsEmpty);
end;
procedure TGHashSetTest.HashSetTest2;
var i:longint;
begin
for i:=0 to 1000 do
data.insert(3*i);
for i:=0 to 1000 do
data.delete(3*i+1);
AssertEquals('bad size before delete', 1001, data.size);
for i:=500 to 1000 do
data.delete(3*i);
AssertEquals('bad size after delete', 500, data.size);
for i:=0 to 499 do
AssertEquals('element not found', true, data.contains(3*i));
for i:=500 to 1000 do
AssertEquals('deleted element found', false, data.contains(3*i));
end;
procedure TGHashSetTest.HashSetTest3;
var i:longint;
x:array[0..1000] of longint;
it:THashSetlli.TIterator;
begin
it:=data.Iterator;
if it <> nil then
AssertEquals('it not null', 0, 1);
for i:=0 to 1000 do begin
data.insert(i);
x[i]:=0;
end;
it:=data.Iterator;
repeat
inc(x[it.GetValue]);
until not it.next;
for i:=0 to 1000 do begin
AssertEquals('som not 1', 1, x[i]);
end;
end;
procedure TGHashSetTest.Setup;
begin
data:=THashSetlli.create;
end;
initialization
RegisterTest(TGHashSetTest);
end.