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
61
73
75{
76 // Line through (2, 4) with slope 3
77 // y = y0 + 3x, where y0 = 4 - 3*2 = -2
79
82
83 // Verify it passes through (2, 4)
85}
86
88{
89 // Line through (0, 0) and (2, 6) -> slope = 3
91
94
95 // Verify it passes through both points
98}
99
101{
102 // Line through (0, 4) and (2, 0) -> slope = -2
104
107}
108
110{
111 // Horizontal line through (0, 5) and (10, 5)
113
117}
118
120{
121 // Vertical line (same x) should throw
122 EXPECT_THROW(LineEq(Geom_Number(3), Geom_Number(0), Geom_Number(3), Geom_Number(10)), std::domain_error);
123}
124
126{
127 // Same point should throw
128 EXPECT_THROW(LineEq(Geom_Number(3), Geom_Number(5), Geom_Number(3), Geom_Number(5)), std::domain_error);
129}
130
131//============================================================================
132// Evaluation Tests
133//============================================================================
134
135class LineEvaluationTest : public ::testing::Test {};
136
142
149
156
164
172
174{
175 LineEq line(Geom_Number(5), Geom_Number(0)); // y = 5 (horizontal)
176 EXPECT_THROW((void)line.x_at(Geom_Number(5)), std::domain_error);
177}
178
179//============================================================================
180// Property Tests
181//============================================================================
182
183class LinePropertyTest : public ::testing::Test {};
184
186{
187 LineEq line(Geom_Number(3), Geom_Number(5, 2)); // 2.5 = 5/2
189}
190
196
198{
199 LineEq horizontal(Geom_Number(5), Geom_Number(0));
200 LineEq nearly_horizontal(Geom_Number(5), Geom_Number(1e-15)); // 1e-15
201 LineEq not_horizontal(Geom_Number(5), Geom_Number(1, 10)); // 0.1
202
203 EXPECT_TRUE(horizontal.is_horizontal());
204 EXPECT_TRUE(nearly_horizontal.is_horizontal(Geom_Number(1e-14))); // 1e-14
205 EXPECT_FALSE(not_horizontal.is_horizontal());
206}
207
209{
210 LineEq l1(Geom_Number(0), Geom_Number(2)); // y = 2x
211 LineEq l2(Geom_Number(5), Geom_Number(2)); // y = 5 + 2x
212 LineEq l3(Geom_Number(0), Geom_Number(3)); // y = 3x
213
214 EXPECT_TRUE(l1.is_parallel_to(l2));
215 EXPECT_TRUE(l2.is_parallel_to(l1));
216 EXPECT_FALSE(l1.is_parallel_to(l3));
217}
218
220{
221 LineEq l1(Geom_Number(0), Geom_Number(2)); // y = 2x
222 LineEq l2(Geom_Number(0), Geom_Number(-1, 2)); // y = -0.5x (perpendicular: 2 * -0.5 = -1)
223 LineEq l3(Geom_Number(0), Geom_Number(1)); // y = x (not perpendicular)
224
225 EXPECT_TRUE(l1.is_perpendicular_to(l2));
226 EXPECT_TRUE(l2.is_perpendicular_to(l1));
227 EXPECT_FALSE(l1.is_perpendicular_to(l3));
228}
229
230//============================================================================
231// Intersection Tests
232//============================================================================
233
234class LineIntersectionTest : public ::testing::Test {};
235
237{
238 LineEq l1(Geom_Number(0), Geom_Number(1)); // y = x
239 LineEq l2(Geom_Number(2), Geom_Number(-1)); // y = 2 - x
240
241 // Intersection: x = 2 - x -> 2x = 2 -> x = 1, y = 1
242 auto [x, y] = l1.intersection(l2);
243
246}
247
249{
250 LineEq l1(Geom_Number(0), Geom_Number(1)); // y = x
251 LineEq l2(Geom_Number(0), Geom_Number(-1)); // y = -x
252
253 auto [x, y] = l1.intersection(l2);
254
257}
258
260{
261 LineEq l1(Geom_Number(2), Geom_Number(1)); // y = 2 + x
262 LineEq l2(Geom_Number(4), Geom_Number(-1)); // y = 4 - x
263
264 // 2 + x = 4 - x -> 2x = 2 -> x = 1, y = 3
265 auto [x, y] = l1.intersection(l2);
266
269}
270
272{
273 LineEq diagonal(Geom_Number(0), Geom_Number(2)); // y = 2x
274 LineEq horizontal(Geom_Number(4), Geom_Number(0)); // y = 4
275
276 // 4 = 2x -> x = 2
277 auto [x, y] = diagonal.intersection(horizontal);
278
281}
282
284{
285 LineEq l1(Geom_Number(0), Geom_Number(2)); // y = 2x
286 LineEq l2(Geom_Number(5), Geom_Number(2)); // y = 5 + 2x (parallel)
287
288 EXPECT_THROW((void)l1.intersection(l2), std::domain_error);
289}
290
292{
294 LineEq l2(Geom_Number(3), Geom_Number(2)); // 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(Geom_Number(0), Geom_Number(2)); // y = 2x
308
309 // Perpendicular through (1, 2)
310 LineEq perp = l1.perpendicular_through(Geom_Number(1), Geom_Number(2));
311
312 EXPECT_DOUBLE_EQ(geom_number_to_double(perp.slope()), -0.5); // -1/2
313 EXPECT_TRUE(l1.is_perpendicular_to(perp));
314 EXPECT_DOUBLE_EQ(geom_number_to_double(perp(Geom_Number(1))), 2.0); // Passes through (1, 2)
315}
316
318{
319 LineEq l1(Geom_Number(0), Geom_Number(1)); // y = x
320
321 LineEq perp = l1.perpendicular_through(Geom_Number(0), Geom_Number(0));
322
324 EXPECT_DOUBLE_EQ(geom_number_to_double(perp.y_intercept()), 0.0);
325}
326
328{
329 LineEq horizontal(Geom_Number(5), Geom_Number(0)); // y = 5
330
331 // Perpendicular would be vertical (undefined)
332 EXPECT_THROW((void)horizontal.perpendicular_through(Geom_Number(1), Geom_Number(5)), std::domain_error);
333}
334
335//============================================================================
336// Distance Tests
337//============================================================================
338
339class LineDistanceTest : public ::testing::Test {};
340
342{
343 LineEq line(Geom_Number(0), Geom_Number(1)); // y = x
344
345 // Point (3, 3) is on the line
347}
348
350{
351 LineEq line(Geom_Number(5), Geom_Number(0)); // y = 5
352
353 // Distance from (0, 10) to y = 5 is 5
355
356 // Distance from (100, 0) to y = 5 is 5
358}
359
361{
362 LineEq line(Geom_Number(0), Geom_Number(1)); // 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);
368}
369
370//============================================================================
371// Contains Point Tests
372//============================================================================
373
374class LineContainsTest : public ::testing::Test {};
375
384
386{
387 LineEq line(Geom_Number(0), Geom_Number(2)); // y = 2x
388
389 EXPECT_FALSE(line.contains_point(Geom_Number(1), Geom_Number(1))); // y should be 2
390 EXPECT_FALSE(line.contains_point(Geom_Number(0), Geom_Number(1))); // y should be 0
391}
392
394{
395 LineEq line(Geom_Number(0), Geom_Number(1)); // y = x
396
397 // Point (1, 1.0000001) is almost on the line
398 EXPECT_TRUE(line.contains_point(Geom_Number(1), Geom_Number(1.00000000001), Geom_Number(1e-10)));
399 EXPECT_FALSE(line.contains_point(Geom_Number(1), Geom_Number(100001, 100000), Geom_Number(1e-10))); // Too far
400}
401
402//============================================================================
403// Equality Tests
404//============================================================================
405
406class LineEqualityTest : public ::testing::Test {};
407
416
418{
420 LineEq l2(Geom_Number(3.000000000001), Geom_Number(2.000000000001));
421
422 EXPECT_TRUE(l1.approx_equal(l2, Geom_Number(1e-10))); // Within epsilon
423}
424
426{
428 LineEq l2(Geom_Number(3), Geom_Number(5, 2)); // 2.5 = 5/2
429
430 EXPECT_FALSE(l1 == l2);
431 EXPECT_TRUE(l1 != l2);
432}
433
442
443//============================================================================
444// String Conversion Tests
445//============================================================================
446
447class LineStringTest : public ::testing::Test {};
448
450{
451 LineEq line(Geom_Number(3), Geom_Number(2));
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(Geom_Number(3, 2), Geom_Number(5, 2)); // 1.5 = 3/2, 2.5 = 5/2
462 std::ostringstream ss;
463 ss << line;
464
465 EXPECT_TRUE(ss.str().find("1.5") != std::string::npos || ss.str().find("3/2") != std::string::npos);
466 EXPECT_TRUE(ss.str().find("2.5") != std::string::npos || ss.str().find("5/2") != std::string::npos);
467}
468
469//============================================================================
470// Edge Cases and Stress Tests
471//============================================================================
472
473class LineEdgeCaseTest : public ::testing::Test {};
474
482
484{
485 LineEq line(Geom_Number(5), Geom_Number(1e-10)); // Nearly horizontal (1e-10)
486
487 EXPECT_NEAR(geom_number_to_double(line(Geom_Number(1))), 5.0, 1e-9);
488 EXPECT_TRUE(line.is_horizontal(Geom_Number(1e-8))); // 1e-8
489}
490
498
507
509{
510 // Points given with x2 < x1: (5, 10) and (0, 0)
511 // Slope = (0 - 10) / (0 - 5) = -10 / -5 = 2
513
515 EXPECT_DOUBLE_EQ(geom_number_to_double(line.y_intercept()), 0.0); // Passes through origin
516}
517
518//============================================================================
519// Constexpr Tests - Note: Geom_Number (mpq_class) is not constexpr
520//============================================================================
521
522class LineConstexprTest : public ::testing::Test {};
523
530
537
544
545//============================================================================
546// Type Traits Tests
547//============================================================================
548
550{
551 // Verify [[nodiscard]] functions return expected types
552 LineEq line(Geom_Number(0), Geom_Number(1));
553
554 EXPECT_TRUE((std::is_same<decltype(line.slope()), const Geom_Number &>::value));
555 EXPECT_TRUE((std::is_same<decltype(line.y_intercept()), const Geom_Number &>::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(Geom_Number(0), Geom_Number(1)); // y = x
570 LineEq l2(Geom_Number(0), Geom_Number(-1)); // y = -x
571 LineEq l3(Geom_Number(2), Geom_Number(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
583}
584
586{
587 // Create line through (0, 0) and (4, 8) -> y = 2x
589
590 // Create line through (0, 4) and (4, 0) -> y = 4 - x
592
593 // Find intersection
594 auto [x, y] = l1.intersection(l2);
595
596 // 2x = 4 - x -> 3x = 4 -> x = 4/3
597 EXPECT_NEAR(geom_number_to_double(x), 4.0/3.0, 1e-10);
598 EXPECT_NEAR(geom_number_to_double(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
610
612 EXPECT_DOUBLE_EQ(geom_number_to_double(perp(Geom_Number(2))), 2.0); // Passes through midpoint
613
614 // Verify it's equidistant from both endpoints
616 geom_number_to_double(perp.distance_to(Geom_Number(4), Geom_Number(4))), 1e-10);
617}
618
619//============================================================================
620// Main
621//============================================================================
622
623int main(int argc, char **argv)
624{
625 ::testing::InitGoogleTest(&argc, argv);
626 return RUN_ALL_TESTS();
627}
#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
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
double geom_number_to_double(const Geom_Number &n)
Converts a Geom_Number to its double precision representation.
Definition point.H:122
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:115
2D infinite line in slope-intercept form.
Definition line.H:126
std::pair< Geom_Number, Geom_Number > intersection(const LineEq &l, const Geom_Number &epsilon=LINE_EPSILON) const
Compute the intersection point with another line.
Definition line.H:343
LineEq perpendicular_through(const Geom_Number &px, const Geom_Number &py) const
Compute perpendicular line through a point.
Definition line.H:365
bool is_horizontal(const Geom_Number &epsilon=LINE_EPSILON) const noexcept
Check if the line is horizontal (slope = 0).
Definition line.H:271
const Geom_Number & y_intercept() const noexcept
Get the y-intercept of the line.
Definition line.H:264
Geom_Number x_at(const Geom_Number &y) const
Compute x-coordinate for a given y-coordinate.
Definition line.H:315
std::string to_string() const
Convert line to a string representation.
Definition line.H:507
Geom_Number distance_to(const Geom_Number &px, const Geom_Number &py) const noexcept
Compute distance from a point to this line.
Definition line.H:383
const Geom_Number & slope() const noexcept
Get the slope of the line.
Definition line.H:258
Geom_Number y0
Y-intercept (where line crosses y-axis)
Definition line.H:127
Geom_Number m
Slope (dy/dx)
Definition line.H:128
bool contains_point(const Geom_Number &px, const Geom_Number &py, const Geom_Number &epsilon=LINE_EPSILON) const noexcept
Check if a point lies on this line.
Definition line.H:399
DynList< char > l3
DynList< int > l1
DynList< int > l2