#### /hphp/benchmarks/php-octane/quick-hull.php

PHP | 189 lines | 148 code | 28 blank | 13 comment | 29 complexity | 40d8347abe2898446a597e98d8f8f2c1 MD5 | raw file
```  1<?
2
3\$MIN_POS = -100000;
4\$MAX_POS = 100000;
5
6
7function generatePoints(\$n) {
8  global \$MIN_POS;
9  global \$MAX_POS;
10
11  \$points = array();
12  for (\$i = 0; \$i < \$n; \$i++) {
13    array_push(\$points, array(
14      "x" => (float)rand(\$MIN_POS, \$MAX_POS),
15      "y" => (float)rand(\$MIN_POS, \$MAX_POS)
16    ));
17  }
18  array_unique(\$points, SORT_REGULAR);
19  return \$points;
20}
21
22function findMinXPos(\$points) {
23  if (count(\$points) === 0) return null;
24
25  \$min_x_point = \$points[0];
26  for (\$i = 1; \$i < count(\$points); \$i++) {
27    \$current_point = \$points[\$i];
28    if (\$min_x_point["x"] < \$current_point["x"]) {
29      \$min_x_point = \$current_point;
30    }
31  }
32
33  return \$min_x_point;
34}
35
36function side(\$a, \$b, \$c) {
37  // Returns positive or negative depending on which side \$c is when looking
38  // from \$a to \$b.
39  \$determinant = (\$b['x'] - \$a['x']) * (\$c['y'] - \$a['y']) -
40    (\$b['y'] - \$a['y']) * (\$c['x'] - \$a['x']);
41  if (\$determinant > 0) return 1;
42  if (\$determinant < 0) return -1;
43  return 0;
44}
45
46function isInTriangle(\$p, \$a, \$b, \$c) {
47  list(\$p_x, \$p_y) = array(\$p['x'], \$p['y']);
48  list(\$a_x, \$a_y) = array(\$a['x'], \$a['y']);
49  list(\$b_x, \$b_y) = array(\$b['x'], \$b['y']);
50  list(\$c_x, \$c_y) = array(\$c['x'], \$c['y']);
51  \$denom = (\$b_y - \$c_y) * (\$a_x - \$c_x) + (\$c_x - \$b_x) * (\$a_y - \$c_y);
52  if (\$denom > 0 || \$denom < 0) {
53    \$alpha = ((\$b_y - \$c_y) * (\$p_x - \$c_x) + (\$c_x - \$b_x) * (\$p_y - \$c_y)) /
54      \$denom;
55    \$beta = ((\$c_y - \$a_y) * (\$p_x - \$c_x) + (\$a_x - \$c_x) * (\$p_y - \$c_y)) /
56      \$denom;
57  } else {
58    \$alpha = 0.0;
59    \$beta = 0.0;
60  }
61  \$gamma = 1.0 - \$alpha - \$beta;
62  return \$alpha > 0.0 && \$beta > 0.0 && \$gamma > 0.0;
63}
64
65function lineEquation(\$p1, \$p2) {
66  // y = mx + b
67  // b = y - mx
68  // mx - y + b = 0
69  // ax + by + c = 0
70  list(\$x1, \$y1) = array(\$p1['x'], \$p1['y']);
71  list(\$x2, \$y2) = array(\$p2['x'], \$p2['y']);
72  if (\$x1 === \$x2) {
73    return array(
74      "a" => 1.0,
75      "b" => 0.0,
76      "c" => -\$x1
77    );
78  }
79  \$slope = (\$y2 - \$y1) / (\$x2 - \$x1);
80  \$b = \$y1 - \$slope * \$x1;
81  return array(
82    "a" => \$slope,
83    "b" => -1.0,
84    "c" => \$b
85  );
86}
87
88function distanceToLine(\$line, \$p) {
89  list(\$a, \$b, \$c) = array(\$line['a'], \$line['b'], \$line['c']);
90  list(\$x_0, \$y_0) = array(\$p['x'], \$p['y']);
91  return abs(\$a * \$x_0 + \$b * \$y_0 + \$c) / sqrt(\$a * \$a + \$b * \$b);
92}
93
94function partitionSides(\$a, \$b, \$points) {
95  return array(
96    array_values(array_filter(\$points, function(\$p) use (\$a, \$b) {
97      if (\$p == \$a || \$p == \$b)
98        return false;
99      return side(\$a, \$b, \$p) >= 0.0;
100    })),
101    array_values(array_filter(\$points, function(\$p) use (\$a, \$b) {
102      if (\$p == \$a || \$p == \$b)
103        return false;
104      return side(\$a, \$b, \$p) < 0.0;
105    }))
106  );
107}
108
109function buildHullWithTriangles(\$a, \$b, \$points) {
110  if (count(\$points) < 2) return \$points;
111
112  \$hull = array();
113
114  // Step 3: Find the point that is farthest from the dividing line.
115  \$line = lineEquation(\$a, \$b);
116  \$farthest_dist = 0.0;
117  \$farthest_point = array_reduce(\$points,
118    function(\$carry, \$item) use (\$line, &\$farthest_dist) {
119      \$dist = distanceToLine(\$line, \$item);
120      if (\$dist > \$farthest_dist) {
121        \$farthest_dist = \$dist;
122        return \$item;
123      }
124      return \$carry;
125    }, \$points[0]);
126  array_push(\$hull, \$farthest_point);
127
128  // Step 4: Filter out all points inside the triangle formed by the new point.
129  \$c = \$farthest_point;
130  \$outsideTriangle = array_values(
131    array_filter(\$points, function(\$p) use (\$a, \$b, \$c) {
132      if (\$p == \$a || \$p == \$b || \$p == \$c)
133        return false;
134      return !isInTriangle(\$p, \$a, \$b, \$c);
135    }));
136
137  list(\$side1, \$side2) = partitionSides(\$a, \$c, \$outsideTriangle);
138
139  // Step 5: Using the two new lines, recursively create triangles to filter
140  // points until there are no more left.
141  \$hull = array_merge(
142    \$hull,
143    buildHullWithTriangles(\$a, \$c, \$side1),
144    buildHullWithTriangles(\$b, \$c, \$side2)
145  );
146  return \$hull;
147}
148
149function findConvexHull(\$points) {
150  if (count(\$points) < 4) return \$points;
151
152  \$hull = array();
153
154  // Step 1: Find the two points with the min and max x positions.
155  \$min_point = array_reduce(\$points, function(\$carry, \$item) {
156    return (\$item["x"] < \$carry["x"]) ? \$item : \$carry;
157  }, \$points[0]);
158  \$max_point = array_reduce(\$points, function(\$carry, \$item) {
159    return (\$item["x"] > \$carry["x"]) ? \$item : \$carry;
160  }, \$points[0]);
161
162  array_push(\$hull, \$min_point, \$max_point);
163
164  // Step 2: Use the line between the two points to divide the remaining
165  // points into two sets, one for each side of the line.
166  list(\$side1, \$side2) = partitionSides(\$min_point, \$max_point, \$points);
167
168  \$hull = array_merge(
169    \$hull,
170    buildHullWithTriangles(\$min_point, \$max_point, \$side1),
171    buildHullWithTriangles(\$min_point, \$max_point, \$side2)
172  );
173
174  return \$hull;
175}
176
177function verifyConvexHull(\$actual, \$expected) {
178  return true;
179}
180
181\$quickHull = function() {
182  srand(42);
183  \$points = generatePoints(1000);
184  \$hull = findConvexHull(\$points);
185};
186
187\$QuickHull = new BenchmarkSuite('QuickHull', [100000], array(
188  new Benchmark('QuickHull', true, false, 4400, \$quickHull)
189));
```