Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
bob
bob.math
Commits
81753eaf
Commit
81753eaf
authored
Mar 24, 2014
by
Manuel Günther
Browse files
Made documentation more beautiful using the novel xbob.extension/documentation.h
parent
8216e504
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
buildout.cfg
View file @
81753eaf
...
...
@@ -21,6 +21,7 @@ prefixes = /idiap/group/torch5spro/nightlies/last/bob/linux-x86_64-release
[sources]
xbob.extension = git https://github.com/bioidiap/xbob.extension branch=prototype
xbob.blitz = git https://github.com/bioidiap/xbob.blitz
numpydoc = git git@github.com:numpy/numpydoc.git
[scripts]
recipe = xbob.buildout:scripts
doc/conf.py
View file @
81753eaf
...
...
@@ -31,8 +31,14 @@ extensions = [
'sphinx.ext.autosummary'
,
'sphinx.ext.doctest'
,
'sphinx.ext.intersphinx'
,
'numpydoc'
,
]
# See: https://github.com/phn/pytpm/issues/3
numpydoc_show_class_members
=
False
# See... figured out myself :-(
numpydoc_class_members_toctree
=
False
# The viewcode extension appeared only on Sphinx >= 1.0.0
import
sphinx
if
sphinx
.
__version__
>=
"1.0"
:
...
...
@@ -143,7 +149,7 @@ html_favicon = ''
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path
=
[
'_static'
]
#
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
...
...
@@ -250,6 +256,7 @@ autodoc_member_order = 'bysource'
autodoc_default_flags
=
[
'members'
,
'undoc-members'
,
'private-members'
,
'inherited-members'
,
'show-inheritance'
,
]
...
...
doc/index.rst
View file @
81753eaf
...
...
@@ -28,4 +28,9 @@ Indices and tables
* :ref:`modindex`
* :ref:`search`
To-Do list
----------
.. todolist::
.. include:: links.rst
doc/py_api.rst
View file @
81753eaf
...
...
@@ -8,5 +8,17 @@
This section includes information for using the pure Python API of ``xbob.math``.
Summary
.......
.. autosummary::
xbob.math.LPInteriorPoint
xbob.math.LPInteriorPointShortstep
xbob.math.LPInteriorPointLongstep
Details
.......
.. automodule:: xbob.math
:members:
setup.py
View file @
81753eaf
...
...
@@ -4,8 +4,9 @@
# Mon 16 Apr 08:18:08 2012 CEST
from
setuptools
import
setup
,
find_packages
,
dist
dist
.
Distribution
(
dict
(
setup_requires
=
[
'xbob.blitz'
]))
dist
.
Distribution
(
dict
(
setup_requires
=
[
'xbob.blitz'
,
'xbob.extension'
]))
from
xbob.blitz.extension
import
Extension
import
xbob.extension
import
os
package_dir
=
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
...
...
@@ -33,6 +34,8 @@ setup(
install_requires
=
[
'setuptools'
,
'xbob.blitz'
,
'xbob.extension'
,
'numpydoc'
,
],
namespace_packages
=
[
...
...
@@ -61,8 +64,9 @@ setup(
packages
=
packages
,
version
=
version
,
include_dirs
=
include_dirs
,
),
],
# define_macros = [('XBOB_SHORT_DOCSTRINGS',1)],
),
],
entry_points
=
{
},
...
...
xbob/math/__init__.py
View file @
81753eaf
from
._library
import
__version__
from
._library
import
*
# To get the full API documentation automatically
__all__
=
dir
()
def
get_config
():
"""Returns a string containing the configuration information.
"""
...
...
@@ -21,4 +25,4 @@ def get_config():
return
retval
.
strip
()
# gets sphinx autodoc done right - don't remove it
__all__
=
[
_
for
_
in
dir
()
if
not
_
.
startswith
(
'_'
)]
__all__
=
[
_
for
_
in
dir
()
if
not
_
.
startswith
(
'_'
)]
\ No newline at end of file
xbob/math/histogram.cpp
View file @
81753eaf
/**
* @author Manuel Guenther <Manuel.Guenther@idiap.ch>
* @author Andre Anjos <andre.anjos@idiap.ch>
* @date Tue 3 Dec 14:23:42 2013 CET
* @date Tue 3 Dec 14:23:42 2013 CET
*
* @brief Binds fast versions of some histogram measures
*
...
...
@@ -89,45 +89,45 @@ static PyObject* py_histogram_intersection_1
}
template
<
typename
T1
>
PyObject
*
py_histogram_intersection_2_inner
(
PyBlitzArrayObject
*
index1
,
PyBlitzArrayObject
*
value1
,
PyBlitzArrayObject
*
index1
,
PyBlitzArrayObject
*
value1
,
PyBlitzArrayObject
*
index2
,
PyBlitzArrayObject
*
value2
)
{
switch
(
value1
->
type_num
)
{
case
NPY_UINT8
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
histogram_intersection
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value2
)));
case
NPY_UINT16
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
histogram_intersection
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value2
)));
case
NPY_INT32
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
histogram_intersection
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value2
)));
case
NPY_INT64
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
histogram_intersection
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value2
)));
case
NPY_FLOAT64
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
histogram_intersection
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value2
)));
default:
break
;
...
...
@@ -151,10 +151,10 @@ static PyObject* py_histogram_intersection_2(PyObject*, PyObject* args, PyObject
PyBlitzArrayObject
*
value2
=
0
;
if
(
!
PyArg_ParseTupleAndKeywords
(
args
,
kwds
,
"O&O&O&O&"
,
kwlist
,
&
PyBlitzArray_Converter
,
&
index1
,
kwlist
,
&
PyBlitzArray_Converter
,
&
index1
,
&
PyBlitzArray_Converter
,
&
value1
,
&
PyBlitzArray_Converter
,
&
index2
,
&
PyBlitzArray_Converter
,
&
index2
,
&
PyBlitzArray_Converter
,
&
value2
))
return
0
;
...
...
@@ -176,7 +176,7 @@ static PyObject* py_histogram_intersection_2(PyObject*, PyObject* args, PyObject
}
// input arrays must be 1d
if
(
index1
->
ndim
!=
1
||
index2
->
ndim
!=
1
||
if
(
index1
->
ndim
!=
1
||
index2
->
ndim
!=
1
||
value1
->
ndim
!=
1
||
value2
->
ndim
!=
1
)
{
PyErr_SetString
(
PyExc_TypeError
,
"all input arrays must be 1D"
);
return
0
;
...
...
@@ -237,7 +237,7 @@ static PyObject* py_histogram_intersection_2(PyObject*, PyObject* args, PyObject
* Note: Dispatcher function.
*/
PyObject
*
py_histogram_intersection
(
PyObject
*
,
PyObject
*
args
,
PyObject
*
kwargs
)
{
Py_ssize_t
nargs
=
args
?
PyTuple_Size
(
args
)
:
0
+
kwargs
?
PyDict_Size
(
kwargs
)
:
0
;
PyObject
*
retval
=
0
;
...
...
@@ -325,45 +325,45 @@ static PyObject* py_chi_square_1
}
template
<
typename
T1
>
PyObject
*
py_chi_square_2_inner
(
PyBlitzArrayObject
*
index1
,
PyBlitzArrayObject
*
value1
,
PyBlitzArrayObject
*
index1
,
PyBlitzArrayObject
*
value1
,
PyBlitzArrayObject
*
index2
,
PyBlitzArrayObject
*
value2
)
{
switch
(
value1
->
type_num
)
{
case
NPY_UINT8
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
chi_square
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value2
)));
case
NPY_UINT16
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
chi_square
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value2
)));
case
NPY_INT32
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
chi_square
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value2
)));
case
NPY_INT64
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
chi_square
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value2
)));
case
NPY_FLOAT64
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
chi_square
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value2
)));
default:
break
;
...
...
@@ -387,10 +387,10 @@ static PyObject* py_chi_square_2(PyObject*, PyObject* args, PyObject* kwds) {
PyBlitzArrayObject
*
value2
=
0
;
if
(
!
PyArg_ParseTupleAndKeywords
(
args
,
kwds
,
"O&O&O&O&"
,
kwlist
,
&
PyBlitzArray_Converter
,
&
index1
,
kwlist
,
&
PyBlitzArray_Converter
,
&
index1
,
&
PyBlitzArray_Converter
,
&
value1
,
&
PyBlitzArray_Converter
,
&
index2
,
&
PyBlitzArray_Converter
,
&
index2
,
&
PyBlitzArray_Converter
,
&
value2
))
return
0
;
...
...
@@ -412,7 +412,7 @@ static PyObject* py_chi_square_2(PyObject*, PyObject* args, PyObject* kwds) {
}
// input arrays must be 1d
if
(
index1
->
ndim
!=
1
||
index2
->
ndim
!=
1
||
if
(
index1
->
ndim
!=
1
||
index2
->
ndim
!=
1
||
value1
->
ndim
!=
1
||
value2
->
ndim
!=
1
)
{
PyErr_SetString
(
PyExc_TypeError
,
"all input arrays must be 1D"
);
return
0
;
...
...
@@ -549,45 +549,45 @@ static PyObject* py_kullback_leibler_1
}
template
<
typename
T1
>
PyObject
*
py_kullback_leibler_2_inner
(
PyBlitzArrayObject
*
index1
,
PyBlitzArrayObject
*
value1
,
PyBlitzArrayObject
*
index1
,
PyBlitzArrayObject
*
value1
,
PyBlitzArrayObject
*
index2
,
PyBlitzArrayObject
*
value2
)
{
switch
(
value1
->
type_num
)
{
case
NPY_UINT8
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
kullback_leibler
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint8_t
,
1
>
(
value2
)));
case
NPY_UINT16
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
kullback_leibler
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
uint16_t
,
1
>
(
value2
)));
case
NPY_INT32
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
kullback_leibler
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int32_t
,
1
>
(
value2
)));
case
NPY_INT64
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
kullback_leibler
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
int64_t
,
1
>
(
value2
)));
case
NPY_FLOAT64
:
return
PyBlitzArrayCxx_FromCScalar
(
bob
::
math
::
kullback_leibler
(
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value2
)));
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index1
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value1
),
*
PyBlitzArrayCxx_AsBlitz
<
T1
,
1
>
(
index2
),
*
PyBlitzArrayCxx_AsBlitz
<
double
,
1
>
(
value2
)));
default:
break
;
...
...
@@ -611,10 +611,10 @@ static PyObject* py_kullback_leibler_2(PyObject*, PyObject* args, PyObject* kwds
PyBlitzArrayObject
*
value2
=
0
;
if
(
!
PyArg_ParseTupleAndKeywords
(
args
,
kwds
,
"O&O&O&O&"
,
kwlist
,
&
PyBlitzArray_Converter
,
&
index1
,
kwlist
,
&
PyBlitzArray_Converter
,
&
index1
,
&
PyBlitzArray_Converter
,
&
value1
,
&
PyBlitzArray_Converter
,
&
index2
,
&
PyBlitzArray_Converter
,
&
index2
,
&
PyBlitzArray_Converter
,
&
value2
))
return
0
;
...
...
@@ -636,7 +636,7 @@ static PyObject* py_kullback_leibler_2(PyObject*, PyObject* args, PyObject* kwds
}
// input arrays must be 1d
if
(
index1
->
ndim
!=
1
||
index2
->
ndim
!=
1
||
if
(
index1
->
ndim
!=
1
||
index2
->
ndim
!=
1
||
value1
->
ndim
!=
1
||
value2
->
ndim
!=
1
)
{
PyErr_SetString
(
PyExc_TypeError
,
"all input arrays must be 1D"
);
return
0
;
...
...
xbob/math/lp_interior_point.cpp
View file @
81753eaf
This diff is collapsed.
Click to expand it.
xbob/math/main.cpp
View file @
81753eaf
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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