##// END OF EJS Templates
Correction clang format for Linux
perrinel -
r618:2266e13252d4
parent child
Show More
@@ -1,522 +1,522
1 1 #include "Data/DataSeries.h"
2 2 #include "Data/ScalarSeries.h"
3 3 #include "Data/VectorSeries.h"
4 4
5 5 #include <cmath>
6 6
7 7 #include <QObject>
8 8 #include <QtTest>
9 9
10 10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12 12
13 13 class TestDataSeries : public QObject {
14 14 Q_OBJECT
15 15 private:
16 16 template <typename T>
17 17 void testValuesBoundsStructure()
18 18 {
19 19 // ////////////// //
20 20 // Test structure //
21 21 // ////////////// //
22 22
23 23 // Data series to get values bounds
24 24 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
25 25
26 26 // x-axis range
27 27 QTest::addColumn<double>("minXAxis");
28 28 QTest::addColumn<double>("maxXAxis");
29 29
30 30 // Expected results
31 31 QTest::addColumn<bool>(
32 32 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
33 33 QTest::addColumn<double>("expectedMinValue");
34 34 QTest::addColumn<double>("expectedMaxValue");
35 35 }
36 36
37 37 template <typename T>
38 38 void testValuesBounds()
39 39 {
40 40 QFETCH(std::shared_ptr<T>, dataSeries);
41 41 QFETCH(double, minXAxis);
42 42 QFETCH(double, maxXAxis);
43 43
44 44 QFETCH(bool, expectedOK);
45 45 QFETCH(double, expectedMinValue);
46 46 QFETCH(double, expectedMaxValue);
47 47
48 48 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
49 49 auto end = dataSeries->cend();
50 50
51 51 // Checks iterators with expected result
52 52 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
53 53
54 54 if (expectedOK) {
55 55 auto compare = [](const auto &v1, const auto &v2) {
56 56 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
57 57 };
58 58
59 59 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
60 60 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
61 61 }
62 62 }
63 63
64 64 private slots:
65 65 /// Input test data
66 66 /// @sa testCtor()
67 67 void testCtor_data();
68 68
69 69 /// Tests construction of a data series
70 70 void testCtor();
71 71
72 72 /// Input test data
73 73 /// @sa testMerge()
74 74 void testMerge_data();
75 75
76 76 /// Tests merge of two data series
77 77 void testMerge();
78 78
79 79 /// Input test data
80 80 /// @sa testMinXAxisData()
81 81 void testMinXAxisData_data();
82 82
83 83 /// Tests get min x-axis data of a data series
84 84 void testMinXAxisData();
85 85
86 86 /// Input test data
87 87 /// @sa testMaxXAxisData()
88 88 void testMaxXAxisData_data();
89 89
90 90 /// Tests get max x-axis data of a data series
91 91 void testMaxXAxisData();
92 92
93 93 /// Input test data
94 94 /// @sa testXAxisRange()
95 95 void testXAxisRange_data();
96 96
97 97 /// Tests get x-axis range of a data series
98 98 void testXAxisRange();
99 99
100 100 /// Input test data
101 101 /// @sa testValuesBoundsScalar()
102 102 void testValuesBoundsScalar_data();
103 103
104 104 /// Tests get values bounds of a scalar series
105 105 void testValuesBoundsScalar();
106 106
107 107 /// Input test data
108 108 /// @sa testValuesBoundsVector()
109 109 void testValuesBoundsVector_data();
110 110
111 111 /// Tests get values bounds of a vector series
112 112 void testValuesBoundsVector();
113 113 };
114 114
115 115 void TestDataSeries::testCtor_data()
116 116 {
117 117 // ////////////// //
118 118 // Test structure //
119 119 // ////////////// //
120 120
121 121 // x-axis data
122 122 QTest::addColumn<QVector<double> >("xAxisData");
123 123 // values data
124 124 QTest::addColumn<QVector<double> >("valuesData");
125 125
126 126 // expected x-axis data
127 127 QTest::addColumn<QVector<double> >("expectedXAxisData");
128 128 // expected values data
129 129 QTest::addColumn<QVector<double> >("expectedValuesData");
130 130
131 131 // ////////// //
132 132 // Test cases //
133 133 // ////////// //
134 134
135 135 QTest::newRow("invalidData (different sizes of vectors)")
136 136 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
137 137 << QVector<double>{} << QVector<double>{};
138 138
139 139 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
140 140 << QVector<double>{100., 200., 300., 400., 500.}
141 141 << QVector<double>{1., 2., 3., 4., 5.}
142 142 << QVector<double>{100., 200., 300., 400., 500.};
143 143
144 144 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
145 145 << QVector<double>{100., 200., 300., 400., 500.}
146 146 << QVector<double>{1., 2., 3., 4., 5.}
147 147 << QVector<double>{500., 400., 300., 200., 100.};
148 148
149 149 QTest::newRow("unsortedData2")
150 150 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
151 151 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
152 152 }
153 153
154 154 void TestDataSeries::testCtor()
155 155 {
156 156 // Creates series
157 157 QFETCH(QVector<double>, xAxisData);
158 158 QFETCH(QVector<double>, valuesData);
159 159
160 160 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
161 161 Unit{}, Unit{});
162 162
163 163 // Validates results : we check that the data series is sorted on its x-axis data
164 164 QFETCH(QVector<double>, expectedXAxisData);
165 165 QFETCH(QVector<double>, expectedValuesData);
166 166
167 167 auto seriesXAxisData = series->xAxisData()->data();
168 168 auto seriesValuesData = series->valuesData()->data();
169 169
170 170 QVERIFY(
171 171 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
172 172 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
173 173 seriesValuesData.cbegin()));
174 174 }
175 175
176 176 namespace {
177 177
178 178 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
179 179 QVector<double> valuesData)
180 180 {
181 181 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
182 182 Unit{});
183 183 }
184 184
185 185 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
186 186 QVector<double> xValuesData,
187 187 QVector<double> yValuesData,
188 188 QVector<double> zValuesData)
189 189 {
190 190 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
191 191 std::move(yValuesData), std::move(zValuesData), Unit{},
192 192 Unit{});
193 193 }
194 194
195 195 } // namespace
196 196
197 197 void TestDataSeries::testMerge_data()
198 198 {
199 199 // ////////////// //
200 200 // Test structure //
201 201 // ////////////// //
202 202
203 203 // Data series to merge
204 204 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
205 205 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
206 206
207 207 // Expected values in the first data series after merge
208 208 QTest::addColumn<QVector<double> >("expectedXAxisData");
209 209 QTest::addColumn<QVector<double> >("expectedValuesData");
210 210
211 211 // ////////// //
212 212 // Test cases //
213 213 // ////////// //
214 214
215 215 QTest::newRow("sortedMerge")
216 216 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
217 217 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
218 218 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
219 219 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
220 220
221 221 QTest::newRow("unsortedMerge")
222 222 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
223 223 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
224 224 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
225 225 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
226 226
227 227 QTest::newRow("unsortedMerge2")
228 228 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
229 229 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
230 230 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
231 231 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
232 232
233 233 QTest::newRow("unsortedMerge3")
234 234 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
235 235 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
236 236 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
237 237 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
238 238 }
239 239
240 240 void TestDataSeries::testMerge()
241 241 {
242 242 // Merges series
243 243 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
244 244 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
245 245
246 246 dataSeries->merge(dataSeries2.get());
247 247
248 248 // Validates results : we check that the merge is valid and the data series is sorted on its
249 249 // x-axis data
250 250 QFETCH(QVector<double>, expectedXAxisData);
251 251 QFETCH(QVector<double>, expectedValuesData);
252 252
253 253 auto seriesXAxisData = dataSeries->xAxisData()->data();
254 254 auto seriesValuesData = dataSeries->valuesData()->data();
255 255
256 256 QVERIFY(
257 257 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
258 258 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
259 259 seriesValuesData.cbegin()));
260 260 }
261 261
262 262 void TestDataSeries::testMinXAxisData_data()
263 263 {
264 264 // ////////////// //
265 265 // Test structure //
266 266 // ////////////// //
267 267
268 268 // Data series to get min data
269 269 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
270 270
271 271 // Min data
272 272 QTest::addColumn<double>("min");
273 273
274 274 // Expected results
275 275 QTest::addColumn<bool>(
276 276 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
277 277 QTest::addColumn<double>(
278 278 "expectedMin"); // Expected value when method doesn't return end iterator
279 279
280 280 // ////////// //
281 281 // Test cases //
282 282 // ////////// //
283 283
284 284 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
285 285 {100., 200., 300., 400., 500.})
286 286 << 0. << true << 1.;
287 287 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
288 288 {100., 200., 300., 400., 500.})
289 289 << 1. << true << 1.;
290 290 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
291 291 {100., 200., 300., 400., 500.})
292 292 << 1.1 << true << 2.;
293 293 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
294 294 {100., 200., 300., 400., 500.})
295 295 << 5. << true << 5.;
296 296 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
297 297 {100., 200., 300., 400., 500.})
298 298 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
299 299 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
300 300 << std::numeric_limits<double>::quiet_NaN();
301 301 }
302 302
303 303 void TestDataSeries::testMinXAxisData()
304 304 {
305 305 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
306 306 QFETCH(double, min);
307 307
308 308 QFETCH(bool, expectedOK);
309 309 QFETCH(double, expectedMin);
310 310
311 311 auto it = dataSeries->minXAxisData(min);
312 312
313 313 QCOMPARE(expectedOK, it != dataSeries->cend());
314 314
315 315 // If the method doesn't return a end iterator, checks with expected value
316 316 if (expectedOK) {
317 317 QCOMPARE(expectedMin, it->x());
318 318 }
319 319 }
320 320
321 321 void TestDataSeries::testMaxXAxisData_data()
322 322 {
323 323 // ////////////// //
324 324 // Test structure //
325 325 // ////////////// //
326 326
327 327 // Data series to get max data
328 328 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
329 329
330 330 // Max data
331 331 QTest::addColumn<double>("max");
332 332
333 333 // Expected results
334 334 QTest::addColumn<bool>(
335 335 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
336 336 QTest::addColumn<double>(
337 337 "expectedMax"); // Expected value when method doesn't return end iterator
338 338
339 339 // ////////// //
340 340 // Test cases //
341 341 // ////////// //
342 342
343 343 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
344 344 {100., 200., 300., 400., 500.})
345 345 << 6. << true << 5.;
346 346 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
347 347 {100., 200., 300., 400., 500.})
348 348 << 5. << true << 5.;
349 349 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
350 350 {100., 200., 300., 400., 500.})
351 351 << 4.9 << true << 4.;
352 352 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
353 353 {100., 200., 300., 400., 500.})
354 354 << 1.1 << true << 1.;
355 355 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
356 356 {100., 200., 300., 400., 500.})
357 357 << 1. << true << 1.;
358 358 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
359 359 << std::numeric_limits<double>::quiet_NaN();
360 360 }
361 361
362 362 void TestDataSeries::testMaxXAxisData()
363 363 {
364 364 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
365 365 QFETCH(double, max);
366 366
367 367 QFETCH(bool, expectedOK);
368 368 QFETCH(double, expectedMax);
369 369
370 370 auto it = dataSeries->maxXAxisData(max);
371 371
372 372 QCOMPARE(expectedOK, it != dataSeries->cend());
373 373
374 374 // If the method doesn't return a end iterator, checks with expected value
375 375 if (expectedOK) {
376 376 QCOMPARE(expectedMax, it->x());
377 377 }
378 378 }
379 379
380 380 void TestDataSeries::testXAxisRange_data()
381 381 {
382 382 // ////////////// //
383 383 // Test structure //
384 384 // ////////////// //
385 385
386 386 // Data series to get x-axis range
387 387 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
388 388
389 389 // Min/max values
390 390 QTest::addColumn<double>("min");
391 391 QTest::addColumn<double>("max");
392 392
393 393 // Expected values
394 394 QTest::addColumn<QVector<double> >("expectedXAxisData");
395 395 QTest::addColumn<QVector<double> >("expectedValuesData");
396 396
397 397 // ////////// //
398 398 // Test cases //
399 399 // ////////// //
400 400
401 401 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
402 402 {100., 200., 300., 400., 500.})
403 403 << -1. << 3.2 << QVector<double>{1., 2., 3.}
404 404 << QVector<double>{100., 200., 300.};
405 405 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
406 406 {100., 200., 300., 400., 500.})
407 407 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
408 408 << QVector<double>{100., 200., 300., 400.};
409 409 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
410 410 {100., 200., 300., 400., 500.})
411 411 << 1. << 3.9 << QVector<double>{1., 2., 3.}
412 412 << QVector<double>{100., 200., 300.};
413 413 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
414 414 {100., 200., 300., 400., 500.})
415 415 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
416 416 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
417 417 {100., 200., 300., 400., 500.})
418 418 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
419 419 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
420 420 {100., 200., 300., 400., 500.})
421 421 << 2.1 << 6. << QVector<double>{3., 4., 5.}
422 422 << QVector<double>{300., 400., 500.};
423 423 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
424 424 {100., 200., 300., 400., 500.})
425 425 << 6. << 9. << QVector<double>{} << QVector<double>{};
426 426 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
427 427 {100., 200., 300., 400., 500.})
428 428 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
429 429 }
430 430
431 431 void TestDataSeries::testXAxisRange()
432 432 {
433 433 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
434 434 QFETCH(double, min);
435 435 QFETCH(double, max);
436 436
437 437 QFETCH(QVector<double>, expectedXAxisData);
438 438 QFETCH(QVector<double>, expectedValuesData);
439 439
440 440 auto bounds = dataSeries->xAxisRange(min, max);
441 441 QVERIFY(std::equal(bounds.first, bounds.second, expectedXAxisData.cbegin(),
442 442 expectedXAxisData.cend(),
443 443 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
444 444 QVERIFY(std::equal(
445 445 bounds.first, bounds.second, expectedValuesData.cbegin(), expectedValuesData.cend(),
446 446 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
447 447 }
448 448
449 449 void TestDataSeries::testValuesBoundsScalar_data()
450 450 {
451 451 testValuesBoundsStructure<ScalarSeries>();
452 452
453 453 // ////////// //
454 454 // Test cases //
455 455 // ////////// //
456 456 auto nan = std::numeric_limits<double>::quiet_NaN();
457 457
458 458 QTest::newRow("scalarBounds1")
459 459 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
460 460 << true << 100. << 500.;
461 461 QTest::newRow("scalarBounds2")
462 462 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
463 463 << true << 200. << 400.;
464 464 QTest::newRow("scalarBounds3")
465 465 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
466 466 << false << nan << nan;
467 467 QTest::newRow("scalarBounds4")
468 468 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
469 469 << false << nan << nan;
470 QTest::newRow("scalarBounds5")
471 << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100. << 100.;
470 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
471 << 100.;
472 472 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
473 473
474 474 // Tests with NaN values: NaN values are not included in min/max search
475 475 QTest::newRow("scalarBounds7")
476 476 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
477 477 << true << 200. << 400.;
478 478 QTest::newRow("scalarBounds8")
479 479 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
480 480 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
481 481 }
482 482
483 483 void TestDataSeries::testValuesBoundsScalar()
484 484 {
485 485 testValuesBounds<ScalarSeries>();
486 486 }
487 487
488 488 void TestDataSeries::testValuesBoundsVector_data()
489 489 {
490 490 testValuesBoundsStructure<VectorSeries>();
491 491
492 492 // ////////// //
493 493 // Test cases //
494 494 // ////////// //
495 495 auto nan = std::numeric_limits<double>::quiet_NaN();
496 496
497 497 QTest::newRow("vectorBounds1")
498 498 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
499 499 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
500 500 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
501 501 QTest::newRow("vectorBounds2")
502 502 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
503 503 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
504 504 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
505 505 QTest::newRow("vectorBounds3")
506 506 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
507 507 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
508 508 << 2. << 3. << true << 10. << 24.;
509 509
510 510 // Tests with NaN values: NaN values are not included in min/max search
511 511 QTest::newRow("vectorBounds4")
512 512 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
513 513 << nan << nan;
514 514 }
515 515
516 516 void TestDataSeries::testValuesBoundsVector()
517 517 {
518 518 testValuesBounds<VectorSeries>();
519 519 }
520 520
521 521 QTEST_MAIN(TestDataSeries)
522 522 #include "TestDataSeries.moc"
General Comments 0
You need to be logged in to leave comments. Login now