Commit 0048f0b1 authored by Alexey Botchkov's avatar Alexey Botchkov

Valgrind warning fixed.

Coordinate size limitation removed.

per-file comments:
  mysql-test/r/gis-precise.result
        test result updated.
  sql/gcalc_slicescan.cc
        Check coordinate extent to pick better precidion in the ::set_double()
  sql/gcalc_slicescan.h
        free_list() can lead to valgrind warnig. Fixed
  sql/gcalc_tools.cc
        free_list() call changed.
parent 0edb291d
...@@ -255,7 +255,7 @@ ST_NUMGEOMETRIES((ST_UNION(ST_UNION( ...@@ -255,7 +255,7 @@ ST_NUMGEOMETRIES((ST_UNION(ST_UNION(
MULTILINESTRINGFROMTEXT('MULTILINESTRING((2 0,4 2,0 2,1 5,0 3,7 0,8 5,5 8), MULTILINESTRINGFROMTEXT('MULTILINESTRING((2 0,4 2,0 2,1 5,0 3,7 0,8 5,5 8),
(6 2,4 0,3 5,3 6,4 3,6 4,3 9,0 7,3 7,8 4,2 9,5 0), (6 2,4 0,3 5,3 6,4 3,6 4,3 9,0 7,3 7,8 4,2 9,5 0),
185 192
SELECT Round(ST_AREA(ST_BUFFER( ST_UNION( SELECT Round(ST_AREA(ST_BUFFER( ST_UNION(
POLYGONFROMTEXT('POLYGON((7 7, 7 7, 7 4, 7 7, 7 7))'), POLYGONFROMTEXT('POLYGON((7 7, 7 7, 7 4, 7 7, 7 7))'),
POLYGONFROMTEXT('POLYGON((7 7, 4 7, 2 9, 7 6, 7 7))')), 1)), 6); POLYGONFROMTEXT('POLYGON((7 7, 4 7, 2 9, 7 6, 7 7))')), 1)), 6);
...@@ -336,7 +336,7 @@ MULTILINESTRINGFROMTEXT('MULTILINESTRING((3 4, 3 1, 2 7, 4 2, 6 2, 1 5))') ...@@ -336,7 +336,7 @@ MULTILINESTRINGFROMTEXT('MULTILINESTRING((3 4, 3 1, 2 7, 4 2, 6 2, 1 5))')
ST_NUMPOINTS(ST_EXTERIORRING(ST_BUFFER(ST_UNION( ST_NUMPOINTS(ST_EXTERIORRING(ST_BUFFER(ST_UNION(
MULTILINESTRINGFROMTEXT('MULTILINESTRING((3 4, 2 5, 7 6, 1 8),(0 0 ,1 6 ,0 1, 8 9, 2 4, 6 1, 3 5, 4 8), (9 3, 5 4, 1 8, 4 2, 5 8, 3 0))' ) , MULTILINESTRINGFROMTEXT('MULTILINESTRING((3 4, 2 5, 7 6, 1 8),(0 0 ,1 6 ,0 1, 8 9, 2 4, 6 1, 3 5, 4 8), (9 3, 5 4, 1 8, 4 2, 5 8, 3 0))' ) ,
MULTILINESTRINGFROMTEXT('MULTILINESTRING((3 4, 3 1, 2 7, 4 2, 6 2 MULTILINESTRINGFROMTEXT('MULTILINESTRING((3 4, 3 1, 2 7, 4 2, 6 2
275 278
SELECT ST_NUMGEOMETRIES(ST_DIFFERENCE ( SELECT ST_NUMGEOMETRIES(ST_DIFFERENCE (
ST_UNION ( ST_UNION (
MULTILINESTRINGFROMTEXT( ' MULTILINESTRING( ( 2 4 , 5 0 , 2 9 , 6 2 , 0 2 ) , ( 4 3 , 5 6 , 9 4 , 0 7 , 7 2 , 2 0 , 8 2 ) , ( 5 0 , 1 5 , 3 7 , 7 7 ) , ( 2 3 , 9 5 , 2 0 , 8 1 ) , ( 0 9 , 9 3 , 2 8 , 8 1 , 9 4 ) ) ' ), MULTILINESTRINGFROMTEXT( ' MULTILINESTRING( ( 2 4 , 5 0 , 2 9 , 6 2 , 0 2 ) , ( 4 3 , 5 6 , 9 4 , 0 7 , 7 2 , 2 0 , 8 2 ) , ( 5 0 , 1 5 , 3 7 , 7 7 ) , ( 2 3 , 9 5 , 2 0 , 8 1 ) , ( 0 9 , 9 3 , 2 8 , 8 1 , 9 4 ) ) ' ),
...@@ -352,7 +352,7 @@ MULTILINESTRINGFROMTEXT( ' MULTILINESTRING( ( 2 9 , 1 3 , 7 3 , 8 5 ) , ( 5 0 , ...@@ -352,7 +352,7 @@ MULTILINESTRINGFROMTEXT( ' MULTILINESTRING( ( 2 9 , 1 3 , 7 3 , 8 5 ) , ( 5 0 ,
ST_NUMGEOMETRIES(ST_DIFFERENCE ( ST_NUMGEOMETRIES(ST_DIFFERENCE (
ST_UNION ( ST_UNION (
MULTILINESTRINGFROMTEXT( ' MULTILINESTRING( ( 2 4 , 5 0 , 2 9 , 6 2 , 0 2 ) , ( 4 3 , 5 6 , 9 4 , 0 7 , 7 2 , 2 0 , 8 2 ) , ( 5 0 , 1 5 , 3 7 , 7 7 ) , ( 2 3 , 9 5 , 2 0 , 8 1 ) , ( 0 9 , 9 3 , 2 8 , 8 1 , 9 4 ) MULTILINESTRINGFROMTEXT( ' MULTILINESTRING( ( 2 4 , 5 0 , 2 9 , 6 2 , 0 2 ) , ( 4 3 , 5 6 , 9 4 , 0 7 , 7 2 , 2 0 , 8 2 ) , ( 5 0 , 1 5 , 3 7 , 7 7 ) , ( 2 3 , 9 5 , 2 0 , 8 1 ) , ( 0 9 , 9 3 , 2 8 , 8 1 , 9 4 )
123 125
SELECT ASTEXT(ST_DIFFERENCE ( SELECT ASTEXT(ST_DIFFERENCE (
POLYGONFROMTEXT( ' POLYGON( ( 2 2 , 2 8 , 8 8 , 8 2 , 2 2 ) , ( 4 4 , 4 6 , 6 6 , 6 4 , 4 4 ) ) ' ) , POLYGONFROMTEXT( ' POLYGON( ( 2 2 , 2 8 , 8 8 , 8 2 , 2 2 ) , ( 4 4 , 4 6 , 6 6 , 6 4 , 4 4 ) ) ' ) ,
ST_UNION ( ST_UNION (
......
...@@ -235,21 +235,23 @@ int Gcalc_internal_coord::is_zero() const ...@@ -235,21 +235,23 @@ int Gcalc_internal_coord::is_zero() const
#ifdef GCALC_CHECK_WITH_FLOAT #ifdef GCALC_CHECK_WITH_FLOAT
double *Gcalc_internal_coord::coord_extent= NULL;
long double Gcalc_internal_coord::get_double() const long double Gcalc_internal_coord::get_double() const
{ {
int n= 0; int n= 1;
long double res= 0.0; long double res= (long double) digits[0];
do do
{ {
res*= (long double) DIG_BASE; res*= (long double) GCALC_DIG_BASE;
res+= digits[n]; res+= (long double) digits[n];
} while(++n < n_digits); } while(++n < n_digits);
n= 0; n= 0;
do do
{ {
if (n & 1) if ((n & 1) && coord_extent)
res/= (long double) C_SCALE; res/= *coord_extent;
} while(++n < n_digits); } while(++n < n_digits);
if (sign) if (sign)
...@@ -266,20 +268,21 @@ static void do_add(Gcalc_internal_coord *result, ...@@ -266,20 +268,21 @@ static void do_add(Gcalc_internal_coord *result,
GCALC_DBUG_ASSERT(a->n_digits == b->n_digits); GCALC_DBUG_ASSERT(a->n_digits == b->n_digits);
GCALC_DBUG_ASSERT(a->n_digits == result->n_digits); GCALC_DBUG_ASSERT(a->n_digits == result->n_digits);
int n_digit= a->n_digits-1; int n_digit= a->n_digits-1;
coord_digit_t carry= 0; gcalc_digit_t carry= 0;
do do
{ {
if ((result->digits[n_digit]= if ((result->digits[n_digit]=
a->digits[n_digit] + b->digits[n_digit] + carry) >= DIG_BASE) a->digits[n_digit] + b->digits[n_digit] + carry) >= GCALC_DIG_BASE)
{ {
carry= 1; carry= 1;
result->digits[n_digit]-= DIG_BASE; result->digits[n_digit]-= GCALC_DIG_BASE;
} }
else else
carry= 0; carry= 0;
} while (n_digit--); } while (--n_digit);
GCALC_DBUG_ASSERT(carry == 0); result->digits[0]= a->digits[0] + b->digits[0] + carry;
GCALC_DBUG_ASSERT(result->digits[0] < GCALC_DIG_BASE);
result->sign= a->sign; result->sign= a->sign;
} }
...@@ -291,7 +294,7 @@ static void do_sub(Gcalc_internal_coord *result, ...@@ -291,7 +294,7 @@ static void do_sub(Gcalc_internal_coord *result,
GCALC_DBUG_ASSERT(a->n_digits == b->n_digits); GCALC_DBUG_ASSERT(a->n_digits == b->n_digits);
GCALC_DBUG_ASSERT(a->n_digits == result->n_digits); GCALC_DBUG_ASSERT(a->n_digits == result->n_digits);
int n_digit= a->n_digits-1; int n_digit= a->n_digits-1;
coord_digit_t carry= 0; gcalc_digit_t carry= 0;
do do
{ {
...@@ -299,7 +302,7 @@ static void do_sub(Gcalc_internal_coord *result, ...@@ -299,7 +302,7 @@ static void do_sub(Gcalc_internal_coord *result,
a->digits[n_digit] - b->digits[n_digit] - carry) < 0) a->digits[n_digit] - b->digits[n_digit] - carry) < 0)
{ {
carry= 1; carry= 1;
result->digits[n_digit]+= DIG_BASE; result->digits[n_digit]+= GCALC_DIG_BASE;
} }
else else
carry= 0; carry= 0;
...@@ -320,7 +323,7 @@ static int do_cmp(const Gcalc_internal_coord *a, ...@@ -320,7 +323,7 @@ static int do_cmp(const Gcalc_internal_coord *a,
do do
{ {
coord_digit_t d= a->digits[n_digit] - b->digits[n_digit]; gcalc_digit_t d= a->digits[n_digit] - b->digits[n_digit];
if (d > 0) if (d > 0)
return 1; return 1;
if (d < 0) if (d < 0)
...@@ -353,7 +356,7 @@ void gcalc_mul_coord(Gcalc_internal_coord *result, ...@@ -353,7 +356,7 @@ void gcalc_mul_coord(Gcalc_internal_coord *result,
{ {
GCALC_DBUG_ASSERT(result->n_digits == a->n_digits + b->n_digits); GCALC_DBUG_ASSERT(result->n_digits == a->n_digits + b->n_digits);
int n_a, n_b, n_res; int n_a, n_b, n_res;
coord_digit_t carry= 0; gcalc_digit_t carry= 0;
result->set_zero(); result->set_zero();
if (a->is_zero() || b->is_zero()) if (a->is_zero() || b->is_zero())
...@@ -365,16 +368,17 @@ void gcalc_mul_coord(Gcalc_internal_coord *result, ...@@ -365,16 +368,17 @@ void gcalc_mul_coord(Gcalc_internal_coord *result,
n_b= b->n_digits - 1; n_b= b->n_digits - 1;
do do
{ {
coord2 mul= ((coord2) a->digits[n_a]) * b->digits[n_b] + carry + gcalc_coord2 mul= ((gcalc_coord2) a->digits[n_a]) * b->digits[n_b] +
result->digits[n_a + n_b + 1]; carry + result->digits[n_a + n_b + 1];
result->digits[n_a + n_b + 1]= mul % DIG_BASE; result->digits[n_a + n_b + 1]= mul % GCALC_DIG_BASE;
carry= mul / DIG_BASE; carry= mul / GCALC_DIG_BASE;
} while (n_b--); } while (n_b--);
if (carry) if (carry)
{ {
for (n_res= n_a; (result->digits[n_res]+= carry) >= DIG_BASE; n_res--) for (n_res= n_a; (result->digits[n_res]+= carry) >= GCALC_DIG_BASE;
n_res--)
{ {
result->digits[n_res]-= DIG_BASE; result->digits[n_res]-= GCALC_DIG_BASE;
carry= 1; carry= 1;
} }
carry= 0; carry= 0;
...@@ -458,14 +462,20 @@ int gcalc_cmp_coord(const Gcalc_internal_coord *a, ...@@ -458,14 +462,20 @@ int gcalc_cmp_coord(const Gcalc_internal_coord *a,
} }
int Gcalc_coord1::set_double(double d) int Gcalc_coord1::set_double(double d, double ext)
{ {
double ds= d * C_SCALE; double ds= d * ext;
init(); init();
if ((sign= ds < 0)) if ((sign= ds < 0))
ds= -ds; ds= -ds;
c[0]= (coord_digit_t) (ds / (double) DIG_BASE); c[0]= (gcalc_digit_t) (ds / (double) GCALC_DIG_BASE);
c[1]= (coord_digit_t) (ds - ((double) c[0]) * DIG_BASE); c[1]= (gcalc_digit_t) nearbyint(ds -
((double) c[0]) * (double) GCALC_DIG_BASE);
if (c[1] >= GCALC_DIG_BASE)
{
c[1]= 0;
c[0]++;
}
#ifdef GCALC_CHECK_WITH_FLOAT #ifdef GCALC_CHECK_WITH_FLOAT
GCALC_DBUG_ASSERT(de_check(d, get_double())); GCALC_DBUG_ASSERT(de_check(d, get_double()));
#endif /*GCALC_CHECK_WITH_FLOAT*/ #endif /*GCALC_CHECK_WITH_FLOAT*/
...@@ -486,17 +496,28 @@ void Gcalc_coord1::copy(const Gcalc_coord1 *from) ...@@ -486,17 +496,28 @@ void Gcalc_coord1::copy(const Gcalc_coord1 *from)
Gcalc_heap::Info *Gcalc_heap::new_point_info(double x, double y, Gcalc_heap::Info *Gcalc_heap::new_point_info(double x, double y,
gcalc_shape_info shape) gcalc_shape_info shape)
{ {
double abs= fabs(x);
Info *result= (Info *)new_item(); Info *result= (Info *)new_item();
if (!result) if (!result)
return NULL; return NULL;
*m_hook= result; *m_hook= result;
m_hook= &result->next; m_hook= &result->next;
m_n_points++;
result->x= x; result->x= x;
result->y= y; result->y= y;
result->shape= shape; result->shape= shape;
result->ix.set_double(x); if (m_n_points)
result->iy.set_double(y); {
if (abs > coord_extent)
coord_extent= abs;
}
else
coord_extent= abs;
abs= fabs(y);
if (abs > coord_extent)
coord_extent= abs;
m_n_points++;
return result; return result;
} }
...@@ -520,11 +541,11 @@ Gcalc_heap::Intersection_info *Gcalc_heap::new_intersection( ...@@ -520,11 +541,11 @@ Gcalc_heap::Intersection_info *Gcalc_heap::new_intersection(
class Gcalc_coord3 : public Gcalc_internal_coord class Gcalc_coord3 : public Gcalc_internal_coord
{ {
coord_digit_t c[COORD_BASE*3]; gcalc_digit_t c[GCALC_COORD_BASE*3];
public: public:
void init() void init()
{ {
n_digits= COORD_BASE*3; n_digits= GCALC_COORD_BASE*3;
digits= c; digits= c;
} }
}; };
...@@ -670,15 +691,36 @@ static int compare_point_info(const void *e0, const void *e1) ...@@ -670,15 +691,36 @@ static int compare_point_info(const void *e0, const void *e1)
} }
#define GCALC_SCALE_1 1e18
static double find_scale(double extent)
{
double scale= 1e-2;
while (scale < extent)
scale*= (double ) 10;
return GCALC_SCALE_1 / scale / 10;
}
void Gcalc_heap::prepare_operation() void Gcalc_heap::prepare_operation()
{ {
Info *cur;
GCALC_DBUG_ASSERT(m_hook); GCALC_DBUG_ASSERT(m_hook);
coord_extent= find_scale(coord_extent);
#ifdef GCALC_CHECK_WITH_FLOAT
Gcalc_internal_coord::coord_extent= &coord_extent;
#endif /*GCALC_CHECK_WITH_FLOAT*/
for (cur= get_first(); cur; cur= cur->get_next())
{
cur->ix.set_double(cur->x, coord_extent);
cur->iy.set_double(cur->y, coord_extent);
}
*m_hook= NULL; *m_hook= NULL;
m_first= sort_list(compare_point_info, m_first, m_n_points); m_first= sort_list(compare_point_info, m_first, m_n_points);
m_hook= NULL; /* just to check it's not called twice */ m_hook= NULL; /* just to check it's not called twice */
/* TODO - move this to the 'normal_scan' loop */ /* TODO - move this to the 'normal_scan' loop */
for (Info *cur= get_first(); cur; cur= cur->get_next()) for (cur= get_first(); cur; cur= cur->get_next())
{ {
trim_node(cur->left, cur); trim_node(cur->left, cur);
trim_node(cur->right, cur); trim_node(cur->right, cur);
...@@ -702,7 +744,8 @@ void Gcalc_heap::reset() ...@@ -702,7 +744,8 @@ void Gcalc_heap::reset()
if (m_n_points) if (m_n_points)
{ {
free_list(m_first); free_list(m_first);
free_list(m_first_intersection, m_intersection_hook); free_list((Gcalc_dyn_list::Item **) &m_first_intersection,
m_intersection_hook);
m_intersection_hook= (Gcalc_dyn_list::Item **) &m_first_intersection; m_intersection_hook= (Gcalc_dyn_list::Item **) &m_first_intersection;
m_n_points= 0; m_n_points= 0;
} }
...@@ -913,12 +956,13 @@ int Gcalc_scan_iterator::point::cmp_dx_dy(const point *p) const ...@@ -913,12 +956,13 @@ int Gcalc_scan_iterator::point::cmp_dx_dy(const point *p) const
void Gcalc_scan_iterator::point::calc_x(long double *x, long double y, void Gcalc_scan_iterator::point::calc_x(long double *x, long double y,
long double ix) const long double ix) const
{ {
if (fabsl(dy.get_double()) < (long double) 1e-15) long double ddy= dy.get_double();
if (fabsl(ddy) < (long double) 1e-20)
{ {
*x= ix; *x= ix;
} }
else else
*x= (long double) pi->x + dx.get_double()/dy.get_double() * (y - pi->y); *x= (ddy * (long double) pi->x + dx.get_double() * (y - pi->y)) / ddy;
} }
#endif /*GCALC_CHECK_WITH_FLOAT*/ #endif /*GCALC_CHECK_WITH_FLOAT*/
...@@ -1509,22 +1553,22 @@ int Gcalc_scan_iterator::find_intersections() ...@@ -1509,22 +1553,22 @@ int Gcalc_scan_iterator::find_intersections()
class Gcalc_coord4 : public Gcalc_internal_coord class Gcalc_coord4 : public Gcalc_internal_coord
{ {
coord_digit_t c[COORD_BASE*4]; gcalc_digit_t c[GCALC_COORD_BASE*4];
public: public:
void init() void init()
{ {
n_digits= COORD_BASE*4; n_digits= GCALC_COORD_BASE*4;
digits= c; digits= c;
} }
}; };
class Gcalc_coord5 : public Gcalc_internal_coord class Gcalc_coord5 : public Gcalc_internal_coord
{ {
coord_digit_t c[COORD_BASE*5]; gcalc_digit_t c[GCALC_COORD_BASE*5];
public: public:
void init() void init()
{ {
n_digits= COORD_BASE*5; n_digits= GCALC_COORD_BASE*5;
digits= c; digits= c;
} }
}; };
......
...@@ -74,13 +74,10 @@ public: ...@@ -74,13 +74,10 @@ public:
item->next= m_free; item->next= m_free;
m_free= item; m_free= item;
} }
inline void free_list(Item *list, Item **hook) inline void free_list(Item **list, Item **hook)
{
if (*hook != list)
{ {
*hook= m_free; *hook= m_free;
m_free= list; m_free= *list;
}
} }
void free_list(Item *list) void free_list(Item *list)
...@@ -88,7 +85,7 @@ public: ...@@ -88,7 +85,7 @@ public:
Item **hook= &list; Item **hook= &list;
while (*hook) while (*hook)
hook= &(*hook)->next; hook= &(*hook)->next;
free_list(list, hook); free_list(&list, hook);
} }
void reset(); void reset();
...@@ -113,22 +110,22 @@ protected: ...@@ -113,22 +110,22 @@ protected:
/* Internal Gcalc coordinates to provide the precise calculations */ /* Internal Gcalc coordinates to provide the precise calculations */
#define DIG_BASE 1000000000 #define GCALC_DIG_BASE 1000000000
typedef int32 coord_digit_t; typedef int32 gcalc_digit_t;
typedef long long coord2; typedef long long gcalc_coord2;
#define C_SCALE 1e13 #define GCALC_COORD_BASE 2
#define COORD_BASE 2
class Gcalc_internal_coord class Gcalc_internal_coord
{ {
public: public:
coord_digit_t *digits; gcalc_digit_t *digits;
int sign; int sign;
int n_digits; int n_digits;
void set_zero(); void set_zero();
int is_zero() const; int is_zero() const;
#ifdef GCALC_CHECK_WITH_FLOAT #ifdef GCALC_CHECK_WITH_FLOAT
static double *coord_extent;
long double get_double() const; long double get_double() const;
#endif /*GCALC_CHECK_WITH_FLOAT*/ #endif /*GCALC_CHECK_WITH_FLOAT*/
}; };
...@@ -136,25 +133,25 @@ public: ...@@ -136,25 +133,25 @@ public:
class Gcalc_coord1 : public Gcalc_internal_coord class Gcalc_coord1 : public Gcalc_internal_coord
{ {
coord_digit_t c[COORD_BASE]; gcalc_digit_t c[GCALC_COORD_BASE];
public: public:
void init() void init()
{ {
n_digits= COORD_BASE; n_digits= GCALC_COORD_BASE;
digits= c; digits= c;
} }
int set_double(double d); int set_double(double d, double ext);
void copy(const Gcalc_coord1 *from); void copy(const Gcalc_coord1 *from);
}; };
class Gcalc_coord2 : public Gcalc_internal_coord class Gcalc_coord2 : public Gcalc_internal_coord
{ {
coord_digit_t c[COORD_BASE*2]; gcalc_digit_t c[GCALC_COORD_BASE*2];
public: public:
void init() void init()
{ {
n_digits= COORD_BASE*2; n_digits= GCALC_COORD_BASE*2;
digits= c; digits= c;
} }
}; };
...@@ -235,12 +232,16 @@ public: ...@@ -235,12 +232,16 @@ public:
const Info *get_first() const { return (const Info *)m_first; } const Info *get_first() const { return (const Info *)m_first; }
Gcalc_dyn_list::Item **get_last_hook() { return m_hook; } Gcalc_dyn_list::Item **get_last_hook() { return m_hook; }
void reset(); void reset();
#ifdef GCALC_CHECK_WITH_FLOAT
long double get_double(const Gcalc_internal_coord *c) const;
#endif /*GCALC_CHECK_WITH_FLOAT*/
private: private:
Gcalc_dyn_list::Item *m_first; Gcalc_dyn_list::Item *m_first;
Gcalc_dyn_list::Item **m_hook; Gcalc_dyn_list::Item **m_hook;
int m_n_points; int m_n_points;
Intersection_info *m_first_intersection; Intersection_info *m_first_intersection;
Gcalc_dyn_list::Item **m_intersection_hook; Gcalc_dyn_list::Item **m_intersection_hook;
double coord_extent;
}; };
......
...@@ -1409,7 +1409,7 @@ int Gcalc_operation_reducer::get_result(Gcalc_result_receiver *storage) ...@@ -1409,7 +1409,7 @@ int Gcalc_operation_reducer::get_result(Gcalc_result_receiver *storage)
void Gcalc_operation_reducer::reset() void Gcalc_operation_reducer::reset()
{ {
free_list(m_result, m_res_hook); free_list((Gcalc_heap::Item **) &m_result, m_res_hook);
m_res_hook= (Gcalc_dyn_list::Item **)&m_result; m_res_hook= (Gcalc_dyn_list::Item **)&m_result;
free_list(m_first_active_thread); free_list(m_first_active_thread);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment