61734ab2b3
[SVN r80481]
462 lines
22 KiB
HTML
462 lines
22 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html><head>
|
||
|
||
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"><title>Voronoi Advanced Tutorial</title></head><body>
|
||
<h1>Voronoi Advanced Tutorial<br>
|
||
</h1>
|
||
|
||
This tutorial consists of two parts. The first one provides two
|
||
examples of a real world problems that default configuration of Voronoi
|
||
library is capable to solve. By default configuration we mean the one
|
||
that accepts
|
||
signed 32-bit integer and outputs floating-point (64-bit
|
||
double) coordinates. We provide those examples to convince even the
|
||
most skeptical users that they probably don't need to configure library
|
||
for higher-precision input or output coordinate types. However if the
|
||
posed problem really requires those, fully featured configuration of
|
||
both input and output coordinate types is provided in the second part
|
||
of this tutorial.<br>
|
||
|
||
<h2>Red Planet</h2>
|
||
|
||
<h3>Problem Statement</h3>
|
||
|
||
<img style="width: 665px; height: 369px;" alt="" src="images/rover.jpg"><br>
|
||
|
||
<br>Lets imagine that NASA is
|
||
planning to send a new robot to Mars. Each day the center situated on Earth
|
||
will send a destination point coordinates the robot needs to reach by
|
||
the end of the day. Of course we'd like to save as much energy as
|
||
possible thus choosing the shortest possible path. This would be a
|
||
straight line in a perfect world (we don't consider curvature of
|
||
surface), but situation becomes more complicated as there are some
|
||
rocks and wells on Mars our robot can't go through. Behind of those our
|
||
robot has some dimensions that might not allow it to pass narrow places.<br>
|
||
|
||
<h3>Application of Voronoi diagram</h3>
|
||
|
||
The problem above could be solved using Voronoi diagram. The first
|
||
stage would be to discretize obstacles (rocks and wells) with
|
||
polylines. Afterwards we will compute Voronoi diagram of the input set
|
||
of segments. As each Voronoi edge is equidistant from the two closest
|
||
sites we are able to filter edges the robot won't be able to pass due
|
||
to it's dimensions. The last step would be to run a bit optimized A*
|
||
algorithm to find
|
||
the shortest or at least suboptimal path and we are done.<br>
|
||
|
||
<h3>Discretization of input geometries</h3>
|
||
|
||
To show how good is the default input coordinate type provided by the
|
||
Voronoi library
|
||
we will discretize the whole area of Mars. That will be approximately
|
||
1.44 * 10^8 square kilometres that is equal to 1.44 *
|
||
10^18 square centimetres, which could be snapped to the integer
|
||
grid with a side of 1.2 * 10^9 centimetres. To make the Voronoi
|
||
graph
|
||
precise on the boundaries of that grid we will replicate input map 9
|
||
times (3x3), thus Voronoi diagram within a central piece will
|
||
provide us with a correct connectivity graph. This step will increase
|
||
the size of our grid to 3.6 * 10^9 centimetres that is less than 2^32.
|
||
So we are able to discretize the Red Planet's surface within a 1
|
||
centimetre
|
||
precision using the default input coordinate type (signed 32-bit
|
||
integer). That would imply maximum absolute error to be
|
||
equal up to 0.5 centimetres per coordinate. Considering the radius of
|
||
our robot to be
|
||
0.3 metres and for security reasons avoiding any large enough obstacles
|
||
that are within 1 metre distance from it that error would be irrelevant.<br>
|
||
|
||
<span style="color: rgb(0, 0, 0); font-family: sans-serif; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 13px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; background-color: rgb(249, 249, 249); display: inline ! important; float: none;"></span>
|
||
<h3>Output analysis</h3>
|
||
|
||
Estimates of the resulting Voronoi diagram precision were already
|
||
explained <a href="voronoi_main.htm">here</a>.
|
||
So to avoid those computations again we will simply state that the
|
||
maximum absolute error of the output geometries will be on the grid
|
||
boundaries and will be equal to 2^-16 centimetres, which is
|
||
approximately equal to 150 nanometres and is 100 times larger than
|
||
a radius of a complex molecule. We would like to notice that the
|
||
absolute error of the discretization step is much higher than the one
|
||
produced by the Voronoi diagram construction algorithm.
|
||
<h2>VLSI Design</h2>
|
||
|
||
<h3>Problem Statement</h3>
|
||
|
||
<img style="width: 400px; height: 283px;" alt="" src="images/vlsi.jpg"><br>
|
||
|
||
<br>
|
||
|
||
Very-large-scale integration (<a href="http://www.rulabinsky.com/cavd/index.html">VLSI</a>) is the
|
||
process of creating
|
||
integrated circuits by combining thousands of transistors into a single
|
||
chip. Considering the fact that it may take weeks or months to get an
|
||
integrated circuit manufactured, designers often spend large amounts of
|
||
time analyzing their layouts to avoid costly mistakes. One of the
|
||
common static analysis checks is minimum distance requirement between
|
||
the components of an integrated circuit (distance should be greater
|
||
than
|
||
specified value).<br>
|
||
|
||
<h3>Application of Voronoi diagram</h3>
|
||
|
||
It appears that the minimum distance between components of the input
|
||
set of points and segments corresponds to the one of the Voronoi
|
||
diagram edges. This means that we can iterate through each edge of
|
||
the Voronoi graph, extract the pair of input geometries that form it
|
||
and find
|
||
the distance between those. As the total amount of such edges is O(N)
|
||
value
|
||
(N - is the number of input geometries) the minimum distance could be
|
||
efficiently find in a linear time once we construct the diagram.<br>
|
||
|
||
<h3>Discretization of input geometries</h3>
|
||
|
||
The average size of the modern CPUs is around 2.5 x 2.5 centimetres.
|
||
Snapping this to the 32-bit integer grid will give discretization
|
||
precision of 2.5 / 2^33 centimetres or 3 picometres that is 10 times
|
||
smaller value than radius of an atom. That would be probably good
|
||
enough precision even for a few next generations of processors.<br>
|
||
|
||
<h3>Output analysis</h3>
|
||
|
||
The maximum absolute error of the output geometries will be 2.5 / 2^47
|
||
centimetres or 0.2 femtometres that is 10 times smaller value than
|
||
the radius of an electron. However in this particular case we are not
|
||
interested in the precision of the output, rather in its topology. As
|
||
it was noticed on
|
||
the <a href="voronoi_main.htm">Voronoi main page</a> very small edges
|
||
are removed from the Voronoi diagram. However user should not worry
|
||
because the edge that correspond to the minimal distance won't be among
|
||
those. That means that we would be able to 100% correctly identify a
|
||
pair of closest objects within the discretization precision.<br>
|
||
|
||
<h2>Conclusions</h2>
|
||
|
||
The above two examples show usage of the default Voronoi coordinate
|
||
types
|
||
in the macro and micro world. The main point of those was to give the user
|
||
understanding of a scale that the default coordinate types provide. There
|
||
are
|
||
two main points we didn't mention before, but that would be relevant to
|
||
the most real world problems:<br>
|
||
|
||
<ul>
|
||
|
||
<li>The absolute error of the coordinates of the output Voronoi
|
||
diagram
|
||
inside the 32-bit integer discretization grid is slightly smaller than
|
||
the absolute error of discretization itself, thus could be neglected at
|
||
all.</li>
|
||
<li>In both problems above we didn't consider error of measurement.
|
||
For example: is it possible to construct a map of the Mars within the
|
||
0.5
|
||
centimetres precision, or to get coordinates of the circuit parts
|
||
withing the subatomic precision? I guess the answer for both questions
|
||
would be "No". And that actually means that the error of the
|
||
discretization
|
||
step could be neglected comparing to the error produced by the
|
||
measuring
|
||
devices.<br>
|
||
</li>
|
||
</ul>
|
||
The second statement means that there is actually no point to provide
|
||
implementation that operates with floating-point input coordinates,
|
||
because those always could be snapped to the integer grid. In case the
|
||
user is not satisfied with the precision that the 32-bit integer grid
|
||
provides or would like to retrieve coordinates of the output geometries
|
||
within a smaller
|
||
relative error, follow the next paragraph.<br>
|
||
|
||
<h2>Voronoi Coordinate Types Configuration</h2>
|
||
|
||
In the following chapter we are going to extend input coordinate type
|
||
to the 48-bit signed
|
||
integer and output coordinate type to the 80-bit IEEE floating-point
|
||
type
|
||
(long double). The code for this chapter is available in <a href="../example/voronoi_advanced_tutorial.cpp">voroni_advanced_tutorial.cpp</a>.
|
||
While it won't be possible to compile it using the MSVC compiler (it
|
||
doesn't
|
||
support 80-bit long double type; ieee754.h header is required), it
|
||
should give a clear understanding of how the library supports the user
|
||
provided coordinate types.<br>
|
||
|
||
<br>
|
||
|
||
So the main step would be to declare the voronoi coordinate type traits
|
||
that satisfy set of restrictions explained <a href="voronoi_builder.htm">here</a>:<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">struct
|
||
my_voronoi_ctype_traits {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef boost::int64_t int_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef detail::extended_int<3> int_x2_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef detail::extended_int<3> uint_x2_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef detail::extended_int<128> big_int_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef fpt80 fpt_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef fpt80 efpt_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef my_ulp_comparison ulp_cmp_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef my_fpt_converter to_fpt_converter_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef my_fpt_converter to_efpt_converter_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">};<br>
|
||
<br>
|
||
</span>It is always better to use C++ built-in types wherever it's
|
||
possible. That's why we use the 64-bit signed integer type to handle
|
||
our
|
||
input coordinates. <span style="font-family: Courier New,Courier,monospace;">int_x2_type</span>
|
||
and <span style="font-family: Courier New,Courier,monospace;">uint_x2_type</span>
|
||
is required to handle 96-bit signed/unsigned integers. As there is no
|
||
such built-in type we use library provided efficient fixed integer
|
||
type.
|
||
The big integer type should be capable to handle 48 * 64 bit integers,
|
||
that is
|
||
less than 32 * 128, and so far we are good with <span style="font-family: Courier New,Courier,monospace;">extended_int<128></span>
|
||
type. We use the same floating point type for both <span style="font-family: Courier New,Courier,monospace;">fpt_type</span>
|
||
and <span style="font-family: Courier New,Courier,monospace;">efpt_type</span>
|
||
as it has enough exponent bits to represent both 48 * 32 bit and 48 *
|
||
64 bit integers (that is also the reason we don't need two
|
||
floating-point converter structures). The <span style="font-family: Courier New,Courier,monospace;">ulp_cmp_type</span>
|
||
structure checks weather two IEEE floating-point values are within the
|
||
given signed integer ulp range (we won't analyze corresponding code
|
||
here as it requires deep understanding of the <a href="http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html">floating-point
|
||
architecture</a> and its <a href="../../../boost/polygon/detail/voronoi_ctypes.hpp">usage to compare
|
||
floating-point values</a>), but just to mention the declaration is
|
||
following:<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"></span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">struct
|
||
my_ulp_comparison {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> enum
|
||
Result {</span><span style="font-family: Courier New,Courier,monospace;"><br>
|
||
LESS = -1,</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
EQUAL = 0,</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
MORE = 1</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> };</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> Result
|
||
operator()(fpt80 a, fpt80 b, unsigned int maxUlps) const;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">};<br>
|
||
<br>
|
||
</span>The last step would be to declare the <span style="font-family: Courier New,Courier,monospace;">my_fpt_converter</span>
|
||
structure (converts the integer types to the floating-point type):<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">struct
|
||
my_fpt_converter {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
template <typename T></span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> fpt80
|
||
operator()(const T& that) const {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
return static_cast<fpt80>(that);</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> }</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
template <size_t N></span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> fpt80
|
||
operator()(const typename detail::extended_int<N>& that)
|
||
const {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
fpt80 result = 0.0;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
for (size_t i = 1; i <= (std::min)((size_t)3, that.size()); ++i) {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
if (i != 1)</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
result *= static_cast<fpt80>(0x100000000ULL);</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
result += that.chunks()[that.size() - i];</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
}</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
return (that.count() < 0) ? -result : result;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> }</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">};<br>
|
||
<br>
|
||
</span>At this point we are done with declaration of the Voronoi
|
||
coordinate type traits. The next step is to declare the Voronoi diagram
|
||
traits:<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">struct
|
||
my_voronoi_diagram_traits {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef fpt80 coordinate_type;</span><span style="font-family: Courier New,Courier,monospace;"></span><span style="font-family: Courier New,Courier,monospace;"></span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef voronoi_cell<coordinate_type> cell_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef voronoi_vertex<coordinate_type> vertex_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef voronoi_edge<coordinate_type> edge_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
typedef struct {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> public:</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
enum { ULPS = 128 };</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
bool operator()(const point_type &v1, const point_type &v2)
|
||
const {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
return (ulp_cmp(v1.x(), v2.x(), ULPS) == my_ulp_comparison::EQUAL
|
||
&&</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
ulp_cmp(v1.y(), v2.y(), ULPS) == my_ulp_comparison::EQUAL);</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
}</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
private:</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
my_ulp_comparison ulp_cmp;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"> }
|
||
vertex_equality_predicate_type;</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">};</span><br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"></span><br>
|
||
|
||
Above we simply declared the Voronoi primitive types
|
||
and vertex
|
||
equality predicate using the new coordinate type and corresponding ulp
|
||
comparison structure. As we are done with the declaration of the
|
||
coordinate
|
||
type specific structures we are ready to proceed to the construction
|
||
step itself. The first step would be to initialize voronoi_builder
|
||
structure with a set of random points:<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">// Random
|
||
generator and distribution. MAX is equal to 2^48.</span><br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">boost::mt19937_64
|
||
gen(std::time(0));</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">boost::random::uniform_int_distribution<boost::int64_t>
|
||
distr(-MAX, MAX-1);<br>
|
||
<br>
|
||
</span><span style="font-family: Courier New,Courier,monospace;">
|
||
// Declaring and configuring Voronoi builder with the new coordinate
|
||
type traits.<br>
|
||
voronoi_builder<boost::int64_t, my_voronoi_ctype_traits> vb;</span><br>
|
||
<span style="font-family: Courier New,Courier,monospace;"></span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">// Voronoi
|
||
builder initialization step.<br>
|
||
for (size_t i = 0; i < GENERATED_POINTS; ++i) {</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
boost::int64_t x = distr(gen);</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
boost::int64_t y = distr(gen);</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
vb.insert_point(x, y);</span><br style="font-family: Courier New,Courier,monospace;">
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">}<br>
|
||
<br>
|
||
</span>The second step would be to generate the Voronoi diagram and
|
||
this is done as before with the two lines of code:<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">// Declaring
|
||
and configuring Voronoi diagram structure with the new coordinate type
|
||
traits.<br>
|
||
voronoi_diagram<fpt80, my_voronoi_diagram_traits> vd;</span><br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">vb.construct(&vd);<br>
|
||
<br>
|
||
</span>From this point the user can operate with the Voronoi diagram
|
||
data structure
|
||
and in our tutorial we output some simple stats of the resulting
|
||
Voronoi graph. Probably the hardest part of this tutorial is
|
||
the declaration of the ulp comparison structure. The library provides
|
||
efficient well-commented cross-platform implementation for 64-bit
|
||
floating-point type (double). So the best advice would be to follow
|
||
that implementation, but before doing that really consider thoughtfully if the
|
||
default
|
||
coordinate types are not capable to deal with your problem.<br>
|
||
|
||
<br>
|
||
|
||
<table class="docinfo" id="table1" frame="void" rules="none">
|
||
|
||
<colgroup> <col class="docinfo-name"><col class="docinfo-content"> </colgroup>
|
||
<tbody valign="top">
|
||
<tr>
|
||
<th class="docinfo-name">Copyright:</th>
|
||
<td>Copyright <20> Andrii Sydorchuk 2010-2012.</td>
|
||
</tr>
|
||
<tr class="field">
|
||
<th class="docinfo-name">License:</th>
|
||
<td class="field-body">Distributed under the Boost Software
|
||
License, Version 1.0. (See accompanying file <tt class="literal"> <span class="pre">LICENSE_1_0.txt</span></tt> or copy at <a class="reference" target="_top" href="http://www.boost.org/LICENSE_1_0.txt">
|
||
http://www.boost.org/LICENSE_1_0.txt</a>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
</body></html> |