Commit Graph

687 Commits (release/4.3a0)

Author SHA1 Message Date
Fan Jiang bb479c2e38 Merging 'master' into 'wrap' 2021-02-15 19:43:28 -05:00
Varun Agrawal 45bbf3d332 Merging 'master' into 'wrap' 2021-01-13 10:54:22 -05:00
Varun Agrawal 9e003715af Merging 'master' into 'wrap' 2021-01-12 20:45:43 -05:00
Varun Agrawal 6309917fdf add docs for cmake update 2021-01-07 11:17:54 -05:00
Varun Agrawal bd9d501269 use older form of CMake install 2021-01-06 17:58:46 -05:00
Varun Agrawal 4945642481 Merging 'master' into 'wrap' 2021-01-04 13:11:36 -05:00
Varun Agrawal acd6fff562 minor typo fixes 2020-11-07 17:28:50 -05:00
Fan Jiang 8d6e689ac1 Merging 'master' into 'wrap' 2020-10-07 02:29:41 -04:00
Fan Jiang 2c51730dbb Merging 'master' into 'wrap' 2020-10-07 01:42:57 -04:00
Varun Agrawal aa173ac230 Merging 'master' into 'wrap' 2020-09-25 20:59:12 -04:00
Varun Agrawal 99e2f554ed Merging 'master' into 'wrap' 2020-09-16 18:03:26 -04:00
Fan Jiang 28fc35af3c Merging 'master' into 'wrap' 2020-08-17 17:17:56 -04:00
Fan Jiang 7f8371aad7 Merging 'master' into 'wrap' 2020-08-17 14:44:43 -04:00
Fan Jiang c7eb02969a Remove old wrap 2020-08-17 14:44:14 -04:00
Jose Luis Blanco Claraco 8165291704
Fix warnings on incorrect for range reference bindings 2020-07-27 00:14:18 +02:00
Jose Luis Blanco Claraco 0198c648e3
Fix all new gcc warnings/errors: make explicit virtual/override methods.
Rules are:
- use "virtual" in base classes only.
- use "override" in all derived classes.
2020-07-26 11:20:42 +02:00
Fan Jiang 693253f376 Fix wrap tests 2020-06-22 15:24:14 -04:00
Fan Jiang 37673771ae Fixed all alignment problems 2020-06-22 15:24:14 -04:00
Frank Dellaert 19c12c8f8f
Merge pull request #350 from borglab/temporary/pybind_add
Add Pybind to develop
2020-06-16 15:24:54 -04:00
Fan Jiang 26880023d0 Merge commit 'cf76820cab' into temporary/add_pybind 2020-06-11 11:27:13 -04:00
Varun Agrawal 083569f97d add full namespace global function wrapping 2020-06-10 11:44:03 -05:00
Bernd Pfrommer 71f4bb3019 Use GNUInstallDirs to make install destination directories configurable 2020-05-31 21:52:00 -04:00
Frank Dellaert 2087075ee7 Transitioned toa method to a functor 2020-03-17 14:34:11 -04:00
Fan Jiang cf76820cab Add pybind11 2020-01-30 12:27:48 -05:00
Jose Luis Blanco-Claraco b10963802c
Revert "Fix cmake handling newer boost versions (Closes: #442)"
This reverts commit a0fce4257f.
2019-10-07 11:21:22 +02:00
Varun Agrawal 8632303157 updated tests to work with new wrap code generation 2019-07-10 15:56:32 -04:00
Varun Agrawal 090994a1a3 propagate exceptions from global functions from cpp to python 2019-07-10 15:56:07 -04:00
Jose Luis Blanco Claraco 7f43054c37 Refactor build flags via CMake target properties
Also:
- Allow users to edit cmake target build options in the cache variables.
- We had to add project() commands for  gtsam and gtsam_unstable,
the PROJECT_SOURCE_DIR changed, but the root GTSAM_SOURCE_DIR instead.
- Ensure use of standard C++11 (no extensions)
2019-06-15 23:09:54 +02:00
Jose Luis Blanco-Claraco ae79c27d45 fix unused parameter warning 2019-06-15 23:09:10 +02:00
Frank Dellaert da09428110
Merge pull request #67 from borglab/feature/housekeeping
Housekeeping
2019-06-15 13:53:02 -04:00
Frank Dellaert ba91bd53fd Add better error reporting 2019-06-15 11:12:19 -04:00
Varun Agrawal b8292399d6 renamed all READMEs to README.md and updated markdown syntax 2019-06-13 17:26:07 -04:00
Duy-Nguyen Ta 9502c71dca Merged in thduynguyen/gtsam-duy/fix/trailing_whitespaces (pull request #381)
Remove trailing whitespaces
2019-05-15 00:37:15 +00:00
Duy-Nguyen Ta 74e7c6485b Fix wrap output when GTSAM_WRAP_SERIALIZATION is OFF. 2019-05-14 11:34:07 -04:00
Varun Agrawal 39812444b2 fix for issue #454 2019-05-13 19:24:25 -04:00
Duy-Nguyen Ta b2e5dadcdb Merge branch 'develop' of https://bitbucket.org/gtborg/gtsam into fix/trailing_whitespaces
# Conflicts:
#	gtsam/symbolic/tests/testVariableIndex.cpp
#	gtsam_unstable/gtsam_unstable.h
#	gtsam_unstable/linear/RawQP.cpp
#	gtsam_unstable/linear/RawQP.h
2019-05-12 09:26:28 -04:00
jlblancoc a0fce4257f Fix cmake handling newer boost versions (Closes: #442) 2019-04-08 10:22:59 +02:00
Frank Dellaert aa8b40b594 Got rid of some obsolete methods/arguments 2019-03-19 12:42:40 -04:00
Frank Dellaert 40051a6226 New expected files after no more Shared 2019-03-19 12:42:09 -04:00
Frank Dellaert 98ed4d7850 Only create typedef to SharedXXX where really needed. 2019-03-19 12:09:00 -04:00
Matthew Broadway 8df2c0a9a1 updated wrap test expected output 2019-02-28 09:14:39 +00:00
Matthew Broadway 70470ff59b fixed more python 3 related import problems 2019-02-28 09:14:39 +00:00
Matthew Broadway 5670c73158 improved cython wrapper python3 support 2019-02-28 09:14:39 +00:00
jlblancoc 7625c21777
Various fixes to cmake exported targets
List of changes:
 * -I boost is no longer required
   (Since the use of Boost::xxx imported targets)
 * fix missing Boost deps in imported gtsam by
   searching for Boost inside GTSAMConfig.cmake
 * Including the dirs for Eigen/MKL/SuiteSparse/Metis
   into exported targets public interface.
 * Fix missing cmake changes in wrap/*
 * Split build flags into private/public, not to
   expose to users flags that may be invasive.
 * Removed now useless include_dirs in "extra cmake"
 * Update cmake/example_project
 * Make cppunitlite to find boost headers via Boost::boost
 * Update README / INSTALL to reflect the updated minimum CMake >= 3.0
2019-02-15 22:04:04 +01:00
Duy-Nguyen Ta 1cdc228d6a remove trailing spaces 2019-02-11 10:58:34 -05:00
Frank Dellaert edb94a6e93 Better error message. 2019-01-20 16:52:44 -05:00
Frank Dellaert fbcfbf0cdd Made naming convention in wrapper uniform.
2D means Pose2 + Point2
3D means Pose3 + Point3
2018-12-31 11:19:46 -05:00
Frank Dellaert fe1daec086 Changed include error as in http://boost.2283326.n4.nabble.com/boost-serialization-Serializing-Dynamically-Loaded-Libraries-quot-Unregistered-Void-Cast-quot-td2570981.html 2018-12-30 16:24:19 -05:00
Frank Dellaert 70a9bbc404 Fix testWrap failure after Duy change 2018-10-31 12:51:30 +00:00
Duy-Nguyen Ta d802255eb3 support pythonic print for gtsam objects. 2018-10-17 05:40:57 -04:00
Frank Dellaert b9f080456c Catch exception by value 2018-10-08 22:54:48 -04:00
Frank Dellaert 9c3949f738 Added virtual destructors 2018-09-27 00:23:17 -04:00
Frank Dellaert 61241ee9ff fixed expected wrapper file 2017-12-02 22:14:56 -08:00
Frank Dellaert 4188a739ec Fixed overloaded methods/constructors 2017-12-02 18:43:18 -08:00
Duy-Nguyen Ta 7ecdbd5908 update expected result for cython wrap test 2017-08-28 14:12:15 -04:00
Duy-Nguyen Ta d23b5e4cfd only catch AssertionError exceptions when handling overloads, so that other C++ exceptions can be raised correctly 2017-08-25 11:18:16 -07:00
Duy-Nguyen Ta da5d3e303c update expected outputs for cython wrapper unittests 2017-08-15 13:46:32 -04:00
dellaert c5a0f1a839 Undo unrelated changes 2017-08-06 17:42:20 -07:00
dellaert 3bbea0f301 Make pxd a bit nicer to read 2017-08-06 17:26:12 -07:00
dellaert 905aac29f8 Only define as many return values as needed 2017-08-06 17:15:47 -07:00
dellaert 81c15d950a Removed kwargs overhead for overloaded methods 2017-08-06 16:53:04 -07:00
dellaert d752c9e249 Re-wrote constructor overloading logic which saves a lot of overhead 2017-08-06 14:58:23 -07:00
dellaert 2374347e69 Make internal, overloaded static methods cdefs to avoid call code/overhead 2017-08-06 13:25:54 -07:00
dellaert 6068166d2b Moved wrapper class signature to Class.cpp 2017-08-06 12:21:26 -07:00
dellaert 74a33ff222 Re-structured argument overloading to call a common function 2017-08-06 11:07:13 -07:00
Duy-Nguyen Ta 82531c561f clonedEigency --> gtsam_eigency. Update readme. 2017-07-28 15:26:19 -04:00
Duy-Nguyen Ta 5ff6a4e397 update expected cython wrap test output 2017-07-27 22:32:53 -04:00
Duy-Nguyen Ta c0dd740d12 correct indentation for __str__ 2017-07-27 22:32:27 -04:00
Duy-Nguyen Ta 742097aed0 eigency --> clonedEigency. Fixing bugs and improve eigency build. 2017-07-27 22:26:53 -04:00
Duy-Nguyen Ta b1071b08a0 redirect stdcout to a stringstream so Python __str__() can properly return a string after calling the c++ print function
This is to avoid a printing issue in some Python's IDE when __str__() is called to update objects' values in Debug mode.
2017-07-25 16:32:26 -04:00
dellaert c8dec5d8dc Small changes in comments and docs 2017-05-20 12:23:41 -07:00
Duy-Nguyen Ta a8d363c347 update expected pyx 2017-03-21 03:52:01 -04:00
Duy-Nguyen Ta 869dc811b0 graceful dynamic cast failures 2017-03-21 02:34:04 -04:00
Duy-Nguyen Ta e624b6fe72 don't change matlab's generated filename (gtsam_wrapper), only cmake targets (to gtsam_matlab_wrapper) 2017-03-18 22:01:24 -04:00
Duy-Nguyen Ta 0da506b3a9 change gtsam_wrapper --> gtsam_matlab_wrapper 2017-03-18 20:32:25 -04:00
Duy-Nguyen Ta ee75faa0df test cython wrapper's generated files 2017-03-18 18:35:28 -04:00
Duy-Nguyen Ta e3918da95c update test to comply with a cython wrapper's requirement: need an include for every class. 2017-03-18 18:33:01 -04:00
Duy-Nguyen Ta 2146aa140c default value for cython extra imports 2017-03-18 18:30:44 -04:00
Duy-Nguyen Ta 07b1bbfe7f remove namespace requirement for cython wrapper
Only for unittesting wrap geometry.h, not yet tested in real python/cython
2017-03-18 18:29:53 -04:00
Duy-Nguyen Ta 42deeb7bf0 fix/update matlab wrapper tests when wrap serialization option is off 2017-03-18 18:26:21 -04:00
Duy-Nguyen Ta 16a1643d17 gracefully rasing exception when trying to create obj of a class with no constructor 2017-03-15 22:47:14 -04:00
Duy-Nguyen Ta 52f54d07bd remove blank lines 2017-03-15 22:45:48 -04:00
Duy-Nguyen Ta c52f54221e update testWrap to call new function names 2017-03-15 17:03:13 -04:00
Duy-Nguyen Ta 6bf7ea23cf convert numpy input params to dtype float and order 'F' automatically
using numpy.astype(...). No copy if the params are already in the correct dtype and storage order.

For a function
    f(Matrix A, Matrix B),
simply wrapping it to pyx as
   f(A.astype(float, order='F', copy=False), B.astype(float, order='F', copy=False))
won't work.
It produces a strange side-effect that the content of A is overwritten by B and the two inputs are the same (data address) inside the function!

This is because Cython decreases the ref count for the temporary variable resulted from A.astype(...) before generates the wrap for B.astype(...).
Hence, the A.astype temp var is probably reused for B.astype, and they were pointing to the same data address.

For that reason, we have to go a longer route and wrap it as:
  A = A.astype(float, order='F', copy=False)
  B = B.astype(float, order='F', copy=False)
  f(A, B)

For future ref., here is a sample of the wrongly generated code that wraps the JacobianFactor constructor:
Jacobian(Key i1, Matrix A1, Key i2, Matrix A2, Vector b, noiseModel::Diagonal model)

Wrongly wrapped pyx code:
self.shared_CJacobianFactor_ = shared_ptr[CJacobianFactor](new CJacobianFactor(i1, <MatrixXd>(Map[MatrixXd](A1.astype(float, order='F',copy=False)), i2, <MatrixXd>(Map[MatrixXd](A2.astype(float, order='F', copy=False)), <VectorXd>(Map[VectorXd](b.astype(float, order='F', copy=False))), model.shared_CnoiseModel_Diagonal_))

The problematic Cython generated CPP code with a comment on the problematic line:
/////////////////////////////////////////
// WRONG VERSION
/////////////////////////////////////////
  __pyx_t_12 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_A1), __pyx_n_s_astype); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_12);
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_INCREF(((PyObject *)(&PyFloat_Type)));
  __Pyx_GIVEREF(((PyObject *)(&PyFloat_Type)));
  PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)(&PyFloat_Type)));
  __pyx_t_5 = PyDict_New(); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_5);
  if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_order, __pyx_n_s_F) < 0) __PYX_ERR(0, 2107, __pyx_L1_error)
  if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_copy, Py_False) < 0) __PYX_ERR(0, 2107, __pyx_L1_error)
  __pyx_t_13 = __Pyx_PyObject_Call(__pyx_t_12, __pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_13);
  __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_13) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_13, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 2107, __pyx_L1_error)
  try {
    __pyx_t_14 = eigency::Map<Eigen::MatrixXd> (((PyArrayObject *)__pyx_t_13));
  } catch(...) {
    __Pyx_CppExn2PyErr();
    __PYX_ERR(0, 2107, __pyx_L1_error)
  }
///////////////////////////////////////////////
  __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;  	//<------- Problematic line!!! Killing this will result in the correct result!
///////////////////////////////////////////////
  __pyx_t_13 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_A2), __pyx_n_s_astype); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_13);
  __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_INCREF(((PyObject *)(&PyFloat_Type)));
  __Pyx_GIVEREF(((PyObject *)(&PyFloat_Type)));
  PyTuple_SET_ITEM(__pyx_t_5, 0, ((PyObject *)(&PyFloat_Type)));
  __pyx_t_4 = PyDict_New(); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_order, __pyx_n_s_F) < 0) __PYX_ERR(0, 2107, __pyx_L1_error)
  if (PyDict_SetItem(__pyx_t_4, __pyx_n_s_copy, Py_False) < 0) __PYX_ERR(0, 2107, __pyx_L1_error)
  __pyx_t_12 = __Pyx_PyObject_Call(__pyx_t_13, __pyx_t_5, __pyx_t_4); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_12);
  __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  if (!(likely(((__pyx_t_12) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_12, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 2107, __pyx_L1_error)
  try {
    __pyx_t_15 = eigency::Map<Eigen::MatrixXd> (((PyArrayObject *)__pyx_t_12));
  } catch(...) {
    __Pyx_CppExn2PyErr();
    __PYX_ERR(0, 2107, __pyx_L1_error)
  }
  __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
  __pyx_t_12 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_b), __pyx_n_s_astype); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_12);
  __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __Pyx_INCREF(((PyObject *)(&PyFloat_Type)));
  __Pyx_GIVEREF(((PyObject *)(&PyFloat_Type)));
  PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)(&PyFloat_Type)));
  __pyx_t_5 = PyDict_New(); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_5);
  if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_order, __pyx_n_s_F) < 0) __PYX_ERR(0, 2107, __pyx_L1_error)
  if (PyDict_SetItem(__pyx_t_5, __pyx_n_s_copy, Py_False) < 0) __PYX_ERR(0, 2107, __pyx_L1_error)
  __pyx_t_13 = __Pyx_PyObject_Call(__pyx_t_12, __pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 2107, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_13);
  __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (!(likely(((__pyx_t_13) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_13, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 2107, __pyx_L1_error)
  try {
    __pyx_t_16 = eigency::Map<Eigen::VectorXd> (((PyArrayObject *)__pyx_t_13));
  } catch(...) {
    __Pyx_CppExn2PyErr();
    __PYX_ERR(0, 2107, __pyx_L1_error)
  }
  __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
  try {
    __pyx_t_17 = new gtsam::JacobianFactor(__pyx_v_i1, ((Eigen::MatrixXd)__pyx_t_14), __pyx_v_i2, ((Eigen::MatrixXd)__pyx_t_15), ((Eigen::VectorXd)__pyx_t_16), __pyx_v_model->shared_CnoiseModel_Diagonal_);
  } catch(...) {
    __Pyx_CppExn2PyErr();
    __PYX_ERR(0, 2107, __pyx_L1_error)
  }
  __pyx_v_self->shared_CJacobianFactor_ = boost::shared_ptr<gtsam::JacobianFactor> (__pyx_t_17);
2017-03-15 13:47:11 -04:00
Duy-Nguyen Ta 0e278f81c6 remove Vectorize, simplify to just numpy.squeeze 2017-03-10 23:33:14 -05:00
Duy-Nguyen Ta 89bc31d703 fix comment 2017-03-10 23:28:26 -05:00
Duy-Nguyen Ta dc7792d350 unify/rename functions: matlab_code and cython_wrapper to generate_xxxxx_wrapper 2017-03-10 23:27:29 -05:00
Duy-Nguyen Ta b7efaf8c3f special ctor signature to be used with cyCreateFromShared
so that calling the default ctor by mistake on a class without the default ctor will respond nicely with an exception instead of a seg-fault
2017-03-08 15:22:16 -05:00
Duy-Nguyen Ta c9666a1b44 fix merge problem
Argument::isScalar() was moved to Qualified and should be checked via Argument::type
2017-03-08 15:15:37 -05:00
Duy-Nguyen Ta c3b11af61e remove unfinished cython-wrap test prototype 2017-03-08 10:05:35 -05:00
Duy-Nguyen Ta d8e9271dd1 fix test 2017-03-08 10:03:27 -05:00
Duy-Nguyen Ta 68e0defa49 Merge branch 'develop' into feature/cython_wrapper 2017-03-08 09:51:15 -05:00
Duy-Nguyen Ta 5a8bd5afda [cython] bypass a problem with no default constructor
Add this to support cyCreateFromShared, which needs to call the default Python constructor to construct the Python object before reassigning the internal shared ptr to the c++ object.
2017-03-06 01:18:19 -05:00
Duy-Nguyen Ta ed8f7c5f82 [cython] remove copy constructor requirement
Using make_shared[C](other) instead of shared_ptr[C](new C(other)) to leverage the implicit default constructor inside C++
2017-03-06 01:06:53 -05:00
Simon Julier 6a109aca9b Throw an exception rather than call exit. 2017-01-20 01:58:59 +00:00
Simon Julier d8d7c5618a Generate an error and exit if trying to wrap a non-const scalar reference. 2017-01-19 01:49:12 +00:00
Simon Julier 21aa7a2e85 Fixed unrwapping of scalar references. 2017-01-17 10:12:00 +00:00
Duy-Nguyen Ta 189ce33e1d Support exceptions so ipython/python can catch and doesn't crash. Trade constness with exception since Cython doesn't allow both.
See: http://stackoverflow.com/questions/26904268/cython-both-const-and-except-in-c-method-declaration
2016-12-19 17:53:14 -05:00
Duy-Nguyen Ta d9d97c4bc7 Forward declare not only classes but their inheritance
This is needed for wrapping to Cython another project based on gtsam. The current scheme requires information about all parent classes. See updated comments in gtsam.h.
2016-12-19 17:47:30 -05:00
Duy-Nguyen Ta b55f7b1fa4 remove unused argument 2016-12-19 17:30:29 -05:00