mirror of https://github.com/CGAL/cgal
- Changed Hash_map to Unique_hash_map. Kept old file.
- Separated Handle_hash_function into own file. - Improved performance of default Handle_hash_function. - Rewrote manual pages including the UniqueHandleFunction concept. - Made protected methods in chained_map.h public such that Unique_hash_map can be implemented using a private member instead of private inheritance.
This commit is contained in:
parent
3d5d6a378b
commit
203e42a0f4
|
|
@ -1,4 +1,14 @@
|
|||
Hash_map Package: Release changes
|
||||
----------------------------------------------------------------------
|
||||
|
||||
0.9.2 (09 Jul 2001)
|
||||
|
||||
- Changed Hash_map to Unique_hash_map. Kept old file.
|
||||
- Separated Handle_hash_function into own file.
|
||||
- Improved performance of default Handle_hash_function.
|
||||
- Rewrote manual pages including the UniqueHandleFunction concept.
|
||||
- Made protected methods in chained_map.h public such that
|
||||
Unique_hash_map can be implemented using a private member
|
||||
instead of private inheritance.
|
||||
|
||||
0.9 initial release
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
% +------------------------------------------------------------------------+
|
||||
% | Reference manual page: Handle_hash_function.tex
|
||||
% +------------------------------------------------------------------------+
|
||||
% | 08.07.2001 Lutz Kettner
|
||||
% | Package: Hash_map
|
||||
% |
|
||||
\RCSdef{\RCSHandlehashfunctionRev}{$Revision$}
|
||||
\RCSdefDate{\RCSHandlehashfunctionDate}{$Date$}
|
||||
% |
|
||||
%%RefPage: end of header, begin of main body
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
|
||||
\begin{ccRefClass}{Handle_hash_function}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
The class \ccRefName\ is a model for the \ccc{UniqueHasFunction}
|
||||
concept. It is applicable for all key types with pointer like
|
||||
functionality, such as handles, iterators, and circulators.
|
||||
Specificaly, for a \ccc{key} value the expression \ccc{&*key} has to
|
||||
return a unique address.
|
||||
|
||||
\ccInclude{CGAL/Handle_hash_function.h}
|
||||
|
||||
\ccIsModel
|
||||
|
||||
\ccRefConceptPage{UniqueHashFunction}
|
||||
|
||||
\ccCreation
|
||||
\ccCreationVariable{hash}
|
||||
|
||||
\ccConstructor{Handle_hash_function();}{default constructor.}
|
||||
|
||||
\ccOperations
|
||||
|
||||
\ccMethod{template <class Handle>
|
||||
std::size_t operator()( const Handle& key);}{
|
||||
returns unique hash value for any \ccc{Handle}
|
||||
type for which \ccc{&*key} gives a unique address.}
|
||||
|
||||
\ccSeeAlso
|
||||
|
||||
\ccRefIdfierPage{CGAL::Unique_hash_map<Key,Data,UniqueHashFunction>}
|
||||
|
||||
\ccImplementation
|
||||
|
||||
Plain type cast of \ccc{&*key} to \ccc{std::size_t}.
|
||||
|
||||
\end{ccRefClass}
|
||||
|
||||
% +------------------------------------------------------------------------+
|
||||
%%RefPage: end of main body, begin of footer
|
||||
% EOF
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
% begin cgal manual page
|
||||
|
||||
\begin{ccRefClass}{Hash_map<D,E,I>}\ccCreationVariable{M}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
An instance \ccc{M} of the parameterized data type
|
||||
\ccc{Hash_map<D,E,I>} is an injective mapping from the set of indexed objects of
|
||||
type \ccc{D}, called the domain type of \ccc{M}, to the set of variables
|
||||
of data type \ccc{E}, called the element type of \ccc{M}.
|
||||
|
||||
An object \ccc{i} of the index data type \ccc{I} delivers a unique integer
|
||||
index \ccc{i(d)} for objects $d \in D$.
|
||||
|
||||
We use \ccc{M(d)} to denote the variable associated to \ccc{d}. All variables
|
||||
are initialized to \ccc{default_element}, an element of \ccc{E} that is
|
||||
specified in the definition of \ccc{M}. A subset of \ccc{D} is designated as
|
||||
the domain of \ccc{M}. Elements are added to \ccc{dom(M)} by the array
|
||||
operator; however, the domain may also contain indices for which the
|
||||
access operator was never executed.
|
||||
|
||||
There's one default index schemes already defined: \ccc{Handle_index}. It
|
||||
allows to index all handles, iterators and circulators. Thus
|
||||
\ccc{Hash_map<D,E>} can be used for any handle or iterator type \ccc{D} (handles
|
||||
are static iterators).
|
||||
|
||||
|
||||
|
||||
\ccSetOneOfTwoColumns{5cm}
|
||||
|
||||
\ccTypes
|
||||
|
||||
\ccNestedType{item}{the item type.
|
||||
}
|
||||
|
||||
\ccNestedType{domain_type}{the domain type.
|
||||
}
|
||||
|
||||
\ccNestedType{element_type}{the element type.
|
||||
}
|
||||
|
||||
\ccNestedType{index_type}{the index type.
|
||||
}
|
||||
|
||||
\ccNestedType{element_iterator}{a forward iterator over all element entries.
|
||||
}
|
||||
|
||||
\ccSetOneOfTwoColumns{4cm}
|
||||
|
||||
\ccCreation
|
||||
|
||||
\ccConstructor{Hash_map<D,E,I>()}{creates an injective function $m$ from $D$ to the set of unused
|
||||
variables of type $E$, sets \ccc{default_value} to the default value of type \ccc{E}
|
||||
(if \ccc{E} has no default value then \ccc{default_value} is set to an unspecified
|
||||
element of \ccc{E}), and initializes $M$ with $m$.
|
||||
}
|
||||
|
||||
\ccConstructor{Hash_map<D,E,I>(E def)}{creates an injective function $m$ from $D$ to the set of unused
|
||||
variables of type $E$, sets \ccc{default_value} to \ccc{def}, and initializes $M$ with
|
||||
$m$.
|
||||
}
|
||||
|
||||
\ccSetTwoOfThreeColumns{2cm}{4.5cm}
|
||||
|
||||
\ccOperations
|
||||
|
||||
\ccMethod{void clear() ;}{makes \ccc{M} empty.
|
||||
}
|
||||
|
||||
\ccMethod{void clear(const E& def) ;}{makes \ccc{M} empty and sets \ccc{default_value} to \ccc{x}.
|
||||
}
|
||||
|
||||
\ccMethod{bool is_defined(const D& d) ;}{returns true if $d \in \ccc{dom(M)}$.
|
||||
}
|
||||
|
||||
\ccMethod{E& operator[](const D& d) ;}{returns the variable $M(d)$ and adds $d$ to \ccc{dom(M)}. If \ccc{M}
|
||||
is a const-object then \ccc{M(d)} is read-only and $d$ is not added to
|
||||
\ccc{dom(M)}.
|
||||
}
|
||||
|
||||
\ccMethod{element_iterator begin() ;}{returns the element iterator pointing to the first element
|
||||
stored.
|
||||
}
|
||||
|
||||
\ccMethod{element_iterator end() ;}{returns the element iterator pointing beyond the last element
|
||||
stored.
|
||||
}
|
||||
|
||||
\ccImplementation
|
||||
|
||||
\ccc{Hash_maps} are implemented via chained hashing
|
||||
scheme. Access operations \ccc{M[i]} take expected time $O(1)$.
|
||||
The design was derived from the LEDA type map.
|
||||
|
||||
|
||||
|
||||
\end{ccRefClass}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
% +------------------------------------------------------------------------+
|
||||
% | Reference manual page: UniqueHashFunction.tex
|
||||
% +------------------------------------------------------------------------+
|
||||
% | 08.07.2001 Lutz Kettner
|
||||
% | Package: Hash_map
|
||||
% |
|
||||
\RCSdef{\RCSUniqueHashFunctionRev}{$Revision$}
|
||||
\RCSdefDate{\RCSUniqueHashFunctionDate}{$Date$}
|
||||
% |
|
||||
%%RefPage: end of header, begin of main body
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
\begin{ccRefConcept}{UniqueHashFunction}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
\ccRefName\ is a concept for a hash function with unique hash values.
|
||||
An instance \ccc{hash} for a model of the \ccRefName\ concept is a
|
||||
function object. It maps objects of its domain type \ccc{Key} to
|
||||
the integral image type \ccc{std::size_t}. The image values have to
|
||||
be unique for all keys in the domain type \ccc{Key}.
|
||||
|
||||
\ccRefines
|
||||
|
||||
\stl\ concept HashFunction.
|
||||
|
||||
\ccTypes
|
||||
\ccThree{UniqueHashFunction&}{hash = UniqueHashFunction hash2}{}
|
||||
\ccThreeToTwo
|
||||
|
||||
\ccTypedef{typedef std::size_t result_type;}{type of the hash value.}
|
||||
|
||||
\ccCreation
|
||||
\ccCreationVariable{hash}
|
||||
|
||||
\ccConstructor{UniqueHashFunction( const UniqueHashFunction& hash2);}{
|
||||
copy constructor.}
|
||||
|
||||
\ccMethod{UniqueHashFunction& operator=(
|
||||
const UniqueHashFunction& hash2);}{assignment.}
|
||||
|
||||
\ccOperations
|
||||
|
||||
\ccMethod{std::size_t operator()( const Key& key);}{
|
||||
returns unique hash value for the \ccc{key} value.}
|
||||
|
||||
\ccHasModels
|
||||
|
||||
\ccRefIdfierPage{CGAL::Handle_hash_function}
|
||||
|
||||
\ccSeeAlso
|
||||
|
||||
\ccRefIdfierPage{CGAL::Unique_hash_map<Key,Data,UniqueHashFunction>}
|
||||
|
||||
\end{ccRefConcept}
|
||||
|
||||
% +------------------------------------------------------------------------+
|
||||
%%RefPage: end of main body, begin of footer
|
||||
% EOF
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
% begin cgal manual page
|
||||
|
||||
\begin{ccRefClass}{Unique_hash_map<Key,Data,UniqueHashFunction>}
|
||||
\ccCreationVariable{map}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
An instance \ccVar\ of the parameterized data type \ccRefName\ is an
|
||||
injective mapping from the set of keys of type \ccc{Key} to the set of
|
||||
variables of type \ccc{Data}. New keys can be inserted at any time,
|
||||
however keys cannot be individually deleted.
|
||||
|
||||
An object \ccc{hash} of the type \ccc{UniqueHashFunction} returns a
|
||||
unique integer index \ccc{hash(key)} of type \ccc{std::size_t} for all
|
||||
objects $key$ stored in \ccVar. The template parameter has as default
|
||||
the \ccc{Handle_hash_function} that hashes all type of pointers, handles,
|
||||
iterators, and circulators.
|
||||
|
||||
All variables are initialized to \ccc{default_data}, a value
|
||||
of type \ccc{Data} specified in the definition of \ccVar.
|
||||
|
||||
\ccInclude{CGAL/Unique_hash_map.h}
|
||||
|
||||
\ccTypes
|
||||
\ccTwo{Unique_hash_map<Key,Data,UniqueHashFunction>:: Hash_function}{}
|
||||
|
||||
\ccNestedType{Key}{the \ccc{Key} type.}
|
||||
\ccGlue
|
||||
\ccNestedType{Data}{the \ccc{Data} type.}
|
||||
\ccGlue
|
||||
\ccNestedType{Hash_function}{the unique hash function type.}
|
||||
|
||||
In compliance with \stl\ the types \ccc{key_type}, \ccc{data_type}, and
|
||||
hasher are defined as well.
|
||||
|
||||
\ccCreation
|
||||
\ccThree{Hash_functi}{mained;}{}
|
||||
\ccThreeToTwo
|
||||
|
||||
\ccConstructor{Unique_hash_map<Key,Data,UniqueHashFunction>(
|
||||
const Data& default = Data(),
|
||||
std::size_t table_size = 1,
|
||||
const Hash_function& fct = Hash_function());}{
|
||||
creates an injective function \ccVar\ from \ccc{Key} to the set of unused
|
||||
variables of type \ccc{Data}, sets \ccc{default_data} to \ccc{default},
|
||||
passes the \ccc{table_size} as argument to the internal implementation,
|
||||
and initializes the hash function with \ccc{fct}.}
|
||||
|
||||
\ccConstructor{Unique_hash_map<Key,Data,UniqueHashFunction>(
|
||||
Key first1, Key beyond1, Data first2,
|
||||
const Data& default = Data(),
|
||||
std::size_t table_size = 1,
|
||||
const Hash_function& fct = Hash_function());}{
|
||||
creates an injective function \ccVar\ from \ccc{Key} to the set of unused
|
||||
variables of type \ccc{Data}, sets \ccc{default_data} to \ccc{default},
|
||||
passes the \ccc{table_size} as argument to the internal implementation,
|
||||
initializes the hash function with \ccc{fct}, and inserts all keys
|
||||
from the range \ccc{[first1,beyond1)}. The data variable for each
|
||||
inserted \ccc{key} is initilized with the corresponding value from
|
||||
the range \ccc{[first2, first2 + (beyond1-first1))}.
|
||||
\ccPrecond\ The increment operator must be defined for values
|
||||
of type \ccc{Key} and for values of type \ccc{Data}. \ccc{beyond1}
|
||||
must be reachable from \ccc{first1} using increments.}
|
||||
|
||||
|
||||
\ccOperations
|
||||
\ccThree{Hash_function}{map.is_defined( Key key);}{}
|
||||
|
||||
\ccMethod{Data default_value() const;}{the current \ccc{default_value}.}
|
||||
\ccGlue
|
||||
\ccMethod{Hash_function hash_function() const;}{the current hash function.}
|
||||
|
||||
\ccMethod{bool is_defined( const Key& key);}{returns true if $key$ is
|
||||
defined in \ccVar. Note that there can be keys defined that have not
|
||||
been inserted explicitly. Their variable are initialized to
|
||||
\ccc{default_value}.}
|
||||
|
||||
%%\ccMethod{element_iterator begin();}{
|
||||
%% returns the element iterator pointing to the first element stored.}
|
||||
%%
|
||||
%%\ccMethod{element_iterator end();}{
|
||||
%% returns the element iterator pointing beyond the last element stored.}
|
||||
|
||||
\ccMethod{void clear();}{
|
||||
resets \ccVar\ to the injective function \ccVar\ from \ccc{Key} to the
|
||||
set of unused variables of type \ccc{Data}. The \ccc{default_data}
|
||||
remains unchanged.}
|
||||
|
||||
\ccMethod{void clear(const Data& default);}{
|
||||
resets \ccVar\ to the injective function \ccVar\ from \ccc{Key} to the
|
||||
set of unused variables of type \ccc{Data} and sets \ccc{default_data}
|
||||
to \ccc{default}.}
|
||||
|
||||
\ccTagFullDeclarations
|
||||
|
||||
\ccMethod{Data& operator[](const Key& key);}{
|
||||
returns a reference to the variable \ccVar\ccc{(key)}. If \ccc{key}
|
||||
has not been inserted into \ccVar\ before, \ccc{key} is inserted and
|
||||
initialized with \ccc{default_value}.}
|
||||
|
||||
\ccMethod{const Data& operator[](const Key& key) const;}{
|
||||
returns a const reference to the variable \ccVar\ccc{(key)}. If \ccc{key}
|
||||
has not been inserted into \ccVar\ before, a const reference to the
|
||||
\ccc{default_value} is returned. However, \ccc{key} is not inserted
|
||||
into \ccVar.}
|
||||
|
||||
\ccTagDefaults
|
||||
|
||||
\ccMethod{Data insert( Key first1, Key beyond1, Data first2);}{
|
||||
inserts all keys from the range \ccc{[first1,beyond1)}.
|
||||
The data variable for each inserted \ccc{key} is initilized with the
|
||||
corresponding value from the range \ccc{[first2, first2 +
|
||||
(beyond1-first1))}. Returns \ccc{first2 + (beyond1-first1)}.
|
||||
\ccPrecond\ The increment operator must be defined for values
|
||||
of type \ccc{Key} and for values of type \ccc{Data}. \ccc{beyond1}
|
||||
must be reachable from \ccc{first1} using increments.}
|
||||
|
||||
\vspace*{-5mm}
|
||||
\ccSeeAlso
|
||||
|
||||
\ccRefConceptPage{UniqueHashFunction}\\
|
||||
\ccRefIdfierPage{CGAL::Handle_hash_function}
|
||||
|
||||
\ccImplementation
|
||||
|
||||
\ccc{Unique_hash_map} is implemented via chained hashing scheme. Access
|
||||
operations \ccVar\ccc{[i]} take expected time $O(1)$. The \ccc{table_size}
|
||||
parameter passed to chained hashing can be used to avoid unnecessary
|
||||
rehashing when set the number of expected elements in the map.
|
||||
The design is derived from the \stl\ \ccc{hash_map} and the \leda\ type
|
||||
\ccc{map}. Its specialization on insertion only and unique hash values
|
||||
allow for a more time and space efficient implementation, see also
|
||||
\cite[Chapter 5]{mn-lpcgc-00}. This implementation makes also use
|
||||
of sentinels that lead to defined keys that have not been inserted.
|
||||
|
||||
\end{ccRefClass}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
% +------------------------------------------------------------------------+
|
||||
% | main.tex
|
||||
% +------------------------------------------------------------------------+
|
||||
% | Driver that includes the various reference pages here.
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
\input{Unique_hash_map}
|
||||
\input{UniqueHashFunction}
|
||||
\input{Handle_hash_function}
|
||||
|
||||
%% EOF
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
% +------------------------------------------------------------------------+
|
||||
% | Reference manual page: Handle_hash_function.tex
|
||||
% +------------------------------------------------------------------------+
|
||||
% | 08.07.2001 Lutz Kettner
|
||||
% | Package: Hash_map
|
||||
% |
|
||||
\RCSdef{\RCSHandlehashfunctionRev}{$Revision$}
|
||||
\RCSdefDate{\RCSHandlehashfunctionDate}{$Date$}
|
||||
% |
|
||||
%%RefPage: end of header, begin of main body
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
|
||||
\begin{ccRefClass}{Handle_hash_function}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
The class \ccRefName\ is a model for the \ccc{UniqueHasFunction}
|
||||
concept. It is applicable for all key types with pointer like
|
||||
functionality, such as handles, iterators, and circulators.
|
||||
Specificaly, for a \ccc{key} value the expression \ccc{&*key} has to
|
||||
return a unique address.
|
||||
|
||||
\ccInclude{CGAL/Handle_hash_function.h}
|
||||
|
||||
\ccIsModel
|
||||
|
||||
\ccRefConceptPage{UniqueHashFunction}
|
||||
|
||||
\ccCreation
|
||||
\ccCreationVariable{hash}
|
||||
|
||||
\ccConstructor{Handle_hash_function();}{default constructor.}
|
||||
|
||||
\ccOperations
|
||||
|
||||
\ccMethod{template <class Handle>
|
||||
std::size_t operator()( const Handle& key);}{
|
||||
returns unique hash value for any \ccc{Handle}
|
||||
type for which \ccc{&*key} gives a unique address.}
|
||||
|
||||
\ccSeeAlso
|
||||
|
||||
\ccRefIdfierPage{CGAL::Unique_hash_map<Key,Data,UniqueHashFunction>}
|
||||
|
||||
\ccImplementation
|
||||
|
||||
Plain type cast of \ccc{&*key} to \ccc{std::size_t}.
|
||||
|
||||
\end{ccRefClass}
|
||||
|
||||
% +------------------------------------------------------------------------+
|
||||
%%RefPage: end of main body, begin of footer
|
||||
% EOF
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
% begin cgal manual page
|
||||
|
||||
\begin{ccRefClass}{Hash_map<D,E,I>}\ccCreationVariable{M}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
An instance \ccc{M} of the parameterized data type
|
||||
\ccc{Hash_map<D,E,I>} is an injective mapping from the set of indexed objects of
|
||||
type \ccc{D}, called the domain type of \ccc{M}, to the set of variables
|
||||
of data type \ccc{E}, called the element type of \ccc{M}.
|
||||
|
||||
An object \ccc{i} of the index data type \ccc{I} delivers a unique integer
|
||||
index \ccc{i(d)} for objects $d \in D$.
|
||||
|
||||
We use \ccc{M(d)} to denote the variable associated to \ccc{d}. All variables
|
||||
are initialized to \ccc{default_element}, an element of \ccc{E} that is
|
||||
specified in the definition of \ccc{M}. A subset of \ccc{D} is designated as
|
||||
the domain of \ccc{M}. Elements are added to \ccc{dom(M)} by the array
|
||||
operator; however, the domain may also contain indices for which the
|
||||
access operator was never executed.
|
||||
|
||||
There's one default index schemes already defined: \ccc{Handle_index}. It
|
||||
allows to index all handles, iterators and circulators. Thus
|
||||
\ccc{Hash_map<D,E>} can be used for any handle or iterator type \ccc{D} (handles
|
||||
are static iterators).
|
||||
|
||||
|
||||
|
||||
\ccSetOneOfTwoColumns{5cm}
|
||||
|
||||
\ccTypes
|
||||
|
||||
\ccNestedType{item}{the item type.
|
||||
}
|
||||
|
||||
\ccNestedType{domain_type}{the domain type.
|
||||
}
|
||||
|
||||
\ccNestedType{element_type}{the element type.
|
||||
}
|
||||
|
||||
\ccNestedType{index_type}{the index type.
|
||||
}
|
||||
|
||||
\ccNestedType{element_iterator}{a forward iterator over all element entries.
|
||||
}
|
||||
|
||||
\ccSetOneOfTwoColumns{4cm}
|
||||
|
||||
\ccCreation
|
||||
|
||||
\ccConstructor{Hash_map<D,E,I>()}{creates an injective function $m$ from $D$ to the set of unused
|
||||
variables of type $E$, sets \ccc{default_value} to the default value of type \ccc{E}
|
||||
(if \ccc{E} has no default value then \ccc{default_value} is set to an unspecified
|
||||
element of \ccc{E}), and initializes $M$ with $m$.
|
||||
}
|
||||
|
||||
\ccConstructor{Hash_map<D,E,I>(E def)}{creates an injective function $m$ from $D$ to the set of unused
|
||||
variables of type $E$, sets \ccc{default_value} to \ccc{def}, and initializes $M$ with
|
||||
$m$.
|
||||
}
|
||||
|
||||
\ccSetTwoOfThreeColumns{2cm}{4.5cm}
|
||||
|
||||
\ccOperations
|
||||
|
||||
\ccMethod{void clear() ;}{makes \ccc{M} empty.
|
||||
}
|
||||
|
||||
\ccMethod{void clear(const E& def) ;}{makes \ccc{M} empty and sets \ccc{default_value} to \ccc{x}.
|
||||
}
|
||||
|
||||
\ccMethod{bool is_defined(const D& d) ;}{returns true if $d \in \ccc{dom(M)}$.
|
||||
}
|
||||
|
||||
\ccMethod{E& operator[](const D& d) ;}{returns the variable $M(d)$ and adds $d$ to \ccc{dom(M)}. If \ccc{M}
|
||||
is a const-object then \ccc{M(d)} is read-only and $d$ is not added to
|
||||
\ccc{dom(M)}.
|
||||
}
|
||||
|
||||
\ccMethod{element_iterator begin() ;}{returns the element iterator pointing to the first element
|
||||
stored.
|
||||
}
|
||||
|
||||
\ccMethod{element_iterator end() ;}{returns the element iterator pointing beyond the last element
|
||||
stored.
|
||||
}
|
||||
|
||||
\ccImplementation
|
||||
|
||||
\ccc{Hash_maps} are implemented via chained hashing
|
||||
scheme. Access operations \ccc{M[i]} take expected time $O(1)$.
|
||||
The design was derived from the LEDA type map.
|
||||
|
||||
|
||||
|
||||
\end{ccRefClass}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
% +------------------------------------------------------------------------+
|
||||
% | Reference manual page: UniqueHashFunction.tex
|
||||
% +------------------------------------------------------------------------+
|
||||
% | 08.07.2001 Lutz Kettner
|
||||
% | Package: Hash_map
|
||||
% |
|
||||
\RCSdef{\RCSUniqueHashFunctionRev}{$Revision$}
|
||||
\RCSdefDate{\RCSUniqueHashFunctionDate}{$Date$}
|
||||
% |
|
||||
%%RefPage: end of header, begin of main body
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
\begin{ccRefConcept}{UniqueHashFunction}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
\ccRefName\ is a concept for a hash function with unique hash values.
|
||||
An instance \ccc{hash} for a model of the \ccRefName\ concept is a
|
||||
function object. It maps objects of its domain type \ccc{Key} to
|
||||
the integral image type \ccc{std::size_t}. The image values have to
|
||||
be unique for all keys in the domain type \ccc{Key}.
|
||||
|
||||
\ccRefines
|
||||
|
||||
\stl\ concept HashFunction.
|
||||
|
||||
\ccTypes
|
||||
\ccThree{UniqueHashFunction&}{hash = UniqueHashFunction hash2}{}
|
||||
\ccThreeToTwo
|
||||
|
||||
\ccTypedef{typedef std::size_t result_type;}{type of the hash value.}
|
||||
|
||||
\ccCreation
|
||||
\ccCreationVariable{hash}
|
||||
|
||||
\ccConstructor{UniqueHashFunction( const UniqueHashFunction& hash2);}{
|
||||
copy constructor.}
|
||||
|
||||
\ccMethod{UniqueHashFunction& operator=(
|
||||
const UniqueHashFunction& hash2);}{assignment.}
|
||||
|
||||
\ccOperations
|
||||
|
||||
\ccMethod{std::size_t operator()( const Key& key);}{
|
||||
returns unique hash value for the \ccc{key} value.}
|
||||
|
||||
\ccHasModels
|
||||
|
||||
\ccRefIdfierPage{CGAL::Handle_hash_function}
|
||||
|
||||
\ccSeeAlso
|
||||
|
||||
\ccRefIdfierPage{CGAL::Unique_hash_map<Key,Data,UniqueHashFunction>}
|
||||
|
||||
\end{ccRefConcept}
|
||||
|
||||
% +------------------------------------------------------------------------+
|
||||
%%RefPage: end of main body, begin of footer
|
||||
% EOF
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
% begin cgal manual page
|
||||
|
||||
\begin{ccRefClass}{Unique_hash_map<Key,Data,UniqueHashFunction>}
|
||||
\ccCreationVariable{map}
|
||||
|
||||
\ccDefinition
|
||||
|
||||
An instance \ccVar\ of the parameterized data type \ccRefName\ is an
|
||||
injective mapping from the set of keys of type \ccc{Key} to the set of
|
||||
variables of type \ccc{Data}. New keys can be inserted at any time,
|
||||
however keys cannot be individually deleted.
|
||||
|
||||
An object \ccc{hash} of the type \ccc{UniqueHashFunction} returns a
|
||||
unique integer index \ccc{hash(key)} of type \ccc{std::size_t} for all
|
||||
objects $key$ stored in \ccVar. The template parameter has as default
|
||||
the \ccc{Handle_hash_function} that hashes all type of pointers, handles,
|
||||
iterators, and circulators.
|
||||
|
||||
All variables are initialized to \ccc{default_data}, a value
|
||||
of type \ccc{Data} specified in the definition of \ccVar.
|
||||
|
||||
\ccInclude{CGAL/Unique_hash_map.h}
|
||||
|
||||
\ccTypes
|
||||
\ccTwo{Unique_hash_map<Key,Data,UniqueHashFunction>:: Hash_function}{}
|
||||
|
||||
\ccNestedType{Key}{the \ccc{Key} type.}
|
||||
\ccGlue
|
||||
\ccNestedType{Data}{the \ccc{Data} type.}
|
||||
\ccGlue
|
||||
\ccNestedType{Hash_function}{the unique hash function type.}
|
||||
|
||||
In compliance with \stl\ the types \ccc{key_type}, \ccc{data_type}, and
|
||||
hasher are defined as well.
|
||||
|
||||
\ccCreation
|
||||
\ccThree{Hash_functi}{mained;}{}
|
||||
\ccThreeToTwo
|
||||
|
||||
\ccConstructor{Unique_hash_map<Key,Data,UniqueHashFunction>(
|
||||
const Data& default = Data(),
|
||||
std::size_t table_size = 1,
|
||||
const Hash_function& fct = Hash_function());}{
|
||||
creates an injective function \ccVar\ from \ccc{Key} to the set of unused
|
||||
variables of type \ccc{Data}, sets \ccc{default_data} to \ccc{default},
|
||||
passes the \ccc{table_size} as argument to the internal implementation,
|
||||
and initializes the hash function with \ccc{fct}.}
|
||||
|
||||
\ccConstructor{Unique_hash_map<Key,Data,UniqueHashFunction>(
|
||||
Key first1, Key beyond1, Data first2,
|
||||
const Data& default = Data(),
|
||||
std::size_t table_size = 1,
|
||||
const Hash_function& fct = Hash_function());}{
|
||||
creates an injective function \ccVar\ from \ccc{Key} to the set of unused
|
||||
variables of type \ccc{Data}, sets \ccc{default_data} to \ccc{default},
|
||||
passes the \ccc{table_size} as argument to the internal implementation,
|
||||
initializes the hash function with \ccc{fct}, and inserts all keys
|
||||
from the range \ccc{[first1,beyond1)}. The data variable for each
|
||||
inserted \ccc{key} is initilized with the corresponding value from
|
||||
the range \ccc{[first2, first2 + (beyond1-first1))}.
|
||||
\ccPrecond\ The increment operator must be defined for values
|
||||
of type \ccc{Key} and for values of type \ccc{Data}. \ccc{beyond1}
|
||||
must be reachable from \ccc{first1} using increments.}
|
||||
|
||||
|
||||
\ccOperations
|
||||
\ccThree{Hash_function}{map.is_defined( Key key);}{}
|
||||
|
||||
\ccMethod{Data default_value() const;}{the current \ccc{default_value}.}
|
||||
\ccGlue
|
||||
\ccMethod{Hash_function hash_function() const;}{the current hash function.}
|
||||
|
||||
\ccMethod{bool is_defined( const Key& key);}{returns true if $key$ is
|
||||
defined in \ccVar. Note that there can be keys defined that have not
|
||||
been inserted explicitly. Their variable are initialized to
|
||||
\ccc{default_value}.}
|
||||
|
||||
%%\ccMethod{element_iterator begin();}{
|
||||
%% returns the element iterator pointing to the first element stored.}
|
||||
%%
|
||||
%%\ccMethod{element_iterator end();}{
|
||||
%% returns the element iterator pointing beyond the last element stored.}
|
||||
|
||||
\ccMethod{void clear();}{
|
||||
resets \ccVar\ to the injective function \ccVar\ from \ccc{Key} to the
|
||||
set of unused variables of type \ccc{Data}. The \ccc{default_data}
|
||||
remains unchanged.}
|
||||
|
||||
\ccMethod{void clear(const Data& default);}{
|
||||
resets \ccVar\ to the injective function \ccVar\ from \ccc{Key} to the
|
||||
set of unused variables of type \ccc{Data} and sets \ccc{default_data}
|
||||
to \ccc{default}.}
|
||||
|
||||
\ccTagFullDeclarations
|
||||
|
||||
\ccMethod{Data& operator[](const Key& key);}{
|
||||
returns a reference to the variable \ccVar\ccc{(key)}. If \ccc{key}
|
||||
has not been inserted into \ccVar\ before, \ccc{key} is inserted and
|
||||
initialized with \ccc{default_value}.}
|
||||
|
||||
\ccMethod{const Data& operator[](const Key& key) const;}{
|
||||
returns a const reference to the variable \ccVar\ccc{(key)}. If \ccc{key}
|
||||
has not been inserted into \ccVar\ before, a const reference to the
|
||||
\ccc{default_value} is returned. However, \ccc{key} is not inserted
|
||||
into \ccVar.}
|
||||
|
||||
\ccTagDefaults
|
||||
|
||||
\ccMethod{Data insert( Key first1, Key beyond1, Data first2);}{
|
||||
inserts all keys from the range \ccc{[first1,beyond1)}.
|
||||
The data variable for each inserted \ccc{key} is initilized with the
|
||||
corresponding value from the range \ccc{[first2, first2 +
|
||||
(beyond1-first1))}. Returns \ccc{first2 + (beyond1-first1)}.
|
||||
\ccPrecond\ The increment operator must be defined for values
|
||||
of type \ccc{Key} and for values of type \ccc{Data}. \ccc{beyond1}
|
||||
must be reachable from \ccc{first1} using increments.}
|
||||
|
||||
\vspace*{-5mm}
|
||||
\ccSeeAlso
|
||||
|
||||
\ccRefConceptPage{UniqueHashFunction}\\
|
||||
\ccRefIdfierPage{CGAL::Handle_hash_function}
|
||||
|
||||
\ccImplementation
|
||||
|
||||
\ccc{Unique_hash_map} is implemented via chained hashing scheme. Access
|
||||
operations \ccVar\ccc{[i]} take expected time $O(1)$. The \ccc{table_size}
|
||||
parameter passed to chained hashing can be used to avoid unnecessary
|
||||
rehashing when set the number of expected elements in the map.
|
||||
The design is derived from the \stl\ \ccc{hash_map} and the \leda\ type
|
||||
\ccc{map}. Its specialization on insertion only and unique hash values
|
||||
allow for a more time and space efficient implementation, see also
|
||||
\cite[Chapter 5]{mn-lpcgc-00}. This implementation makes also use
|
||||
of sentinels that lead to defined keys that have not been inserted.
|
||||
|
||||
\end{ccRefClass}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
% +------------------------------------------------------------------------+
|
||||
% | main.tex
|
||||
% +------------------------------------------------------------------------+
|
||||
% | Driver that includes the various reference pages here.
|
||||
% +------------------------------------------------------------------------+
|
||||
|
||||
\input{Unique_hash_map}
|
||||
\input{UniqueHashFunction}
|
||||
\input{Handle_hash_function}
|
||||
|
||||
%% EOF
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// ============================================================================
|
||||
//
|
||||
// Copyright (c) 1997-2000 The CGAL Consortium
|
||||
//
|
||||
// This software and related documentation is part of an INTERNAL release
|
||||
// of the Computational Geometry Algorithms Library (CGAL). It is not
|
||||
// intended for general use.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// release : $CGAL_Revision$
|
||||
// release_date : $CGAL_Date$
|
||||
//
|
||||
// file : include/CGAL/Handle_hash_function.h
|
||||
// package : Hash_map
|
||||
// chapter : STL_Extensions
|
||||
//
|
||||
// revision : $Revision$
|
||||
// revision_date : $Date$
|
||||
//
|
||||
// author(s) : Michael Seel <seel@mpi-sb.mpg.de>
|
||||
// Lutz Kettner <kettner@inf.ethz.ch>
|
||||
// maintainer : Michael Seel <seel@mpi-sb.mpg.de>
|
||||
// coordinator : Michael Seel <seel@mpi-sb.mpg.de>
|
||||
//
|
||||
// implementation: Hash Map by chained hashing, default hash function
|
||||
// ============================================================================
|
||||
|
||||
#ifndef CGAL_HANDLE_HASH_FUNCTION_H
|
||||
#define CGAL_HANDLE_HASH_FUNCTION_H
|
||||
|
||||
#include <CGAL/basic.h>
|
||||
|
||||
CGAL_BEGIN_NAMESPACE
|
||||
|
||||
struct Handle_hash_function {
|
||||
typedef std::size_t result_type;
|
||||
template <class H>
|
||||
std::size_t operator() (const H& h) const {
|
||||
return std::size_t(&*h) /
|
||||
sizeof( typename std::iterator_traits<H>::value_type);
|
||||
}
|
||||
};
|
||||
|
||||
CGAL_END_NAMESPACE
|
||||
|
||||
#endif // CGAL_HANDLE_HASH_FUNCTION_H
|
||||
// EOF
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ class chained_map
|
|||
|
||||
unsigned long old_index;
|
||||
|
||||
protected:
|
||||
public:
|
||||
T& xdef() { return STOP.i; }
|
||||
const T& cxdef() const { return STOP.i; }
|
||||
private:
|
||||
|
|
@ -79,7 +79,7 @@ private:
|
|||
|
||||
inline void insert(unsigned long x, T y);
|
||||
|
||||
protected:
|
||||
public:
|
||||
typedef chained_map_elem<T>* chained_map_item;
|
||||
typedef chained_map_item item;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
// ============================================================================
|
||||
//
|
||||
// Copyright (c) 1997-2000 The CGAL Consortium
|
||||
//
|
||||
// This software and related documentation is part of an INTERNAL release
|
||||
// of the Computational Geometry Algorithms Library (CGAL). It is not
|
||||
// intended for general use.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// release : $CGAL_Revision$
|
||||
// release_date : $CGAL_Date$
|
||||
//
|
||||
// file : include/CGAL/Unique_hash_map.h
|
||||
// package : Hash_map
|
||||
// chapter : STL_Extensions
|
||||
//
|
||||
// revision : $Revision$
|
||||
// revision_date : $Date$
|
||||
//
|
||||
// author(s) : Michael Seel <seel@mpi-sb.mpg.de>
|
||||
// Lutz Kettner <kettner@inf.ethz.ch>
|
||||
// maintainer : Michael Seel <seel@mpi-sb.mpg.de>
|
||||
// coordinator : Michael Seel <seel@mpi-sb.mpg.de>
|
||||
//
|
||||
// implementation: Hash Map by chained hashing
|
||||
// ============================================================================
|
||||
|
||||
#ifndef CGAL_UNIQUE_HASH_MAP_H
|
||||
#define CGAL_UNIQUE_HASH_MAP_H
|
||||
|
||||
#include <CGAL/basic.h>
|
||||
#include <CGAL/Handle_hash_function.h>
|
||||
#include <CGAL/Tools/chained_map.h>
|
||||
|
||||
CGAL_BEGIN_NAMESPACE
|
||||
|
||||
template <class Key_, class Data_,
|
||||
class UniqueHashFunction = Handle_hash_function>
|
||||
class Unique_hash_map {
|
||||
public:
|
||||
typedef Key_ Key;
|
||||
typedef Data_ Data;
|
||||
typedef UniqueHashFunction Hash_function;
|
||||
|
||||
// STL compliance
|
||||
typedef Key_ key_type;
|
||||
typedef Data_ data_type;
|
||||
typedef UniqueHashFunction hasher;
|
||||
|
||||
typedef Unique_hash_map<Key,Data,Hash_function> Self;
|
||||
|
||||
private:
|
||||
typedef CGALi::chained_map<Data> Map;
|
||||
typedef typename Map::item Item;
|
||||
|
||||
private:
|
||||
Hash_function m_hash_function;
|
||||
Map m_map;
|
||||
|
||||
public:
|
||||
|
||||
Unique_hash_map() { m_map.xdef() = Data(); }
|
||||
|
||||
Unique_hash_map( const Data& deflt, std::size_t table_size = 1)
|
||||
: m_map( table_size) { m_map.xdef() = deflt; }
|
||||
|
||||
Unique_hash_map( const Data& deflt,
|
||||
std::size_t table_size,
|
||||
const Hash_function& fct)
|
||||
: m_hash_function(fct), m_map( table_size) { m_map.xdef() = deflt; }
|
||||
|
||||
Unique_hash_map( Key first1, Key beyond1, Data first2) {
|
||||
m_map.xdef() = Data();
|
||||
insert( first1, beyond1, first2);
|
||||
}
|
||||
Unique_hash_map( Key first1, Key beyond1, Data first2,
|
||||
const Data& deflt,
|
||||
std::size_t table_size = 1,
|
||||
const Hash_function& fct = Hash_function())
|
||||
: m_hash_function(fct), m_map( table_size) {
|
||||
m_map.xdef() = deflt;
|
||||
insert( first1, beyond1, first2);
|
||||
}
|
||||
|
||||
Data default_value() const { return m_map.cxdef(); }
|
||||
|
||||
Hash_function hash_function() const { return m_hash_function; }
|
||||
|
||||
void clear() { m_map.clear(); }
|
||||
|
||||
void clear( const Data& deflt) {
|
||||
m_map.clear();
|
||||
m_map.xdef() = deflt; }
|
||||
|
||||
bool is_defined( const Key& key) const {
|
||||
return m_map.lookup( m_hash_function(key)) != 0;
|
||||
}
|
||||
|
||||
const Data& operator[]( const Key& key) const {
|
||||
Item p = m_map.lookup( m_hash_function(key));
|
||||
if ( p != 0 )
|
||||
return m_map.inf(p);
|
||||
return m_map.cxdef();
|
||||
}
|
||||
|
||||
Data& operator[]( const Key& key) {
|
||||
return m_map.access( m_hash_function(key));
|
||||
}
|
||||
|
||||
Data insert( Key first1, Key beyond1, Data first2) {
|
||||
for ( ; first1 != beyond1; (++first1, ++first2)) {
|
||||
operator[]( first1) = first2;
|
||||
}
|
||||
return first2;
|
||||
}
|
||||
|
||||
void statistics() const { m_map.statistics(); }
|
||||
};
|
||||
|
||||
CGAL_END_NAMESPACE
|
||||
|
||||
#endif // CGAL_UNIQUE_HASH_MAP_H
|
||||
// EOF
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#include <CGAL/Cartesian.h>
|
||||
#include <list>
|
||||
#include <CGAL/Unique_hash_map.h>
|
||||
#include <CGAL/test_macros.h>
|
||||
|
||||
using namespace std;
|
||||
typedef list<int>::iterator iterator;
|
||||
|
||||
int main() {
|
||||
CGAL_TEST_START;
|
||||
list<int> L;
|
||||
L.push_back(1);
|
||||
iterator it1 = L.begin();
|
||||
CGAL::Unique_hash_map<iterator,int> H1;
|
||||
CGAL::Unique_hash_map<iterator,int> H2(-1);
|
||||
CGAL_TEST( H1.default_value() == 0);
|
||||
CGAL_TEST( H2.default_value() == -1);
|
||||
H1[it1] = 2;
|
||||
CGAL_TEST(H1[it1]==2);
|
||||
CGAL_TEST(H2[it1]==-1);
|
||||
H1.clear();
|
||||
H2.clear(-2);
|
||||
H2[it1] = 2;
|
||||
CGAL_TEST(H1[it1]==0);
|
||||
CGAL_TEST(H2[it1]==2);
|
||||
iterator it2 = L.end();
|
||||
const CGAL::Unique_hash_map<iterator,int>* pH = &H2;
|
||||
CGAL_TEST((*pH)[it2]==-2);
|
||||
|
||||
H1.clear(-1);
|
||||
L.push_back(2);
|
||||
L.push_back(3);
|
||||
L.push_back(4);
|
||||
L.push_back(5);
|
||||
CGAL_TEST( L.size() == 5);
|
||||
H1.insert( L.begin(), L.end(), 1);
|
||||
for ( iterator i = L.begin(); i != L.end(); ++i) {
|
||||
CGAL_TEST( H1[i] == *i);
|
||||
}
|
||||
CGAL::Unique_hash_map<iterator,int> H3( L.begin(), L.end(), 1);
|
||||
for ( iterator i = L.begin(); i != L.end(); ++i) {
|
||||
CGAL_TEST( H3[i] == *i);
|
||||
}
|
||||
CGAL::Handle_hash_function hash;
|
||||
CGAL::Unique_hash_map<iterator,int,CGAL::Handle_hash_function>
|
||||
H4( L.begin(), L.end(), 1, -1, 512, hash);
|
||||
for ( iterator i = L.begin(); i != L.end(); ++i) {
|
||||
CGAL_TEST( H4[i] == *i);
|
||||
}
|
||||
hash = H4.hash_function();
|
||||
|
||||
CGAL_TEST_END;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -33,10 +33,10 @@ compile_and_run()
|
|||
rm -f $OUTPUTFILE
|
||||
COMMAND="./$1"
|
||||
if [ -f $1.cmd ] ; then
|
||||
COMMAND="$COMMAND '`cat $1.cmd`'"
|
||||
COMMAND="$COMMAND `cat $1.cmd`"
|
||||
fi
|
||||
if [ -f $1.cin ] ; then
|
||||
COMMAND="echo '`cat $1.cin`' | $COMMAND"
|
||||
COMMAND="cat $1.cin | $COMMAND"
|
||||
fi
|
||||
echo "Executing $1 ... "
|
||||
echo
|
||||
|
|
@ -63,4 +63,12 @@ touch $ERRORFILE
|
|||
# compile and run the tests
|
||||
#---------------------------------------------------------------------#
|
||||
|
||||
compile_and_run Hash_map-test
|
||||
if [ $# -ne 0 ] ; then
|
||||
for file in $* ; do
|
||||
compile_and_run $file
|
||||
done
|
||||
else
|
||||
compile_and_run Hash_map-test
|
||||
compile_and_run Unique_hash_map_test
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ CXXFLAGS = \
|
|||
$(EXTRA_FLAGS) \
|
||||
-Iinclude \
|
||||
$(CGAL_CXXFLAGS) \
|
||||
$(LONG_NAME_PROBLEM_CXXFLAGS) \
|
||||
$(DEBUG_OPT)
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
|
|
@ -31,19 +30,25 @@ LIBPATH = \
|
|||
|
||||
LDFLAGS = \
|
||||
$(TESTSUITE_LDFLAGS) \
|
||||
$(LONG_NAME_PROBLEM_LDFLAGS) \
|
||||
$(CGAL_LDFLAGS)
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
# target entries
|
||||
#---------------------------------------------------------------------#
|
||||
|
||||
all: Hash_map-test
|
||||
all: \
|
||||
Hash_map-test \
|
||||
Unique_hash_map_test
|
||||
|
||||
Hash_map-test$(EXE_EXT): Hash_map-test$(OBJ_EXT)
|
||||
$(CGAL_CXX) $(LIBPATH) $(EXE_OPT)Hash_map-test Hash_map-test$(OBJ_EXT) $(LDFLAGS)
|
||||
|
||||
clean: Hash_map-test.clean
|
||||
Unique_hash_map_test$(EXE_EXT): Unique_hash_map_test$(OBJ_EXT)
|
||||
$(CGAL_CXX) $(LIBPATH) $(EXE_OPT)Unique_hash_map_test Unique_hash_map_test$(OBJ_EXT) $(LDFLAGS)
|
||||
|
||||
clean: \
|
||||
Hash_map-test.clean \
|
||||
Unique_hash_map_test.clean
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
# suffix rules
|
||||
|
|
@ -52,3 +57,6 @@ clean: Hash_map-test.clean
|
|||
.C$(OBJ_EXT):
|
||||
$(CGAL_CXX) $(CXXFLAGS) $(OBJ_OPT) $<
|
||||
|
||||
.cpp$(OBJ_EXT):
|
||||
$(CGAL_CXX) $(CXXFLAGS) $(OBJ_OPT) $<
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.9.1 (22 Jun 2001)
|
||||
0.9.2 (09 Jul 2001)
|
||||
|
|
|
|||
Loading…
Reference in New Issue