1 | // Copyright 2005 Google Inc. All Rights Reserved. |
2 | |
3 | #include <stdlib.h> |
4 | #include <sys/resource.h> // for rusage, RUSAGE_SELF |
5 | #include <limits.h> |
6 | |
7 | #include <vector> |
8 | using std::vector; |
9 | |
10 | |
11 | #include "base/integral_types.h" |
12 | #include "base/logging.h" |
13 | #include "base/stringprintf.h" |
14 | #include "util/math/matrix3x3-inl.h" |
15 | #include "s2testing.h" |
16 | #include "s2loop.h" |
17 | #include "s2latlng.h" |
18 | #include "s2latlngrect.h" |
19 | #include "s2polygon.h" |
20 | #include "s2polyline.h" |
21 | #include "s2cellunion.h" |
22 | #include "s2cell.h" |
23 | #include "s2cap.h" |
24 | #include "strings/split.h" |
25 | #include "strings/strutil.h" |
26 | |
27 | S2Testing::Random::Random() { |
28 | srandom(4); |
29 | } |
30 | |
31 | uint64 S2Testing::Random::Rand64() { |
32 | int bits_of_rand = log2(1ULL + RAND_MAX); |
33 | uint64 result = 0; |
34 | for (int num_bits = 0; num_bits < 8 * sizeof(uint64); |
35 | num_bits += bits_of_rand) { |
36 | result <<= bits_of_rand; |
37 | result += random(); |
38 | } |
39 | return result; |
40 | } |
41 | |
42 | uint32 S2Testing::Random::Rand32() { |
43 | return Rand64() & ((1ULL << 32) - 1); |
44 | } |
45 | |
46 | double S2Testing::Random::RandDouble() { |
47 | const int NUMBITS = 53; |
48 | return ldexp(Rand64() & ((1ULL << NUMBITS) - 1ULL), -NUMBITS); |
49 | } |
50 | |
51 | int S2Testing::Random::Uniform(int upper_bound) { |
52 | return static_cast<uint32>(RandDouble() * upper_bound); |
53 | } |
54 | |
55 | bool S2Testing::Random::OneIn(int x) { |
56 | return Uniform(x) == 0; |
57 | } |
58 | |
59 | int32 S2Testing::Random::Skewed(int max_log) { |
60 | const int32 base = Rand32() % (max_log + 1); |
61 | return Rand32() & ((1u << base) - 1); |
62 | } |
63 | |
64 | int32 S2Testing::Random::PlusOrMinus(int32 value, float multiplier) { |
65 | const int32 range = static_cast<int32>(value * multiplier); |
66 | const int32 rand_val = Uniform(range * 2); |
67 | return value - range + rand_val; |
68 | } |
69 | |
70 | S2Testing::Random S2Testing::rnd; |
71 | |
72 | static double ParseDouble(const string& str) { |
73 | char* end_ptr = NULL; |
74 | double value = strtod(str.c_str(), &end_ptr); |
75 | CHECK(end_ptr && *end_ptr == 0) << ": str == \"" << str << "\"" ; |
76 | return value; |
77 | } |
78 | |
79 | void S2Testing::ParseLatLngs(string const& str, vector<S2LatLng>* latlngs) { |
80 | vector<pair<string, string> > p; |
81 | CHECK(DictionaryParse(str, &p)) << ": str == \"" << str << "\"" ; |
82 | latlngs->clear(); |
83 | for (int i = 0; i < p.size(); ++i) { |
84 | latlngs->push_back(S2LatLng::FromDegrees(ParseDouble(p[i].first), |
85 | ParseDouble(p[i].second))); |
86 | } |
87 | } |
88 | |
89 | void S2Testing::ParsePoints(string const& str, vector<S2Point>* vertices) { |
90 | vector<S2LatLng> latlngs; |
91 | S2Testing::ParseLatLngs(str, &latlngs); |
92 | vertices->clear(); |
93 | for (int i = 0; i < latlngs.size(); ++i) { |
94 | vertices->push_back(latlngs[i].ToPoint()); |
95 | } |
96 | } |
97 | |
98 | S2Point S2Testing::MakePoint(string const& str) { |
99 | vector<S2Point> vertices; |
100 | ParsePoints(str, &vertices); |
101 | CHECK_EQ(vertices.size(), 1); |
102 | return vertices[0]; |
103 | } |
104 | |
105 | S2LatLngRect S2Testing::MakeLatLngRect(string const& str) { |
106 | vector<S2LatLng> latlngs; |
107 | ParseLatLngs(str, &latlngs); |
108 | CHECK_GT(latlngs.size(), 0); |
109 | S2LatLngRect rect = S2LatLngRect::FromPoint(latlngs[0]); |
110 | for (int i = 1; i < latlngs.size(); ++i) { |
111 | rect.AddPoint(latlngs[i]); |
112 | } |
113 | return rect; |
114 | } |
115 | |
116 | S2Loop* S2Testing::MakeLoop(string const& str) { |
117 | vector<S2Point> vertices; |
118 | ParsePoints(str, &vertices); |
119 | return new S2Loop(vertices); |
120 | } |
121 | |
122 | S2Polyline* S2Testing::MakePolyline(string const& str) { |
123 | vector<S2Point> vertices; |
124 | ParsePoints(str, &vertices); |
125 | return new S2Polyline(vertices); |
126 | } |
127 | |
128 | S2Polygon* S2Testing::MakePolygon(string const& str) { |
129 | vector<string> loop_strs; |
130 | SplitStringUsing(str, ";" , &loop_strs); |
131 | vector<S2Loop*> loops; |
132 | for (int i = 0; i < loop_strs.size(); ++i) { |
133 | S2Loop* loop = MakeLoop(loop_strs[i]); |
134 | loop->Normalize(); |
135 | loops.push_back(loop); |
136 | } |
137 | return new S2Polygon(&loops); // Takes ownership. |
138 | } |
139 | |
140 | |
141 | S2Loop* S2Testing::MakeRegularLoop(S2Point const& center, |
142 | int num_vertices, |
143 | double angle_radius) { |
144 | Matrix3x3_d m; |
145 | S2::GetFrame(center, &m); |
146 | vector<S2Point> vertices; |
147 | double radian_step = 2 * M_PI / num_vertices; |
148 | // We create the vertices on the plane tangent to center, so the |
149 | // radius on that plane is larger. |
150 | double planar_radius = tan(angle_radius); |
151 | for (int vi = 0; vi < num_vertices; ++vi) { |
152 | double angle = vi * radian_step; |
153 | S2Point p(planar_radius * cos(angle), planar_radius * sin(angle), 1); |
154 | vertices.push_back(S2::FromFrame(m, p.Normalize())); |
155 | } |
156 | return new S2Loop(vertices); |
157 | } |
158 | |
159 | static void AppendVertex(S2Point const& p, string* out) { |
160 | S2LatLng ll(p); |
161 | StringAppendF(out, "%.17g:%.17g" , ll.lat().degrees(), ll.lng().degrees()); |
162 | } |
163 | |
164 | static void AppendVertices(S2Point const* v, int n, string* out) { |
165 | for (int i = 0; i < n; ++i) { |
166 | if (i > 0) *out += ", " ; |
167 | AppendVertex(v[i], out); |
168 | } |
169 | } |
170 | |
171 | string S2Testing::ToString(S2Point const& point) { |
172 | string out; |
173 | AppendVertex(point, &out); |
174 | return out; |
175 | } |
176 | |
177 | string S2Testing::ToString(S2LatLngRect const& rect) { |
178 | string out; |
179 | AppendVertex(rect.lo().ToPoint(), &out); |
180 | out += ", " ; |
181 | AppendVertex(rect.hi().ToPoint(), &out); |
182 | return out; |
183 | } |
184 | |
185 | string S2Testing::ToString(S2Loop const* loop) { |
186 | string out; |
187 | AppendVertices(&loop->vertex(0), loop->num_vertices(), &out); |
188 | return out; |
189 | } |
190 | |
191 | string S2Testing::ToString(S2Polyline const* polyline) { |
192 | string out; |
193 | AppendVertices(&polyline->vertex(0), polyline->num_vertices(), &out); |
194 | return out; |
195 | } |
196 | |
197 | string S2Testing::ToString(S2Polygon const* polygon) { |
198 | string out; |
199 | for (int i = 0; i < polygon->num_loops(); ++i) { |
200 | if (i > 0) out += ";\n" ; |
201 | S2Loop* loop = polygon->loop(i); |
202 | AppendVertices(&loop->vertex(0), loop->num_vertices(), &out); |
203 | } |
204 | return out; |
205 | } |
206 | |
207 | void DumpLoop(S2Loop const* loop) { |
208 | // Only for calling from a debugger. |
209 | cout << S2Testing::ToString(loop) << "\n" ; |
210 | } |
211 | |
212 | void DumpPolyline(S2Polyline const* polyline) { |
213 | // Only for calling from a debugger. |
214 | cout << S2Testing::ToString(polyline) << "\n" ; |
215 | } |
216 | |
217 | void DumpPolygon(S2Polygon const* polygon) { |
218 | // Only for calling from a debugger. |
219 | cout << S2Testing::ToString(polygon) << "\n" ; |
220 | } |
221 | |
222 | S2Point S2Testing::RandomPoint() { |
223 | // The order of evaluation of function arguments is unspecified, |
224 | // so we may not just call S2Point with three RandDouble-based args. |
225 | // Use temporaries to induce sequence points between calls. |
226 | double x = 2 * rnd.RandDouble() - 1; |
227 | double y = 2 * rnd.RandDouble() - 1; |
228 | double z = 2 * rnd.RandDouble() - 1; |
229 | return S2Point(x, y, z).Normalize(); |
230 | } |
231 | |
232 | void S2Testing::GetRandomFrame(S2Point* x, S2Point* y, S2Point* z) { |
233 | *x = RandomPoint(); |
234 | *y = x->CrossProd(RandomPoint()).Normalize(); |
235 | *z = x->CrossProd(*y).Normalize(); |
236 | } |
237 | |
238 | S2CellId S2Testing::GetRandomCellId(int level) { |
239 | int face = rnd.Uniform(S2CellId::kNumFaces); |
240 | uint64 pos = rnd.Rand64() & ((1ULL << (2 * S2CellId::kMaxLevel)) - 1); |
241 | return S2CellId::FromFacePosLevel(face, pos, level); |
242 | } |
243 | |
244 | S2CellId S2Testing::GetRandomCellId() { |
245 | return GetRandomCellId(rnd.Uniform(S2CellId::kMaxLevel + 1)); |
246 | } |
247 | |
248 | S2Cap S2Testing::GetRandomCap(double min_area, double max_area) { |
249 | double cap_area = max_area * pow(min_area / max_area, rnd.RandDouble()); |
250 | DCHECK_GE(cap_area, min_area); |
251 | DCHECK_LE(cap_area, max_area); |
252 | |
253 | // The surface area of a cap is 2*Pi times its height. |
254 | return S2Cap::FromAxisArea(RandomPoint(), cap_area); |
255 | } |
256 | |
257 | S2Point S2Testing::SamplePoint(S2Cap const& cap) { |
258 | // We consider the cap axis to be the "z" axis. We choose two other axes to |
259 | // complete the coordinate frame. |
260 | |
261 | Matrix3x3_d m; |
262 | S2::GetFrame(cap.axis(), &m); |
263 | |
264 | // The surface area of a spherical cap is directly proportional to its |
265 | // height. First we choose a random height, and then we choose a random |
266 | // point along the circle at that height. |
267 | |
268 | double h = rnd.RandDouble() * cap.height(); |
269 | double theta = 2 * M_PI * rnd.RandDouble(); |
270 | double r = sqrt(h * (2 - h)); // Radius of circle. |
271 | |
272 | // The result should already be very close to unit-length, but we might as |
273 | // well make it accurate as possible. |
274 | return S2::FromFrame(m, S2Point(cos(theta) * r, sin(theta) * r, 1 - h)) |
275 | .Normalize(); |
276 | } |
277 | |
278 | void S2Testing::CheckCovering(S2Region const& region, |
279 | S2CellUnion const& covering, |
280 | bool check_tight, |
281 | S2CellId const& id) { |
282 | if (!id.is_valid()) { |
283 | for (int face = 0; face < 6; ++face) { |
284 | CheckCovering(region, covering, check_tight, |
285 | S2CellId::FromFacePosLevel(face, 0, 0)); |
286 | } |
287 | return; |
288 | } |
289 | |
290 | if (!region.MayIntersect(S2Cell(id))) { |
291 | // If region does not intersect id, then neither should the covering. |
292 | if (check_tight) CHECK(!covering.Intersects(id)); |
293 | |
294 | } else if (!covering.Contains(id)) { |
295 | // The region may intersect id, but we can't assert that the covering |
296 | // intersects id because we may discover that the region does not actually |
297 | // intersect upon further subdivision. (MayIntersect is not exact.) |
298 | CHECK(!region.Contains(S2Cell(id))); |
299 | CHECK(!id.is_leaf()); |
300 | S2CellId end = id.child_end(); |
301 | S2CellId child; |
302 | for (child = id.child_begin(); child != end; child = child.next()) { |
303 | CheckCovering(region, covering, check_tight, child); |
304 | } |
305 | } |
306 | } |
307 | |
308 | double S2Testing::GetCpuTime() { |
309 | struct rusage ru; |
310 | CHECK_EQ(getrusage(RUSAGE_SELF, &ru), 0); |
311 | return ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1e6; |
312 | } |
313 | |