Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pybind11
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
pybind11
Commits
f2008325
Commit
f2008325
authored
Oct 15, 2020
by
Henry Schreiner
Committed by
Henry Schreiner
Oct 15, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
style: ssize_t -> py::ssize_t
parent
63f2deea
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
56 additions
and
56 deletions
+56
-56
docs/advanced/pycpp/numpy.rst
+10
-10
tests/test_buffers.cpp
+12
-12
tests/test_kwargs_and_defaults.cpp
+2
-2
tests/test_numpy_array.cpp
+24
-24
tests/test_numpy_dtypes.cpp
+4
-4
tests/test_numpy_vectorize.cpp
+2
-2
tests/test_pytypes.cpp
+1
-1
tests/test_sequences_and_iterators.cpp
+1
-1
No files found.
docs/advanced/pycpp/numpy.rst
View file @
f2008325
...
...
@@ -57,11 +57,11 @@ specification.
struct buffer_info {
void *ptr;
ssize_t itemsize;
py::
ssize_t itemsize;
std::string format;
ssize_t ndim;
std::vector<ssize_t> shape;
std::vector<ssize_t> strides;
py::
ssize_t ndim;
std::vector<
py::
ssize_t> shape;
std::vector<
py::
ssize_t> strides;
};
To create a C++ function that can take a Python buffer object as an argument,
...
...
@@ -309,17 +309,17 @@ where ``N`` gives the required dimensionality of the array:
m.def("sum_3d", [](py::array_t<double> x) {
auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable
double sum = 0;
for (ssize_t i = 0; i < r.shape(0); i++)
for (ssize_t j = 0; j < r.shape(1); j++)
for (ssize_t k = 0; k < r.shape(2); k++)
for (
py::
ssize_t i = 0; i < r.shape(0); i++)
for (
py::
ssize_t j = 0; j < r.shape(1); j++)
for (
py::
ssize_t k = 0; k < r.shape(2); k++)
sum += r(i, j, k);
return sum;
});
m.def("increment_3d", [](py::array_t<double> x) {
auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false
for (ssize_t i = 0; i < r.shape(0); i++)
for (ssize_t j = 0; j < r.shape(1); j++)
for (ssize_t k = 0; k < r.shape(2); k++)
for (
py::
ssize_t i = 0; i < r.shape(0); i++)
for (
py::
ssize_t j = 0; j < r.shape(1); j++)
for (
py::
ssize_t k = 0; k < r.shape(2); k++)
r(i, j, k) += 1.0;
}, py::arg().noconvert());
...
...
tests/test_buffers.cpp
View file @
f2008325
...
...
@@ -15,7 +15,7 @@ TEST_SUBMODULE(buffers, m) {
// test_from_python / test_to_python:
class
Matrix
{
public
:
Matrix
(
ssize_t
rows
,
ssize_t
cols
)
:
m_rows
(
rows
),
m_cols
(
cols
)
{
Matrix
(
py
::
ssize_t
rows
,
py
::
ssize_t
cols
)
:
m_rows
(
rows
),
m_cols
(
cols
)
{
print_created
(
this
,
std
::
to_string
(
m_rows
)
+
"x"
+
std
::
to_string
(
m_cols
)
+
" matrix"
);
m_data
=
new
float
[(
size_t
)
(
rows
*
cols
)];
memset
(
m_data
,
0
,
sizeof
(
float
)
*
(
size_t
)
(
rows
*
cols
));
...
...
@@ -59,25 +59,25 @@ TEST_SUBMODULE(buffers, m) {
return
*
this
;
}
float
operator
()(
ssize_t
i
,
ssize_t
j
)
const
{
float
operator
()(
py
::
ssize_t
i
,
py
::
ssize_t
j
)
const
{
return
m_data
[(
size_t
)
(
i
*
m_cols
+
j
)];
}
float
&
operator
()(
ssize_t
i
,
ssize_t
j
)
{
float
&
operator
()(
py
::
ssize_t
i
,
py
::
ssize_t
j
)
{
return
m_data
[(
size_t
)
(
i
*
m_cols
+
j
)];
}
float
*
data
()
{
return
m_data
;
}
ssize_t
rows
()
const
{
return
m_rows
;
}
ssize_t
cols
()
const
{
return
m_cols
;
}
py
::
ssize_t
rows
()
const
{
return
m_rows
;
}
py
::
ssize_t
cols
()
const
{
return
m_cols
;
}
private
:
ssize_t
m_rows
;
ssize_t
m_cols
;
py
::
ssize_t
m_rows
;
py
::
ssize_t
m_cols
;
float
*
m_data
;
};
py
::
class_
<
Matrix
>
(
m
,
"Matrix"
,
py
::
buffer_protocol
())
.
def
(
py
::
init
<
ssize_t
,
ssize_t
>
())
.
def
(
py
::
init
<
py
::
ssize_t
,
py
::
ssize_t
>
())
/// Construct from a buffer
.
def
(
py
::
init
([](
py
::
buffer
const
b
)
{
py
::
buffer_info
info
=
b
.
request
();
...
...
@@ -93,12 +93,12 @@ TEST_SUBMODULE(buffers, m) {
.
def
(
"cols"
,
&
Matrix
::
cols
)
/// Bare bones interface
.
def
(
"__getitem__"
,
[](
const
Matrix
&
m
,
std
::
pair
<
ssize_t
,
ssize_t
>
i
)
{
.
def
(
"__getitem__"
,
[](
const
Matrix
&
m
,
std
::
pair
<
py
::
ssize_t
,
py
::
ssize_t
>
i
)
{
if
(
i
.
first
>=
m
.
rows
()
||
i
.
second
>=
m
.
cols
())
throw
py
::
index_error
();
return
m
(
i
.
first
,
i
.
second
);
})
.
def
(
"__setitem__"
,
[](
Matrix
&
m
,
std
::
pair
<
ssize_t
,
ssize_t
>
i
,
float
v
)
{
.
def
(
"__setitem__"
,
[](
Matrix
&
m
,
std
::
pair
<
py
::
ssize_t
,
py
::
ssize_t
>
i
,
float
v
)
{
if
(
i
.
first
>=
m
.
rows
()
||
i
.
second
>=
m
.
cols
())
throw
py
::
index_error
();
m
(
i
.
first
,
i
.
second
)
=
v
;
...
...
@@ -118,11 +118,11 @@ TEST_SUBMODULE(buffers, m) {
// test_inherited_protocol
class
SquareMatrix
:
public
Matrix
{
public
:
SquareMatrix
(
ssize_t
n
)
:
Matrix
(
n
,
n
)
{
}
SquareMatrix
(
py
::
ssize_t
n
)
:
Matrix
(
n
,
n
)
{
}
};
// Derived classes inherit the buffer protocol and the buffer access function
py
::
class_
<
SquareMatrix
,
Matrix
>
(
m
,
"SquareMatrix"
)
.
def
(
py
::
init
<
ssize_t
>
());
.
def
(
py
::
init
<
py
::
ssize_t
>
());
// test_pointer_to_member_fn
...
...
tests/test_kwargs_and_defaults.cpp
View file @
f2008325
...
...
@@ -71,7 +71,7 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
py
::
tuple
t
(
a
.
size
());
for
(
size_t
i
=
0
;
i
<
a
.
size
();
i
++
)
// Use raw Python API here to avoid an extra, intermediate incref on the tuple item:
t
[
i
]
=
(
int
)
Py_REFCNT
(
PyTuple_GET_ITEM
(
a
.
ptr
(),
static_cast
<
ssize_t
>
(
i
)));
t
[
i
]
=
(
int
)
Py_REFCNT
(
PyTuple_GET_ITEM
(
a
.
ptr
(),
static_cast
<
py
::
ssize_t
>
(
i
)));
return
t
;
});
m
.
def
(
"mixed_args_refcount"
,
[](
py
::
object
o
,
py
::
args
a
)
{
...
...
@@ -80,7 +80,7 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
t
[
0
]
=
o
.
ref_count
();
for
(
size_t
i
=
0
;
i
<
a
.
size
();
i
++
)
// Use raw Python API here to avoid an extra, intermediate incref on the tuple item:
t
[
i
+
1
]
=
(
int
)
Py_REFCNT
(
PyTuple_GET_ITEM
(
a
.
ptr
(),
static_cast
<
ssize_t
>
(
i
)));
t
[
i
+
1
]
=
(
int
)
Py_REFCNT
(
PyTuple_GET_ITEM
(
a
.
ptr
(),
static_cast
<
py
::
ssize_t
>
(
i
)));
return
t
;
});
...
...
tests/test_numpy_array.cpp
View file @
f2008325
...
...
@@ -89,23 +89,23 @@ template<typename... Ix> arr data_t(const arr_t& a, Ix... index) {
template
<
typename
...
Ix
>
arr
&
mutate_data
(
arr
&
a
,
Ix
...
index
)
{
auto
ptr
=
(
uint8_t
*
)
a
.
mutable_data
(
index
...);
for
(
ssize_t
i
=
0
;
i
<
a
.
nbytes
()
-
a
.
offset_at
(
index
...);
i
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
a
.
nbytes
()
-
a
.
offset_at
(
index
...);
i
++
)
ptr
[
i
]
=
(
uint8_t
)
(
ptr
[
i
]
*
2
);
return
a
;
}
template
<
typename
...
Ix
>
arr_t
&
mutate_data_t
(
arr_t
&
a
,
Ix
...
index
)
{
auto
ptr
=
a
.
mutable_data
(
index
...);
for
(
ssize_t
i
=
0
;
i
<
a
.
size
()
-
a
.
index_at
(
index
...);
i
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
a
.
size
()
-
a
.
index_at
(
index
...);
i
++
)
ptr
[
i
]
++
;
return
a
;
}
template
<
typename
...
Ix
>
ssize_t
index_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
index_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
offset_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
offset_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
ssize_t
at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
index_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
index_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
index_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
offset_at
(
const
arr
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
offset_at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
offset_at
(
idx
...);
}
template
<
typename
...
Ix
>
py
::
ssize_t
at_t
(
const
arr_t
&
a
,
Ix
...
idx
)
{
return
a
.
at
(
idx
...);
}
template
<
typename
...
Ix
>
arr_t
&
mutate_at_t
(
arr_t
&
a
,
Ix
...
idx
)
{
a
.
mutable_at
(
idx
...)
++
;
return
a
;
}
#define def_index_fn(name, type) \
...
...
@@ -159,9 +159,9 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_array_attributes
sm
.
def
(
"ndim"
,
[](
const
arr
&
a
)
{
return
a
.
ndim
();
});
sm
.
def
(
"shape"
,
[](
const
arr
&
a
)
{
return
arr
(
a
.
ndim
(),
a
.
shape
());
});
sm
.
def
(
"shape"
,
[](
const
arr
&
a
,
ssize_t
dim
)
{
return
a
.
shape
(
dim
);
});
sm
.
def
(
"shape"
,
[](
const
arr
&
a
,
py
::
ssize_t
dim
)
{
return
a
.
shape
(
dim
);
});
sm
.
def
(
"strides"
,
[](
const
arr
&
a
)
{
return
arr
(
a
.
ndim
(),
a
.
strides
());
});
sm
.
def
(
"strides"
,
[](
const
arr
&
a
,
ssize_t
dim
)
{
return
a
.
strides
(
dim
);
});
sm
.
def
(
"strides"
,
[](
const
arr
&
a
,
py
::
ssize_t
dim
)
{
return
a
.
strides
(
dim
);
});
sm
.
def
(
"writeable"
,
[](
const
arr
&
a
)
{
return
a
.
writeable
();
});
sm
.
def
(
"size"
,
[](
const
arr
&
a
)
{
return
a
.
size
();
});
sm
.
def
(
"itemsize"
,
[](
const
arr
&
a
)
{
return
a
.
itemsize
();
});
...
...
@@ -281,33 +281,33 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_array_unchecked_fixed_dims
sm
.
def
(
"proxy_add2"
,
[](
py
::
array_t
<
double
>
a
,
double
v
)
{
auto
r
=
a
.
mutable_unchecked
<
2
>
();
for
(
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
py
::
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
r
(
i
,
j
)
+=
v
;
},
py
::
arg
().
noconvert
(),
py
::
arg
());
sm
.
def
(
"proxy_init3"
,
[](
double
start
)
{
py
::
array_t
<
double
,
py
::
array
::
c_style
>
a
({
3
,
3
,
3
});
auto
r
=
a
.
mutable_unchecked
<
3
>
();
for
(
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
ssize_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
py
::
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
py
::
ssize_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
r
(
i
,
j
,
k
)
=
start
++
;
return
a
;
});
sm
.
def
(
"proxy_init3F"
,
[](
double
start
)
{
py
::
array_t
<
double
,
py
::
array
::
f_style
>
a
({
3
,
3
,
3
});
auto
r
=
a
.
mutable_unchecked
<
3
>
();
for
(
ssize_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
py
::
ssize_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
py
::
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
r
(
i
,
j
,
k
)
=
start
++
;
return
a
;
});
sm
.
def
(
"proxy_squared_L2_norm"
,
[](
py
::
array_t
<
double
>
a
)
{
auto
r
=
a
.
unchecked
<
1
>
();
double
sumsq
=
0
;
for
(
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
sumsq
+=
r
[
i
]
*
r
(
i
);
// Either notation works for a 1D array
return
sumsq
;
});
...
...
@@ -335,17 +335,17 @@ TEST_SUBMODULE(numpy_array, sm) {
sm
.
def
(
"proxy_add2_dyn"
,
[](
py
::
array_t
<
double
>
a
,
double
v
)
{
auto
r
=
a
.
mutable_unchecked
();
if
(
r
.
ndim
()
!=
2
)
throw
std
::
domain_error
(
"error: ndim != 2"
);
for
(
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
py
::
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
r
(
i
,
j
)
+=
v
;
},
py
::
arg
().
noconvert
(),
py
::
arg
());
sm
.
def
(
"proxy_init3_dyn"
,
[](
double
start
)
{
py
::
array_t
<
double
,
py
::
array
::
c_style
>
a
({
3
,
3
,
3
});
auto
r
=
a
.
mutable_unchecked
();
if
(
r
.
ndim
()
!=
3
)
throw
std
::
domain_error
(
"error: ndim != 3"
);
for
(
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
ssize_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
r
.
shape
(
0
);
i
++
)
for
(
py
::
ssize_t
j
=
0
;
j
<
r
.
shape
(
1
);
j
++
)
for
(
py
::
ssize_t
k
=
0
;
k
<
r
.
shape
(
2
);
k
++
)
r
(
i
,
j
,
k
)
=
start
++
;
return
a
;
});
...
...
@@ -374,7 +374,7 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_array_resize
// reshape array to 2D without changing size
sm
.
def
(
"array_reshape2"
,
[](
py
::
array_t
<
double
>
a
)
{
const
auto
dim_sz
=
(
ssize_t
)
std
::
sqrt
(
a
.
size
());
const
auto
dim_sz
=
(
py
::
ssize_t
)
std
::
sqrt
(
a
.
size
());
if
(
dim_sz
*
dim_sz
!=
a
.
size
())
throw
std
::
domain_error
(
"array_reshape2: input array total size is not a squared integer"
);
a
.
resize
({
dim_sz
,
dim_sz
});
...
...
tests/test_numpy_dtypes.cpp
View file @
f2008325
...
...
@@ -168,7 +168,7 @@ py::list print_recarray(py::array_t<S, 0> arr) {
const
auto
req
=
arr
.
request
();
const
auto
ptr
=
static_cast
<
S
*>
(
req
.
ptr
);
auto
l
=
py
::
list
();
for
(
ssize_t
i
=
0
;
i
<
req
.
size
;
i
++
)
{
for
(
py
::
ssize_t
i
=
0
;
i
<
req
.
size
;
i
++
)
{
std
::
stringstream
ss
;
ss
<<
ptr
[
i
];
l
.
append
(
py
::
str
(
ss
.
str
()));
...
...
@@ -180,8 +180,8 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
using
arr_t
=
py
::
array_t
<
int32_t
,
0
>
;
std
::
vector
<
int32_t
>
data
{
1
,
2
,
3
,
4
,
5
,
6
};
std
::
vector
<
ssize_t
>
shape
{
3
,
2
};
std
::
vector
<
ssize_t
>
strides
{
8
,
4
};
std
::
vector
<
py
::
ssize_t
>
shape
{
3
,
2
};
std
::
vector
<
py
::
ssize_t
>
strides
{
8
,
4
};
auto
ptr
=
data
.
data
();
auto
vptr
=
(
void
*
)
ptr
;
...
...
@@ -398,7 +398,7 @@ TEST_SUBMODULE(numpy_dtypes, m) {
if
(
non_empty
)
{
auto
req
=
arr
.
request
();
auto
ptr
=
static_cast
<
StringStruct
*>
(
req
.
ptr
);
for
(
ssize_t
i
=
0
;
i
<
req
.
size
*
req
.
itemsize
;
i
++
)
for
(
py
::
ssize_t
i
=
0
;
i
<
req
.
size
*
req
.
itemsize
;
i
++
)
static_cast
<
char
*>
(
req
.
ptr
)[
i
]
=
0
;
ptr
[
1
].
a
[
0
]
=
'a'
;
ptr
[
1
].
b
[
0
]
=
'a'
;
ptr
[
2
].
a
[
0
]
=
'a'
;
ptr
[
2
].
b
[
0
]
=
'a'
;
...
...
tests/test_numpy_vectorize.cpp
View file @
f2008325
...
...
@@ -83,8 +83,8 @@ TEST_SUBMODULE(numpy_vectorize, m) {
py
::
array_t
<
float
,
py
::
array
::
forcecast
>
arg2
,
py
::
array_t
<
double
,
py
::
array
::
forcecast
>
arg3
)
{
ssize_t
ndim
;
std
::
vector
<
ssize_t
>
shape
;
py
::
ssize_t
ndim
;
std
::
vector
<
py
::
ssize_t
>
shape
;
std
::
array
<
py
::
buffer_info
,
3
>
buffers
{{
arg1
.
request
(),
arg2
.
request
(),
arg3
.
request
()
}};
return
py
::
detail
::
broadcast
(
buffers
,
ndim
,
shape
);
});
...
...
tests/test_pytypes.cpp
View file @
f2008325
...
...
@@ -404,7 +404,7 @@ TEST_SUBMODULE(pytypes, m) {
m
.
def
(
"test_memoryview_from_memory"
,
[]()
{
const
char
*
buf
=
"
\xff\xe1\xab\x37
"
;
return
py
::
memoryview
::
from_memory
(
buf
,
static_cast
<
ssize_t
>
(
strlen
(
buf
)));
buf
,
static_cast
<
py
::
ssize_t
>
(
strlen
(
buf
)));
});
#endif
...
...
tests/test_sequences_and_iterators.cpp
View file @
f2008325
...
...
@@ -83,7 +83,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
py
::
class_
<
Sliceable
>
(
m
,
"Sliceable"
)
.
def
(
py
::
init
<
int
>
())
.
def
(
"__getitem__"
,[](
const
Sliceable
&
s
,
py
::
slice
slice
)
{
ssize_t
start
,
stop
,
step
,
slicelength
;
py
::
ssize_t
start
,
stop
,
step
,
slicelength
;
if
(
!
slice
.
compute
(
s
.
size
,
&
start
,
&
stop
,
&
step
,
&
slicelength
))
throw
py
::
error_already_set
();
int
istart
=
static_cast
<
int
>
(
start
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment