Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
line_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
37#include <gtest/gtest.h>
38#include <cmath>
39#include <line.H>
40
41using namespace Aleph;
42
43//============================================================================
44// Construction Tests
45//============================================================================
46
47class LineConstructorTest : public ::testing::Test {};
48
50{
51 LineEq line;
52
53 EXPECT_DOUBLE_EQ(line.y0, 0.0);
54 EXPECT_DOUBLE_EQ(line.m, 1.0);
55
56 // Default line is y = x
57 EXPECT_DOUBLE_EQ(line(0.0), 0.0);
58 EXPECT_DOUBLE_EQ(line(1.0), 1.0);
59 EXPECT_DOUBLE_EQ(line(5.0), 5.0);
60}
61
63{
64 LineEq line(5.0, 2.0); // y = 5 + 2x
65
66 EXPECT_DOUBLE_EQ(line.y_intercept(), 5.0);
67 EXPECT_DOUBLE_EQ(line.slope(), 2.0);
68
69 EXPECT_DOUBLE_EQ(line(0.0), 5.0);
70 EXPECT_DOUBLE_EQ(line(1.0), 7.0);
71 EXPECT_DOUBLE_EQ(line(3.0), 11.0);
72}
73
75{
76 // Line through (2, 4) with slope 3
77 // y = y0 + 3x, where y0 = 4 - 3*2 = -2
78 LineEq line(2.0, 4.0, 3.0);
79
80 EXPECT_DOUBLE_EQ(line.slope(), 3.0);
81 EXPECT_DOUBLE_EQ(line.y_intercept(), -2.0);
82
83 // Verify it passes through (2, 4)
84 EXPECT_DOUBLE_EQ(line(2.0), 4.0);
85}
86
88{
89 // Line through (0, 0) and (2, 6) -> slope = 3
90 LineEq line(0.0, 0.0, 2.0, 6.0);
91
92 EXPECT_DOUBLE_EQ(line.slope(), 3.0);
93 EXPECT_DOUBLE_EQ(line.y_intercept(), 0.0);
94
95 // Verify it passes through both points
96 EXPECT_DOUBLE_EQ(line(0.0), 0.0);
97 EXPECT_DOUBLE_EQ(line(2.0), 6.0);
98}
99
101{
102 // Line through (0, 4) and (2, 0) -> slope = -2
103 LineEq line(0.0, 4.0, 2.0, 0.0);
104
105 EXPECT_DOUBLE_EQ(line.slope(), -2.0);
106 EXPECT_DOUBLE_EQ(line.y_intercept(), 4.0);
107}
108
110{
111 // Horizontal line through (0, 5) and (10, 5)
112 LineEq line(0.0, 5.0, 10.0, 5.0);
113
114 EXPECT_DOUBLE_EQ(line.slope(), 0.0);
115 EXPECT_DOUBLE_EQ(line.y_intercept(), 5.0);
117}
118
120{
121 // Vertical line (same x) should throw
122 EXPECT_THROW(LineEq(3.0, 0.0, 3.0, 10.0), std::domain_error);
123}
124
126{
127 // Same point should throw
128 EXPECT_THROW(LineEq(3.0, 5.0, 3.0, 5.0), std::domain_error);
129}
130
131//============================================================================
132// Evaluation Tests
133//============================================================================
134
135class LineEvaluationTest : public ::testing::Test {};
136
138{
139 LineEq line(5.0, 2.0); // y = 5 + 2x
140 EXPECT_DOUBLE_EQ(line(0.0), 5.0);
141}
142
144{
145 LineEq line(1.0, 3.0); // y = 1 + 3x
146 EXPECT_DOUBLE_EQ(line(2.0), 7.0);
147 EXPECT_DOUBLE_EQ(line(10.0), 31.0);
148}
149
151{
152 LineEq line(1.0, 3.0); // y = 1 + 3x
153 EXPECT_DOUBLE_EQ(line(-2.0), -5.0);
154 EXPECT_DOUBLE_EQ(line(-1.0), -2.0);
155}
156
158{
159 LineEq line(7.0, 0.0); // y = 7
160 EXPECT_DOUBLE_EQ(line(-100.0), 7.0);
161 EXPECT_DOUBLE_EQ(line(0.0), 7.0);
162 EXPECT_DOUBLE_EQ(line(100.0), 7.0);
163}
164
166{
167 LineEq line(0.0, 2.0); // y = 2x -> x = y/2
168 EXPECT_DOUBLE_EQ(line.x_at(4.0), 2.0);
169 EXPECT_DOUBLE_EQ(line.x_at(0.0), 0.0);
170 EXPECT_DOUBLE_EQ(line.x_at(-6.0), -3.0);
171}
172
174{
175 LineEq line(5.0, 0.0); // y = 5 (horizontal)
176 EXPECT_THROW((void)line.x_at(5.0), std::domain_error);
177}
178
179//============================================================================
180// Property Tests
181//============================================================================
182
183class LinePropertyTest : public ::testing::Test {};
184
186{
187 LineEq line(3.0, 2.5);
188 EXPECT_DOUBLE_EQ(line.slope(), 2.5);
189}
190
192{
193 LineEq line(3.0, 2.5);
194 EXPECT_DOUBLE_EQ(line.y_intercept(), 3.0);
195}
196
198{
199 LineEq horizontal(5.0, 0.0);
200 LineEq nearly_horizontal(5.0, 1e-15);
201 LineEq not_horizontal(5.0, 0.1);
202
203 EXPECT_TRUE(horizontal.is_horizontal());
204 EXPECT_TRUE(nearly_horizontal.is_horizontal());
205 EXPECT_FALSE(not_horizontal.is_horizontal());
206}
207
209{
210 LineEq l1(0.0, 2.0); // y = 2x
211 LineEq l2(5.0, 2.0); // y = 5 + 2x
212 LineEq l3(0.0, 3.0); // y = 3x
213
217}
218
220{
221 LineEq l1(0.0, 2.0); // y = 2x
222 LineEq l2(0.0, -0.5); // y = -0.5x (perpendicular: 2 * -0.5 = -1)
223 LineEq l3(0.0, 1.0); // y = x (not perpendicular)
224
228}
229
230//============================================================================
231// Intersection Tests
232//============================================================================
233
234class LineIntersectionTest : public ::testing::Test {};
235
237{
238 LineEq l1(0.0, 1.0); // y = x
239 LineEq l2(2.0, -1.0); // y = 2 - x
240
241 // Intersection: x = 2 - x -> 2x = 2 -> x = 1, y = 1
242 auto [x, y] = l1.intersection(l2);
243
244 EXPECT_DOUBLE_EQ(x, 1.0);
245 EXPECT_DOUBLE_EQ(y, 1.0);
246}
247
249{
250 LineEq l1(0.0, 1.0); // y = x
251 LineEq l2(0.0, -1.0); // y = -x
252
253 auto [x, y] = l1.intersection(l2);
254
255 EXPECT_DOUBLE_EQ(x, 0.0);
256 EXPECT_DOUBLE_EQ(y, 0.0);
257}
258
260{
261 LineEq l1(2.0, 1.0); // y = 2 + x
262 LineEq l2(4.0, -1.0); // y = 4 - x
263
264 // 2 + x = 4 - x -> 2x = 2 -> x = 1, y = 3
265 auto [x, y] = l1.intersection(l2);
266
267 EXPECT_DOUBLE_EQ(x, 1.0);
268 EXPECT_DOUBLE_EQ(y, 3.0);
269}
270
272{
273 LineEq diagonal(0.0, 2.0); // y = 2x
274 LineEq horizontal(4.0, 0.0); // y = 4
275
276 // 4 = 2x -> x = 2
277 auto [x, y] = diagonal.intersection(horizontal);
278
279 EXPECT_DOUBLE_EQ(x, 2.0);
280 EXPECT_DOUBLE_EQ(y, 4.0);
281}
282
284{
285 LineEq l1(0.0, 2.0); // y = 2x
286 LineEq l2(5.0, 2.0); // y = 5 + 2x (parallel)
287
288 EXPECT_THROW((void)l1.intersection(l2), std::domain_error);
289}
290
292{
293 LineEq l1(3.0, 2.0);
294 LineEq l2(3.0, 2.0); // Same line
295
296 EXPECT_THROW((void)l1.intersection(l2), std::domain_error);
297}
298
299//============================================================================
300// Perpendicular Through Point Tests
301//============================================================================
302
303class LinePerpendicularTest : public ::testing::Test {};
304
306{
307 LineEq l1(0.0, 2.0); // y = 2x
308
309 // Perpendicular through (1, 2)
310 LineEq perp = l1.perpendicular_through(1.0, 2.0);
311
312 EXPECT_DOUBLE_EQ(perp.slope(), -0.5); // -1/2
314 EXPECT_DOUBLE_EQ(perp(1.0), 2.0); // Passes through (1, 2)
315}
316
318{
319 LineEq l1(0.0, 1.0); // y = x
320
321 LineEq perp = l1.perpendicular_through(0.0, 0.0);
322
323 EXPECT_DOUBLE_EQ(perp.slope(), -1.0);
324 EXPECT_DOUBLE_EQ(perp.y_intercept(), 0.0);
325}
326
328{
329 LineEq horizontal(5.0, 0.0); // y = 5
330
331 // Perpendicular would be vertical (undefined)
332 EXPECT_THROW((void)horizontal.perpendicular_through(1.0, 5.0), std::domain_error);
333}
334
335//============================================================================
336// Distance Tests
337//============================================================================
338
339class LineDistanceTest : public ::testing::Test {};
340
342{
343 LineEq line(0.0, 1.0); // y = x
344
345 // Point (3, 3) is on the line
346 EXPECT_NEAR(line.distance_to(3.0, 3.0), 0.0, 1e-10);
347}
348
350{
351 LineEq line(5.0, 0.0); // y = 5
352
353 // Distance from (0, 10) to y = 5 is 5
354 EXPECT_DOUBLE_EQ(line.distance_to(0.0, 10.0), 5.0);
355
356 // Distance from (100, 0) to y = 5 is 5
357 EXPECT_DOUBLE_EQ(line.distance_to(100.0, 0.0), 5.0);
358}
359
361{
362 LineEq line(0.0, 1.0); // y = x
363
364 // Distance from (0, 1) to y = x
365 // Formula: |y - mx - b| / sqrt(1 + m^2) = |1 - 0 - 0| / sqrt(2) = 1/sqrt(2)
366 double expected = 1.0 / std::sqrt(2.0);
367 EXPECT_NEAR(line.distance_to(0.0, 1.0), expected, 1e-10);
368}
369
370//============================================================================
371// Contains Point Tests
372//============================================================================
373
374class LineContainsTest : public ::testing::Test {};
375
377{
378 LineEq line(0.0, 2.0); // y = 2x
379
380 EXPECT_TRUE(line.contains_point(0.0, 0.0));
381 EXPECT_TRUE(line.contains_point(1.0, 2.0));
382 EXPECT_TRUE(line.contains_point(-3.0, -6.0));
383}
384
386{
387 LineEq line(0.0, 2.0); // y = 2x
388
389 EXPECT_FALSE(line.contains_point(1.0, 1.0)); // y should be 2
390 EXPECT_FALSE(line.contains_point(0.0, 1.0)); // y should be 0
391}
392
394{
395 LineEq line(0.0, 1.0); // y = x
396
397 // Point (1, 1.0000001) is almost on the line
398 EXPECT_TRUE(line.contains_point(1.0, 1.0 + 1e-11));
399 EXPECT_FALSE(line.contains_point(1.0, 1.0 + 1e-5)); // Too far
400}
401
402//============================================================================
403// Equality Tests
404//============================================================================
405
406class LineEqualityTest : public ::testing::Test {};
407
409{
410 LineEq l1(3.0, 2.0);
411 LineEq l2(3.0, 2.0);
412
413 EXPECT_TRUE(l1 == l2);
414 EXPECT_FALSE(l1 != l2);
415}
416
418{
419 LineEq l1(3.0, 2.0);
420 LineEq l2(3.0 + 1e-12, 2.0 + 1e-12);
421
422 EXPECT_TRUE(l1 == l2); // Within epsilon
423}
424
426{
427 LineEq l1(3.0, 2.0);
428 LineEq l2(3.0, 2.5);
429
430 EXPECT_FALSE(l1 == l2);
431 EXPECT_TRUE(l1 != l2);
432}
433
435{
436 LineEq l1(3.0, 2.0);
437 LineEq l2(4.0, 2.0);
438
439 EXPECT_FALSE(l1 == l2);
440 EXPECT_TRUE(l1 != l2);
441}
442
443//============================================================================
444// String Conversion Tests
445//============================================================================
446
447class LineStringTest : public ::testing::Test {};
448
450{
451 LineEq line(3.0, 2.0);
452 std::string str = line.to_string();
453
454 EXPECT_TRUE(str.find("y = ") != std::string::npos);
455 EXPECT_TRUE(str.find("3") != std::string::npos);
456 EXPECT_TRUE(str.find("2") != std::string::npos);
457}
458
460{
461 LineEq line(1.5, 2.5);
462 std::ostringstream ss;
463 ss << line;
464
465 EXPECT_TRUE(ss.str().find("1.5") != std::string::npos);
466 EXPECT_TRUE(ss.str().find("2.5") != std::string::npos);
467}
468
469//============================================================================
470// Edge Cases and Stress Tests
471//============================================================================
472
473class LineEdgeCaseTest : public ::testing::Test {};
474
476{
477 LineEq line(0.0, 1e10); // Very steep line
478
479 EXPECT_DOUBLE_EQ(line(1.0), 1e10);
480 EXPECT_DOUBLE_EQ(line(-1.0), -1e10);
481}
482
484{
485 LineEq line(5.0, 1e-10); // Nearly horizontal
486
487 EXPECT_NEAR(line(1.0), 5.0, 1e-9);
488 EXPECT_TRUE(line.is_horizontal(1e-8));
489}
490
492{
493 LineEq line(-100.0, 1.0);
494
495 EXPECT_DOUBLE_EQ(line(0.0), -100.0);
496 EXPECT_DOUBLE_EQ(line(100.0), 0.0);
497}
498
500{
501 LineEq line(10.0, -2.0); // y = 10 - 2x
502
503 EXPECT_DOUBLE_EQ(line(0.0), 10.0);
504 EXPECT_DOUBLE_EQ(line(5.0), 0.0);
505 EXPECT_DOUBLE_EQ(line(10.0), -10.0);
506}
507
509{
510 // Points given with x2 < x1: (5, 10) and (0, 0)
511 // Slope = (0 - 10) / (0 - 5) = -10 / -5 = 2
512 LineEq line(5.0, 10.0, 0.0, 0.0);
513
514 EXPECT_DOUBLE_EQ(line.slope(), 2.0);
515 EXPECT_DOUBLE_EQ(line.y_intercept(), 0.0); // Passes through origin
516}
517
518//============================================================================
519// Constexpr Tests
520//============================================================================
521
522class LineConstexprTest : public ::testing::Test {};
523
525{
526 constexpr LineEq line;
527 static_assert(line.y0 == 0.0, "Default y0 should be 0");
528 static_assert(line.m == 1.0, "Default m should be 1");
529}
530
532{
533 constexpr LineEq line(5.0, 2.0);
534 static_assert(line.y0 == 5.0, "y0 should be 5");
535 static_assert(line.m == 2.0, "m should be 2");
536}
537
539{
540 constexpr LineEq line(1.0, 2.0);
541 constexpr double y = line(3.0);
542 static_assert(y == 7.0, "y at x=3 should be 7");
543}
544
545//============================================================================
546// Type Traits Tests
547//============================================================================
548
550{
551 // Verify [[nodiscard]] functions return expected types
552 LineEq line(0.0, 1.0);
553
554 EXPECT_TRUE((std::is_same<decltype(line.slope()), double>::value));
555 EXPECT_TRUE((std::is_same<decltype(line.y_intercept()), double>::value));
556 EXPECT_TRUE((std::is_same<decltype(line.is_horizontal()), bool>::value));
557 EXPECT_TRUE((std::is_same<decltype(line.to_string()), std::string>::value));
558}
559
560//============================================================================
561// Integration Tests
562//============================================================================
563
564class LineIntegrationTest : public ::testing::Test {};
565
567{
568 // Create three lines forming a triangle
569 LineEq l1(0.0, 1.0); // y = x
570 LineEq l2(0.0, -1.0); // y = -x
571 LineEq l3(2.0, 0.0); // y = 2
572
573 auto [x1, y1] = l1.intersection(l2); // (0, 0)
574 auto [x2, y2] = l1.intersection(l3); // (2, 2)
575 auto [x3, y3] = l2.intersection(l3); // (-2, 2)
576
577 EXPECT_DOUBLE_EQ(x1, 0.0);
578 EXPECT_DOUBLE_EQ(y1, 0.0);
579 EXPECT_DOUBLE_EQ(x2, 2.0);
580 EXPECT_DOUBLE_EQ(y2, 2.0);
581 EXPECT_DOUBLE_EQ(x3, -2.0);
582 EXPECT_DOUBLE_EQ(y3, 2.0);
583}
584
586{
587 // Create line through (0, 0) and (4, 8) -> y = 2x
588 LineEq l1(0.0, 0.0, 4.0, 8.0);
589
590 // Create line through (0, 4) and (4, 0) -> y = 4 - x
591 LineEq l2(0.0, 4.0, 4.0, 0.0);
592
593 // Find intersection
594 auto [x, y] = l1.intersection(l2);
595
596 // 2x = 4 - x -> 3x = 4 -> x = 4/3
597 EXPECT_NEAR(x, 4.0/3.0, 1e-10);
598 EXPECT_NEAR(y, 8.0/3.0, 1e-10);
599}
600
602{
603 // Find perpendicular bisector of segment from (0, 0) to (4, 4)
604 // Midpoint: (2, 2)
605 // Original line: y = x, slope = 1
606 // Perpendicular slope: -1
607
608 LineEq original(0.0, 0.0, 4.0, 4.0); // y = x
609 LineEq perp = original.perpendicular_through(2.0, 2.0);
610
611 EXPECT_DOUBLE_EQ(perp.slope(), -1.0);
612 EXPECT_DOUBLE_EQ(perp(2.0), 2.0); // Passes through midpoint
613
614 // Verify it's equidistant from both endpoints
615 EXPECT_NEAR(perp.distance_to(0.0, 0.0), perp.distance_to(4.0, 4.0), 1e-10);
616}
617
618//============================================================================
619// Main
620//============================================================================
621
622int main(int argc, char **argv)
623{
624 ::testing::InitGoogleTest(&argc, argv);
625 return RUN_ALL_TESTS();
626}
int main()
#define TEST(name)
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4103
2D infinite line representation using slope-intercept form.
TEST_F(LineConstructorTest, DefaultConstructor)
Definition line_test.cc:49
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
2D infinite line in slope-intercept form.
Definition line.H:121
constexpr bool is_horizontal(double epsilon=LINE_EPSILON) const noexcept
Check if the line is horizontal (slope = 0).
Definition line.H:233
double x_at(double y) const
Compute x-coordinate for a given y-coordinate.
Definition line.H:277
double distance_to(double px, double py) const noexcept
Compute distance from a point to this line.
Definition line.H:345
double y0
Y-intercept (where line crosses y-axis)
Definition line.H:122
constexpr bool is_parallel_to(const LineEq &l, double epsilon=LINE_EPSILON) const noexcept
Check if this line is parallel to another.
Definition line.H:247
LineEq perpendicular_through(double px, double py) const
Compute perpendicular line through a point.
Definition line.H:327
constexpr double y_intercept() const noexcept
Get the y-intercept of the line.
Definition line.H:226
std::string to_string() const
Convert line to string representation.
Definition line.H:396
double m
Slope (dy/dx)
Definition line.H:123
std::pair< double, double > intersection(const LineEq &l, double epsilon=LINE_EPSILON) const
Compute intersection point with another line.
Definition line.H:305
bool contains_point(double px, double py, double epsilon=LINE_EPSILON) const noexcept
Check if a point lies on this line.
Definition line.H:359
constexpr bool is_perpendicular_to(const LineEq &l, double epsilon=LINE_EPSILON) const noexcept
Check if this line is perpendicular to another.
Definition line.H:262
constexpr double slope() const noexcept
Get the slope of the line.
Definition line.H:220