ruby-geometry 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/comparison_with_precision.rb +11 -0
- data/lib/geometry.rb +16 -0
- data/lib/line.rb +81 -0
- data/lib/point.rb +11 -0
- data/lib/polygon.rb +13 -0
- data/lib/segment.rb +105 -0
- data/lib/vector.rb +50 -0
- data/test/geometry/distance_test.rb +14 -0
- data/test/line/angle_to_test.rb +25 -0
- data/test/line/horizontal_test.rb +18 -0
- data/test/line/initialize_test.rb +20 -0
- data/test/line/intersect_x_test.rb +45 -0
- data/test/line/parallel_to_test.rb +45 -0
- data/test/line/slope_test.rb +53 -0
- data/test/line/vertical_test.rb +18 -0
- data/test/line/x_intercept_test.rb +44 -0
- data/test/line/y_intercept_test.rb +44 -0
- data/test/point/equals_test.rb +15 -0
- data/test/point/initialize_test.rb +20 -0
- data/test/polygon/edges_test.rb +56 -0
- data/test/segment/bounds_test.rb +26 -0
- data/test/segment/contains_point_test.rb +61 -0
- data/test/segment/initialize_test.rb +20 -0
- data/test/segment/intersection_point_with_test.rb +54 -0
- data/test/segment/intersects_with_test.rb +98 -0
- data/test/segment/length_test.rb +15 -0
- data/test/segment/overlaps_test.rb +46 -0
- data/test/segment/parallel_to_test.rb +27 -0
- data/test/segment/to_vector_test.rb +19 -0
- data/test/vector/arithmetics_test.rb +22 -0
- data/test/vector/collinear_with_test.rb +27 -0
- data/test/vector/cross_product_test.rb +22 -0
- data/test/vector/equals_test.rb +15 -0
- data/test/vector/modulus_test.rb +15 -0
- data/test/vector/scalar_product_test.rb +18 -0
- metadata +125 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class SlopeTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@inf = 1.0/0.0
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_vertical
|
12
|
+
[-1, 0, 1].each do |x|
|
13
|
+
line = Line.new(Point.new(x, 0), Point.new(x, 1))
|
14
|
+
assert_equal @inf, line.slope
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_vertical_negative
|
19
|
+
[-1, 0, 1].each do |x|
|
20
|
+
line = Line.new(Point.new(x, 0), Point.new(x, -1))
|
21
|
+
assert_equal -@inf, line.slope
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_horizontal
|
26
|
+
[-1, 0, 1].each do |y|
|
27
|
+
line = Line.new(Point.new(0, y), Point.new(1, y))
|
28
|
+
assert_equal 0, line.slope
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_horizontal_negative
|
33
|
+
[-1, 0, 1].each do |y|
|
34
|
+
line = Line.new(Point.new(0, y), Point.new(-1, y))
|
35
|
+
assert_equal 0, line.slope
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_full_circle
|
40
|
+
delta = 0.000001
|
41
|
+
increment = Math::PI/10
|
42
|
+
divisions = 2*Math::PI/increment
|
43
|
+
radius = 1
|
44
|
+
(0..divisions).each do |d|
|
45
|
+
angle = d*increment
|
46
|
+
y = radius*Math::sin(angle)
|
47
|
+
x = radius*Math::cos(angle)
|
48
|
+
line = Line.new(Point.new(0, 0), Point.new(x, y))
|
49
|
+
|
50
|
+
assert_in_delta Math::tan(angle), line.slope, delta
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class VerticalTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_vertical
|
8
|
+
x, y = 0, 0
|
9
|
+
l1 = Line.new_by_arrays([x, y], [x, y+1])
|
10
|
+
assert l1.vertical?
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_not_vertical
|
14
|
+
x, y = 0, 0
|
15
|
+
l1 = Line.new_by_arrays([x, y], [x+1, y+1])
|
16
|
+
assert ! l1.vertical?
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class XInterceptTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@inf = 1.0/0.0
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_anchored_at_x_axis
|
12
|
+
[-1, 0, 1].each do |xi|
|
13
|
+
line = Line.new(Point.new(xi, 0), Point.new(1, 1))
|
14
|
+
assert_equal xi, line.x_intercept
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_floating
|
19
|
+
[-1, 1].each do |slope|
|
20
|
+
[-2, 0, 2].each do |yo|
|
21
|
+
[-1, 0, 1].each do |xi|
|
22
|
+
point1 = Point.new(xi+yo/slope, yo)
|
23
|
+
point2 = Point.new(xi+(yo+1)/slope, yo+1)
|
24
|
+
line = Line.new(point1, point2)
|
25
|
+
assert_equal xi, line.x_intercept
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_verticals
|
32
|
+
[-1, 0, 1].each do |xi|
|
33
|
+
line = Line.new(Point.new(xi, 1), Point.new(xi, 2))
|
34
|
+
assert_equal xi, line.x_intercept
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_horizontals
|
39
|
+
[-1, 0, 1].each do |yo|
|
40
|
+
line = Line.new(Point.new(0, yo), Point.new(1, yo))
|
41
|
+
assert line.x_intercept.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class YInterceptTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@inf = 1.0/0.0
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_anchored_at_y_axis
|
12
|
+
[-1, 0, 1].each do |yi|
|
13
|
+
line = Line.new(Point.new(0, yi), Point.new(1, 1))
|
14
|
+
assert_equal yi, line.y_intercept
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_floating
|
19
|
+
[-1, 1].each do |slope|
|
20
|
+
[-2, 0, 2].each do |xo|
|
21
|
+
[-1, 0, 1].each do |yi|
|
22
|
+
point1 = Point.new(xo, yi+slope*xo)
|
23
|
+
point2 = Point.new(xo+1, yi+slope*(xo+1))
|
24
|
+
line = Line.new(point1, point2)
|
25
|
+
assert_equal yi, line.y_intercept
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_horizontals
|
32
|
+
[-1, 0, 1].each do |yi|
|
33
|
+
line = Line.new(Point.new(1, yi), Point.new(2, yi))
|
34
|
+
assert_equal yi, line.y_intercept
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_verticals
|
39
|
+
[-1, 0, 1].each do |xo|
|
40
|
+
line = Line.new(Point.new(xo, 0), Point.new(xo, 1))
|
41
|
+
assert line.y_intercept.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class EqualsTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_equal
|
8
|
+
assert_equal Point.new(1, 3), Point.new(1, 3)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_not_equal
|
12
|
+
assert_not_equal Point.new(1, 3), Point.new(1, 2)
|
13
|
+
assert_not_equal Point.new(1, 2), Point.new(0, 2)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class InitializeTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_initialize_by_numbers
|
8
|
+
point = Point.new(1, 2)
|
9
|
+
|
10
|
+
assert_equal 1, point.x
|
11
|
+
assert_equal 2, point.y
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_initialize_by_array
|
15
|
+
point = Point.new_by_array([1, 2])
|
16
|
+
|
17
|
+
assert_equal 1, point.x
|
18
|
+
assert_equal 2, point.y
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class EdgesTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
# +-----+
|
8
|
+
# | |
|
9
|
+
# | |
|
10
|
+
# +-----+
|
11
|
+
def test_regular
|
12
|
+
vertices = [
|
13
|
+
Point.new(0, 0),
|
14
|
+
Point.new(1, 0),
|
15
|
+
Point.new(1, 1),
|
16
|
+
Point.new(0, 1)
|
17
|
+
]
|
18
|
+
|
19
|
+
polygon = Polygon.new(vertices)
|
20
|
+
|
21
|
+
expected_edges = [
|
22
|
+
Segment.new_by_arrays(vertices[0], vertices[1]),
|
23
|
+
Segment.new_by_arrays(vertices[1], vertices[2]),
|
24
|
+
Segment.new_by_arrays(vertices[2], vertices[3]),
|
25
|
+
Segment.new_by_arrays(vertices[3], vertices[0])
|
26
|
+
]
|
27
|
+
|
28
|
+
assert_equal expected_edges, polygon.edges
|
29
|
+
end
|
30
|
+
|
31
|
+
# /\
|
32
|
+
# / \
|
33
|
+
# -----------
|
34
|
+
# \ /
|
35
|
+
# \/
|
36
|
+
def test_self_intersecting
|
37
|
+
vertices = [
|
38
|
+
Point.new(0, 0),
|
39
|
+
Point.new(4, 0),
|
40
|
+
Point.new(3, 2),
|
41
|
+
Point.new(1, -2),
|
42
|
+
]
|
43
|
+
|
44
|
+
polygon = Polygon.new(vertices)
|
45
|
+
|
46
|
+
expected_edges = [
|
47
|
+
Segment.new_by_arrays(vertices[0], vertices[1]),
|
48
|
+
Segment.new_by_arrays(vertices[1], vertices[2]),
|
49
|
+
Segment.new_by_arrays(vertices[2], vertices[3]),
|
50
|
+
Segment.new_by_arrays(vertices[3], vertices[0])
|
51
|
+
]
|
52
|
+
|
53
|
+
assert_equal expected_edges, polygon.edges
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class BoundsTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@segment = Segment.new(Point.new(1, 1), Point.new(2, 2))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_leftmost_endpoint
|
12
|
+
assert_equal Point.new(1, 1), @segment.leftmost_endpoint
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_rightmost_endpoint
|
16
|
+
assert_equal Point.new(2, 2), @segment.rightmost_endpoint
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_topmost_endpoint
|
20
|
+
assert_equal Point.new(2, 2), @segment.topmost_endpoint
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_botommost_endpoint
|
24
|
+
assert_equal Point.new(1, 1), @segment.bottommost_endpoint
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class ContainsPointTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
# ----*----
|
8
|
+
def test_point_belongs_to_horizontal_segment
|
9
|
+
point = Point.new(3, 1)
|
10
|
+
segment = Segment.new_by_arrays([2, 1], [4, 1])
|
11
|
+
|
12
|
+
assert segment.contains_point?(point)
|
13
|
+
end
|
14
|
+
|
15
|
+
# /
|
16
|
+
# *
|
17
|
+
# /
|
18
|
+
def test_point_belongs_to_inclined_segment
|
19
|
+
point = Point.new(3, 3)
|
20
|
+
segment = Segment.new_by_arrays([2, 2], [4, 4])
|
21
|
+
|
22
|
+
assert segment.contains_point?(point)
|
23
|
+
end
|
24
|
+
|
25
|
+
# *--------
|
26
|
+
def test_point_is_segment_endpoint
|
27
|
+
point = Point.new(2, 1)
|
28
|
+
segment = Segment.new_by_arrays([2, 1], [4, 1])
|
29
|
+
|
30
|
+
assert segment.contains_point?(point)
|
31
|
+
end
|
32
|
+
|
33
|
+
# *
|
34
|
+
# ---------
|
35
|
+
def test_piont_does_not_belong_to_segment
|
36
|
+
point = Point.new(2, 3)
|
37
|
+
segment = Segment.new_by_arrays([2, 1], [4, 1])
|
38
|
+
|
39
|
+
assert ! segment.contains_point?(point)
|
40
|
+
end
|
41
|
+
|
42
|
+
# * ---------
|
43
|
+
def test_point_on_same_horizontal_line
|
44
|
+
point = Point.new(1, 1)
|
45
|
+
segment = Segment.new_by_arrays([2, 1], [4, 1])
|
46
|
+
|
47
|
+
assert ! segment.contains_point?(point)
|
48
|
+
end
|
49
|
+
|
50
|
+
# /
|
51
|
+
# /
|
52
|
+
# /
|
53
|
+
#
|
54
|
+
# *
|
55
|
+
def test_point_on_same_inclined_line
|
56
|
+
point = Point.new(1, 1)
|
57
|
+
segment = Segment.new_by_arrays([2, 2], [4, 4])
|
58
|
+
|
59
|
+
assert ! segment.contains_point?(point)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class InitializeTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_initialize_by_points
|
8
|
+
segment = Segment.new(Point.new(1, 2), Point.new(3, 4))
|
9
|
+
|
10
|
+
assert_equal Point.new(1, 2), segment.point1
|
11
|
+
assert_equal Point.new(3, 4), segment.point2
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_initialize_by_points_coordinates_arrays
|
15
|
+
segment = Segment.new_by_arrays([1, 2], [3, 4])
|
16
|
+
|
17
|
+
assert_equal Point.new(1, 2), segment.point1
|
18
|
+
assert_equal Point.new(3, 4), segment.point2
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class IntersectionPointWithTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_regular_case
|
8
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 2])
|
9
|
+
segment2 = Segment.new_by_arrays([0, 2], [2, 0])
|
10
|
+
|
11
|
+
assert_equal Point.new(1, 1), segment1.intersection_point_with(segment2)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_segments_intersect_at_the_endpoint
|
15
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 2])
|
16
|
+
segment2 = Segment.new_by_arrays([0, 2], [2, 2])
|
17
|
+
|
18
|
+
assert_equal Point.new(2, 2), segment1.intersection_point_with(segment2)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_segments_do_not_intersect
|
22
|
+
segment1 = Segment.new_by_arrays([0, 0], [0, 2])
|
23
|
+
segment2 = Segment.new_by_arrays([1, 1], [2, 1])
|
24
|
+
|
25
|
+
assert_raise SegmentsDoNotIntersect do
|
26
|
+
segment1.intersection_point_with(segment2)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_segments_are_parallel
|
31
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 2])
|
32
|
+
segment2 = Segment.new_by_arrays([1, 0], [3, 2])
|
33
|
+
|
34
|
+
assert_raise SegmentsDoNotIntersect do
|
35
|
+
segment1.intersection_point_with(segment2)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_segments_overlap
|
40
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 2])
|
41
|
+
segment2 = Segment.new_by_arrays([1, 1], [3, 3])
|
42
|
+
|
43
|
+
assert_raise SegmentsOverlap do
|
44
|
+
segment1.intersection_point_with(segment2)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_segments_parallel_and_have_common_endpoint
|
49
|
+
segment1 = Segment.new_by_arrays([0, 0], [1, 0])
|
50
|
+
segment2 = Segment.new_by_arrays([1, 0], [2, 0])
|
51
|
+
|
52
|
+
assert_equal Point.new(0, 1), segment1.intersection_point_with(segment2)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class IntersectsTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
# |
|
8
|
+
# |
|
9
|
+
# -----+-----
|
10
|
+
# |
|
11
|
+
# |
|
12
|
+
def test_segments_intersect
|
13
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 0])
|
14
|
+
segment2 = Segment.new_by_arrays([1, 1], [1, -1])
|
15
|
+
|
16
|
+
assert segment1.intersects_with?(segment2)
|
17
|
+
end
|
18
|
+
|
19
|
+
# |
|
20
|
+
# | ------
|
21
|
+
# |
|
22
|
+
def test_segments_does_not_intersect_but_lay_on_intersecting_lines
|
23
|
+
segment1 = Segment.new_by_arrays([0, 0], [0, 2])
|
24
|
+
segment2 = Segment.new_by_arrays([1, 1], [3, 1])
|
25
|
+
|
26
|
+
assert ! segment1.intersects_with?(segment2)
|
27
|
+
end
|
28
|
+
|
29
|
+
# ---
|
30
|
+
# |
|
31
|
+
# |
|
32
|
+
# |
|
33
|
+
# --- |---------|
|
34
|
+
def test_segments_does_not_intersect_but_line_contains_end_point
|
35
|
+
segment1 = Segment.new_by_arrays([0, 0], [0, 2])
|
36
|
+
segment2 = Segment.new_by_arrays([1, 0], [3, 0])
|
37
|
+
|
38
|
+
assert ! segment1.intersects_with?(segment2)
|
39
|
+
end
|
40
|
+
|
41
|
+
# /
|
42
|
+
# /
|
43
|
+
# /
|
44
|
+
# *--------
|
45
|
+
def test_segment_contains_endpoint
|
46
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 2])
|
47
|
+
segment2 = Segment.new_by_arrays([0, 0], [2, 0])
|
48
|
+
|
49
|
+
assert segment1.intersects_with?(segment2)
|
50
|
+
end
|
51
|
+
|
52
|
+
# --------
|
53
|
+
# --------
|
54
|
+
def test_segments_parralel
|
55
|
+
segment1 = Segment.new_by_arrays([0, 1], [2, 1])
|
56
|
+
segment2 = Segment.new_by_arrays([0, 0], [2, 0])
|
57
|
+
|
58
|
+
assert ! segment1.intersects_with?(segment2)
|
59
|
+
end
|
60
|
+
|
61
|
+
# |------*------|
|
62
|
+
def test_segments_parallel_and_have_common_endpoint
|
63
|
+
segment1 = Segment.new_by_arrays([0, 0], [1, 0])
|
64
|
+
segment2 = Segment.new_by_arrays([1, 0], [2, 0])
|
65
|
+
|
66
|
+
assert segment1.intersects_with?(segment2)
|
67
|
+
end
|
68
|
+
|
69
|
+
# |----|===|----|
|
70
|
+
def test_segments_overlap
|
71
|
+
segment1 = Segment.new_by_arrays([0, 0], [2, 0])
|
72
|
+
segment2 = Segment.new_by_arrays([1, 0], [3, 0])
|
73
|
+
|
74
|
+
assert segment1.intersects_with?(segment2)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_segments_overlap_on_vertical_line
|
78
|
+
segment1 = Segment.new_by_arrays([0, 0], [0, 2])
|
79
|
+
segment2 = Segment.new_by_arrays([0, 1], [0, 3])
|
80
|
+
|
81
|
+
assert segment1.intersects_with?(segment2)
|
82
|
+
end
|
83
|
+
|
84
|
+
# |----| |----|
|
85
|
+
def test_segments_lie_on_one_line
|
86
|
+
segment1 = Segment.new_by_arrays([0, 0], [1, 0])
|
87
|
+
segment2 = Segment.new_by_arrays([2, 0], [3, 0])
|
88
|
+
|
89
|
+
assert ! segment1.intersects_with?(segment2)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_segments_lie_on_one_vertical_line
|
93
|
+
segment1 = Segment.new_by_arrays([0, 0], [0, 1])
|
94
|
+
segment2 = Segment.new_by_arrays([0, 2], [0, 3])
|
95
|
+
|
96
|
+
assert ! segment1.intersects_with?(segment2)
|
97
|
+
end
|
98
|
+
end
|