Skip to content

Commit f2760e2

Browse files
committedAug 2, 2024·
Made interval types meaningful.
1 parent a661ab1 commit f2760e2

File tree

4 files changed

+1224
-148
lines changed

4 files changed

+1224
-148
lines changed
 
+21-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
#pragma once
22

3-
#if defined(__cpp_concepts) && __cpp_concepts >= 202002L
4-
# define LIB_INTERVAL_TREE_CONCEPTS
3+
// define LIB_INTERVAL_TREE_CONCEPTS to override the concepts feature test
4+
#ifndef LIB_INTERVAL_TREE_CONCEPTS
5+
# if defined(__cpp_concepts) && __cpp_concepts >= 202002L
6+
# define LIB_INTERVAL_TREE_CONCEPTS
7+
# endif
58
#endif
69

7-
namespace lib_interval_tree
8-
{
9-
}
10+
// define LIB_INTERVAL_TREE_DEPRECATED if __attribute__((deprecated)) is not supported
11+
#ifndef LIB_INTERVAL_TREE_DEPRECATED
12+
# if __has_cpp_attribute(depreacted)
13+
# define LIB_INTERVAL_TREE_DEPRECATED [[deprecated]]
14+
# else
15+
# define LIB_INTERVAL_TREE_DEPRECATED __attribute__((deprecated))
16+
# endif
17+
#endif
18+
19+
#ifndef LIB_INTERVAL_TREE_FALLTHROUGH
20+
# if __has_cpp_attribute(fallthrough)
21+
# define LIB_INTERVAL_TREE_FALLTHROUGH [[fallthrough]]
22+
# else
23+
# define LIB_INTERVAL_TREE_FALLTHROUGH __attribute__((fallthrough))
24+
# endif
25+
#endif

‎include/interval-tree/interval_tree.hpp

+183-26
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "interval_tree_fwd.hpp"
44
#include "interval_types.hpp"
55
#include "tree_hooks.hpp"
6+
#include "feature_test.hpp"
67

78
#include <string>
89
#include <memory>
@@ -26,8 +27,8 @@ namespace lib_interval_tree
2627
// ############################################################################################################
2728
using default_interval_value_type = int;
2829
// ############################################################################################################
29-
template <typename numerical_type, typename interval_kind_ = closed>
30-
struct interval
30+
template <typename numerical_type, typename interval_kind_>
31+
struct interval_base
3132
{
3233
public:
3334
using value_type = numerical_type;
@@ -40,7 +41,7 @@ namespace lib_interval_tree
4041
# if __cplusplus >= 201703L
4142
constexpr
4243
# endif
43-
interval(value_type low, value_type high)
44+
interval_base(value_type low, value_type high)
4445
: low_{low}
4546
, high_{high}
4647
{
@@ -51,52 +52,86 @@ namespace lib_interval_tree
5152
# if __cplusplus >= 201703L
5253
constexpr
5354
# endif
54-
interval(value_type low, value_type high)
55+
interval_base(value_type low, value_type high)
5556
: low_{std::min(low, high)}
5657
, high_{std::max(low, high)}
5758
{}
5859
#endif
59-
virtual ~interval() = default;
60+
virtual ~interval_base() = default;
6061

6162
/**
62-
* Returns if both intervals equal.
63+
* Returns the lower bound of the interval
6364
*/
64-
friend bool operator==(interval const& lhs, interval const& other)
65+
value_type low() const
6566
{
66-
return lhs.low_ == other.low_ && lhs.high_ == other.high_;
67+
return low_;
6768
}
6869

6970
/**
70-
* Returns if both intervals are different.
71+
* Returns the upper bound of the interval
7172
*/
72-
friend bool operator!=(interval const& lhs, interval const& other)
73+
value_type high() const
7374
{
74-
return lhs.low_ != other.low_ || lhs.high_ != other.high_;
75+
return high_;
7576
}
7677

78+
protected:
79+
value_type low_;
80+
value_type high_;
81+
};
82+
// ############################################################################################################
83+
template <typename numerical_type, typename interval_kind_ = closed>
84+
struct interval : public interval_base<numerical_type, interval_kind_>
85+
{
86+
public:
87+
using value_type = typename interval_base<numerical_type, interval_kind_>::value_type;
88+
using interval_kind = typename interval_base<numerical_type, interval_kind_>::interval_kind;
89+
90+
using interval_base<numerical_type, interval_kind_>::low;
91+
using interval_base<numerical_type, interval_kind_>::high;
92+
93+
using interval_base<numerical_type, interval_kind_>::low_;
94+
using interval_base<numerical_type, interval_kind_>::high_;
95+
7796
/**
78-
* Returns the lower bound of the interval
97+
* Constructs an interval. low MUST be smaller than high.
7998
*/
80-
value_type low() const
99+
#if __cplusplus >= 201703L
100+
constexpr
101+
#endif
102+
interval(value_type low, value_type high)
103+
: interval_base<numerical_type, interval_kind_>{low, high}
104+
{}
105+
106+
/**
107+
* Returns true if both intervals equal.
108+
*/
109+
friend bool operator==(
110+
interval<numerical_type, interval_kind_> const& lhs,
111+
interval<numerical_type, interval_kind_> const& rhs
112+
)
81113
{
82-
return low_;
114+
return lhs.low_ == rhs.low_ && lhs.high_ == rhs.high_;
83115
}
84116

85117
/**
86-
* Returns the upper bound of the interval
118+
* Returns true if both intervals are different.
87119
*/
88-
value_type high() const
120+
friend bool operator!=(
121+
interval<numerical_type, interval_kind_> const& lhs,
122+
interval<numerical_type, interval_kind_> const& rhs
123+
)
89124
{
90-
return high_;
125+
return lhs.low_ != rhs.low_ || lhs.high_ != rhs.high_;
91126
}
92127

93128
/**
94129
* Returns whether the intervals overlap.
95130
* For when both intervals are closed.
96131
*/
97-
bool overlaps(value_type l, value_type h) const
132+
LIB_INTERVAL_TREE_DEPRECATED bool overlaps(value_type l, value_type h) const
98133
{
99-
return low_ <= h && l <= high_;
134+
return interval_kind::overlaps(low_, high_, l, h);
100135
}
101136

102137
/**
@@ -113,7 +148,7 @@ namespace lib_interval_tree
113148
*/
114149
bool overlaps(interval const& other) const
115150
{
116-
return overlaps(other.low_, other.high_);
151+
return interval_kind::overlaps(low_, high_, other.low_, other.high_);
117152
}
118153

119154
/**
@@ -137,7 +172,7 @@ namespace lib_interval_tree
137172
*/
138173
bool within(interval const& other) const
139174
{
140-
return low_ <= other.low_ && high_ >= other.high_;
175+
return within(other.low_) && within(other.high_);
141176
}
142177

143178
/**
@@ -148,18 +183,113 @@ namespace lib_interval_tree
148183
{
149184
if (overlaps(other))
150185
return 0;
151-
if (high_ < other.low_)
186+
if (high_ <= other.low_)
152187
return other.low_ - high_;
153188
else
154189
return low_ - other.high_;
155190
}
156191

192+
/**
193+
* Creates a new interval from this and other, that contains both intervals and whatever
194+
* is between.
195+
*/
196+
interval join(interval const& other) const
197+
{
198+
return {std::min(low_, other.low_), std::max(high_, other.high_)};
199+
}
200+
157201
/**
158202
* Returns the size of the interval.
159203
*/
160204
value_type size() const
161205
{
162-
return high_ - low_;
206+
return interval_kind::size(low_, high_);
207+
}
208+
};
209+
210+
template <typename numerical_type>
211+
struct interval<numerical_type, dynamic> : public interval_base<numerical_type, dynamic>
212+
{
213+
public:
214+
using value_type = typename interval_base<numerical_type, dynamic>::value_type;
215+
using interval_kind = dynamic;
216+
217+
/**
218+
* Constructs an interval. low MUST be smaller than high.
219+
*/
220+
#if __cplusplus >= 201703L
221+
constexpr
222+
#endif
223+
interval(value_type low, value_type high, interval_border leftBorder, interval_border rightBorder)
224+
: interval_base<numerical_type, interval_kind>{low, high}
225+
, left_border_{leftBorder}
226+
, right_border_{rightBorder}
227+
{}
228+
229+
/**
230+
* Returns true if both intervals equal.
231+
*/
232+
friend bool
233+
operator==(interval<numerical_type, dynamic> const& lhs, interval<numerical_type, dynamic> const& other)
234+
{
235+
return lhs.low_ == other.low_ && lhs.high_ == other.high_ && lhs.left_border_ == other.left_border_ &&
236+
lhs.right_border_ == other.right_border_;
237+
}
238+
239+
/**
240+
* Returns true if both intervals are different.
241+
*/
242+
friend bool
243+
operator!=(interval<numerical_type, dynamic> const& lhs, interval<numerical_type, dynamic> const& other)
244+
{
245+
return lhs.low_ != other.low_ || lhs.high_ != other.high_ || lhs.left_border_ != other.left_border_ ||
246+
lhs.right_border_ != other.right_border_;
247+
}
248+
249+
using interval_base<numerical_type, interval_kind>::low;
250+
using interval_base<numerical_type, interval_kind>::high;
251+
using interval_base<numerical_type, interval_kind>::low_;
252+
using interval_base<numerical_type, interval_kind>::high_;
253+
254+
/**
255+
* Returns whether the intervals overlap
256+
*/
257+
bool overlaps(interval const& other) const
258+
{
259+
return interval_kind::overlaps(*this, other);
260+
}
261+
262+
/**
263+
* Returns whether the intervals overlap exclusively, independent of border.
264+
*/
265+
bool overlaps_exclusive(interval const& other) const
266+
{
267+
return low_ < other.high_ && other.low_ < high_;
268+
}
269+
270+
/**
271+
* Returns whether the given value is in this.
272+
*/
273+
bool within(value_type value) const
274+
{
275+
return interval_kind::within(*this, value);
276+
}
277+
278+
/**
279+
* Returns whether the given interval is in this.
280+
*/
281+
bool within(interval const& other) const
282+
{
283+
return within(other.low_) && within(other.high_);
284+
}
285+
286+
/**
287+
* Calculates the distance between the two intervals.
288+
* Overlapping intervals have 0 distance.
289+
*/
290+
value_type operator-(interval const& other) const
291+
{
292+
interval_kind::distance(*this, other);
163293
}
164294

165295
/**
@@ -168,13 +298,40 @@ namespace lib_interval_tree
168298
*/
169299
interval join(interval const& other) const
170300
{
171-
return {std::min(low_, other.low_), std::max(high_, other.high_)};
301+
return interval_kind::join(*this, other);
302+
}
303+
304+
/**
305+
* Returns the size of the interval.
306+
*/
307+
value_type size() const
308+
{
309+
return interval_kind::size(*this);
310+
}
311+
312+
/**
313+
* @brief Returns the left border type of the interval.
314+
*
315+
*/
316+
interval_border left_border() const
317+
{
318+
return left_border_;
319+
}
320+
321+
/**
322+
* @brief Returns the right border type of the interval.
323+
*
324+
*/
325+
interval_border right_border() const
326+
{
327+
return right_border_;
172328
}
173329

174330
protected:
175-
value_type low_;
176-
value_type high_;
331+
interval_border left_border_;
332+
interval_border right_border_;
177333
};
334+
178335
// ############################################################################################################
179336
/**
180337
* Creates a safe interval that puts the lower bound left automatically.
+344-8
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,377 @@
11
#pragma once
22

3+
#include "feature_test.hpp"
4+
5+
#ifdef LIB_INTERVAL_TREE_CONCEPTS
6+
# include <type_traits>
7+
#endif
8+
9+
#include <cmath>
10+
#include <algorithm>
11+
312
namespace lib_interval_tree
413
{
514
// (]
615
struct left_open
716
{
817
template <typename numerical_type>
9-
static inline bool within(numerical_type b, numerical_type e, numerical_type p)
18+
static inline bool within(numerical_type low, numerical_type high, numerical_type p)
19+
{
20+
return (low < p) && (p <= high);
21+
}
22+
23+
template <typename numerical_type>
24+
static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2)
25+
{
26+
return (l1 < h2) && (l2 <= h1);
27+
}
28+
29+
template <typename numerical_type>
30+
static inline numerical_type size(numerical_type low, numerical_type high)
1031
{
11-
return (b < p) && (p <= e);
32+
return high - low;
1233
}
1334
};
1435
// [)
1536
struct right_open
1637
{
1738
template <typename numerical_type>
18-
static inline bool within(numerical_type b, numerical_type e, numerical_type p)
39+
static inline bool within(numerical_type low, numerical_type high, numerical_type p)
1940
{
20-
return (b <= p) && (p < e);
41+
return (low <= p) && (p < high);
42+
}
43+
44+
template <typename numerical_type>
45+
static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2)
46+
{
47+
return (l1 <= h2) && (l2 < h1);
48+
}
49+
50+
template <typename numerical_type>
51+
static inline numerical_type size(numerical_type low, numerical_type high)
52+
{
53+
return high - low;
2154
}
2255
};
2356
// []
2457
struct closed
2558
{
2659
template <typename numerical_type>
27-
static inline bool within(numerical_type b, numerical_type e, numerical_type p)
60+
static inline bool within(numerical_type low, numerical_type high, numerical_type p)
2861
{
29-
return (b <= p) && (p <= e);
62+
return (low <= p) && (p <= high);
63+
}
64+
65+
template <typename numerical_type>
66+
static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2)
67+
{
68+
return (l1 <= h2) && (l2 <= h1);
69+
}
70+
71+
template <typename numerical_type>
72+
static inline numerical_type size(numerical_type low, numerical_type high)
73+
{
74+
return high - low + 1;
3075
}
3176
};
3277
// ()
3378
struct open
3479
{
3580
template <typename numerical_type>
36-
static inline bool within(numerical_type b, numerical_type e, numerical_type p)
81+
static inline bool within(numerical_type low, numerical_type high, numerical_type p)
3782
{
38-
return (b < p) && (p < e);
83+
return (low < p) && (p < high);
84+
}
85+
86+
template <typename numerical_type>
87+
static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2)
88+
{
89+
return (l1 < h2) && (l2 < h1);
90+
}
91+
92+
template <typename numerical_type>
93+
static inline numerical_type size(numerical_type low, numerical_type high)
94+
{
95+
return high - low - 1;
96+
}
97+
};
98+
/// [] and adjacent counts as overlapping
99+
struct closed_adjacent
100+
{
101+
template <typename numerical_type>
102+
static inline bool within(numerical_type low, numerical_type high, numerical_type p)
103+
{
104+
return (low <= p) && (p <= high);
105+
}
106+
107+
template <typename numerical_type>
108+
static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2)
109+
{
110+
return (l1 <= (h2 + 1)) && ((l2 - 1) <= h1);
111+
}
112+
113+
template <typename numerical_type>
114+
static inline numerical_type size(numerical_type low, numerical_type high)
115+
{
116+
return high - low + 1;
117+
}
118+
};
119+
120+
enum class interval_border
121+
{
122+
closed,
123+
open,
124+
closed_adjacent
125+
};
126+
class dynamic
127+
{
128+
public:
129+
template <typename interval_type>
130+
#ifdef LIB_INTERVAL_TREE_CONCEPTS
131+
requires std::is_integral_v<typename interval_type::value_type>
132+
#endif
133+
static inline bool within(interval_type const& ival, typename interval_type::value_type p)
134+
{
135+
switch (ival.left_border())
136+
{
137+
case interval_border::open:
138+
{
139+
if (ival.low() >= p)
140+
{
141+
return false;
142+
}
143+
break;
144+
}
145+
case interval_border::closed_adjacent:
146+
LIB_INTERVAL_TREE_FALLTHROUGH;
147+
case interval_border::closed:
148+
{
149+
if (ival.low() > p)
150+
{
151+
return false;
152+
}
153+
break;
154+
}
155+
}
156+
switch (ival.right_border())
157+
{
158+
case interval_border::open:
159+
{
160+
if (p >= ival.high())
161+
{
162+
return false;
163+
}
164+
break;
165+
}
166+
case interval_border::closed_adjacent:
167+
LIB_INTERVAL_TREE_FALLTHROUGH;
168+
case interval_border::closed:
169+
{
170+
if (p > ival.high())
171+
{
172+
return false;
173+
}
174+
break;
175+
}
176+
}
177+
return true;
178+
}
179+
180+
template <typename interval_type>
181+
#ifdef LIB_INTERVAL_TREE_CONCEPTS
182+
requires std::is_integral_v<typename interval_type::value_type>
183+
#endif
184+
static inline bool overlaps(interval_type const& ival1, interval_type const& ival2)
185+
{
186+
const auto lowToClosed = [](auto const& ival) {
187+
if (ival.left_border() == interval_border::open)
188+
return ival.low() + 1;
189+
return ival.low();
190+
};
191+
192+
const auto highToClosed = [](auto const& ival) {
193+
if (ival.right_border() == interval_border::open)
194+
return ival.high() - 1;
195+
return ival.high();
196+
};
197+
198+
const interval_type closedEquiv1{
199+
lowToClosed(ival1), highToClosed(ival1), interval_border::closed, interval_border::closed
200+
};
201+
const interval_type closedEquiv2{
202+
lowToClosed(ival2), highToClosed(ival2), interval_border::closed, interval_border::closed
203+
};
204+
205+
auto closedOverlap =
206+
closed::overlaps(closedEquiv1.low(), closedEquiv1.high(), closedEquiv2.low(), closedEquiv2.high());
207+
if (!closedOverlap)
208+
{
209+
if (closedEquiv1.high() + 1 == closedEquiv2.low() &&
210+
(ival1.right_border() == interval_border::closed_adjacent ||
211+
ival2.left_border() == interval_border::closed_adjacent))
212+
{
213+
return true;
214+
}
215+
if (closedEquiv2.high() + 1 == closedEquiv1.low() &&
216+
(ival2.right_border() == interval_border::closed_adjacent ||
217+
ival1.left_border() == interval_border::closed_adjacent))
218+
{
219+
return true;
220+
}
221+
return false;
222+
}
223+
return true;
224+
}
225+
226+
template <typename interval_type>
227+
#ifdef LIB_INTERVAL_TREE_CONCEPTS
228+
requires std::is_integral_v<typename interval_type::value_type>
229+
#endif
230+
static typename interval_type::value_type distance(interval_type const& ival1, interval_type const& ival2)
231+
{
232+
using value_type = typename interval_type::value_type;
233+
234+
if (overlaps(ival1, ival2))
235+
return 0;
236+
237+
value_type adjusted_low = ival1.left_border() == interval_border::open ? ival1.low() + 1 : ival1.low();
238+
value_type adjusted_high = ival1.right_border() == interval_border::open ? ival1.high() - 1 : ival1.high();
239+
value_type other_adjusted_low =
240+
ival2.left_border() == interval_border::open ? ival2.low() + 1 : ival2.low();
241+
value_type other_adjusted_high =
242+
ival2.right_border() == interval_border::open ? ival2.high() - 1 : ival2.high();
243+
244+
if (adjusted_high < other_adjusted_low)
245+
return other_adjusted_low - adjusted_high;
246+
return adjusted_low - other_adjusted_high;
247+
}
248+
249+
static interval_border border_promote(interval_border border1, interval_border border2)
250+
{
251+
if (border1 == interval_border::closed_adjacent || border2 == interval_border::closed_adjacent)
252+
return interval_border::closed_adjacent;
253+
if (border1 == interval_border::closed || border2 == interval_border::closed)
254+
return interval_border::closed;
255+
return interval_border::open;
256+
}
257+
258+
/**
259+
* @brief Assumes that ival1 and ival2 overlap and both intervals are dynamic
260+
*
261+
* @tparam interval_type
262+
* @param ival1
263+
* @param ival2
264+
* @return interval_type
265+
*/
266+
template <typename interval_type>
267+
#ifdef LIB_INTERVAL_TREE_CONCEPTS
268+
requires std::is_integral_v<typename interval_type::value_type>
269+
#endif
270+
static interval_type join(interval_type const& ival1, interval_type const& ival2)
271+
{
272+
typename interval_type::value_type low;
273+
typename interval_type::value_type high;
274+
interval_border left;
275+
interval_border right;
276+
277+
const bool ival1LeftAnyClosed = ival1.left_border() == interval_border::closed_adjacent ||
278+
ival1.left_border() == interval_border::closed;
279+
const bool ival2LeftAnyClosed = ival2.left_border() == interval_border::closed_adjacent ||
280+
ival2.left_border() == interval_border::closed;
281+
const bool ival1RightAnyClosed = ival1.right_border() == interval_border::closed_adjacent ||
282+
ival1.right_border() == interval_border::closed;
283+
const bool ival2RightAnyClosed = ival2.right_border() == interval_border::closed_adjacent ||
284+
ival2.right_border() == interval_border::closed;
285+
286+
// same border means trivial min/max:
287+
if (ival1.left_border() == ival2.left_border() || (ival1LeftAnyClosed && ival2LeftAnyClosed))
288+
{
289+
left = border_promote(ival1.left_border(), ival2.left_border());
290+
low = std::min(ival1.low(), ival2.low());
291+
}
292+
else
293+
{
294+
auto* leftOpenInterval = ival1.left_border() == interval_border::open ? &ival1 : &ival2;
295+
auto* leftClosedInterval = (ival1.left_border() == interval_border::closed ||
296+
ival1.left_border() == interval_border::closed_adjacent)
297+
? &ival1
298+
: &ival2;
299+
300+
const auto openAdjusted = leftOpenInterval->low() + 1;
301+
302+
if (openAdjusted == leftClosedInterval->low())
303+
{
304+
left = leftClosedInterval->left_border();
305+
low = leftClosedInterval->low();
306+
}
307+
else if (leftOpenInterval->low() < leftClosedInterval->low())
308+
{
309+
left = leftOpenInterval->left_border();
310+
low = leftOpenInterval->low();
311+
}
312+
else
313+
{
314+
left = leftClosedInterval->left_border();
315+
low = leftClosedInterval->low();
316+
}
317+
}
318+
319+
if (ival1.right_border() == ival2.right_border() || (ival1RightAnyClosed && ival2RightAnyClosed))
320+
{
321+
right = border_promote(ival1.right_border(), ival2.right_border());
322+
high = std::max(ival1.high(), ival2.high());
323+
}
324+
else
325+
{
326+
auto* rightOpenInterval = ival1.right_border() == interval_border::open ? &ival1 : &ival2;
327+
auto* rightClosedInterval = (ival1.right_border() == interval_border::closed ||
328+
ival1.right_border() == interval_border::closed_adjacent)
329+
? &ival1
330+
: &ival2;
331+
332+
const auto openAdjusted = rightOpenInterval->high() - 1;
333+
334+
if (openAdjusted == rightClosedInterval->high())
335+
{
336+
right = rightClosedInterval->right_border();
337+
high = rightClosedInterval->high();
338+
}
339+
else if (rightOpenInterval->high() > rightClosedInterval->high())
340+
{
341+
right = rightOpenInterval->right_border();
342+
high = rightOpenInterval->high();
343+
}
344+
else
345+
{
346+
right = rightClosedInterval->right_border();
347+
high = rightClosedInterval->high();
348+
}
349+
}
350+
351+
return interval_type{low, high, left, right};
352+
}
353+
354+
template <typename interval_type>
355+
#ifdef LIB_INTERVAL_TREE_CONCEPTS
356+
requires std::is_integral_v<typename interval_type::value_type>
357+
#endif
358+
typename interval_type::value_type size(interval_type const& ival)
359+
{
360+
auto left = ival.left_border();
361+
if (left == interval_border::closed_adjacent)
362+
left = interval_border::closed;
363+
auto right = ival.right_border();
364+
if (right == interval_border::closed_adjacent)
365+
right = interval_border::closed;
366+
367+
if (left == right)
368+
{
369+
return left == interval_border::open ? open::size(ival.low(), ival.high())
370+
: closed::size(ival.low(), ival.high());
371+
}
372+
if (left == interval_border::open)
373+
return left_open::size(ival.low(), ival.high());
374+
return right_open::size(ival.low(), ival.high());
39375
}
40376
};
41377
}

‎tests/interval_tests.hpp

+676-109
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.