PyAPI: add utilities PyTuple_SET_ITEMS, Py_INCREF_RET
[blender.git] / source / blender / python / mathutils / mathutils_Euler.c
index 66caed50aa99bd86f2abfab3965dd673b5b3280f..9c0ced39403927ca68d13e68b3137d3e1e4dc00a 100644 (file)
@@ -1,5 +1,4 @@
 /*
- *
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- *
  * Contributor(s): Joseph Gilbert
  *
  * ***** END GPL LICENSE BLOCK *****
 
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
-#include "BLI_dynstr.h"
+#include "../generic/python_utildefines.h"
+
+#ifndef MATH_STANDALONE
+#  include "BLI_dynstr.h"
+#endif
 
 #define EULER_SIZE 3
 
-//----------------------------------mathutils.Euler() -------------------
-//makes a new euler for you to play with
+/* ----------------------------------mathutils.Euler() ------------------- */
+/* makes a new euler for you to play with */
 static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
        PyObject *seq = NULL;
@@ -61,18 +60,18 @@ static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
 
        switch (PyTuple_GET_SIZE(args)) {
-       case 0:
-               break;
-       case 2:
-               if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1)
-                       return NULL;
-               /* intentionally pass through */
-       case 1:
-               if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1)
-                       return NULL;
-               break;
+               case 0:
+                       break;
+               case 2:
+                       if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1)
+                               return NULL;
+                       /* fall-through */
+               case 1:
+                       if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1)
+                               return NULL;
+                       break;
        }
-       return Euler_CreatePyObject(eul, order, Py_NEW, type);
+       return Euler_CreatePyObject(eul, order, type);
 }
 
 /* internal use, assume read callback is done */
@@ -85,14 +84,23 @@ static const char *euler_order_str(EulerObject *self)
 short euler_order_from_string(const char *str, const char *error_prefix)
 {
        if ((str[0] && str[1] && str[2] && str[3] == '\0')) {
+
+#ifdef __LITTLE_ENDIAN__
+#  define MAKE_ID3(a, b, c)  (((a)) | ((b) << 8) | ((c) << 16))
+#else
+#  define MAKE_ID3(a, b, c)  (((a) << 24) | ((b) << 16) | ((c) << 8))
+#endif
+
                switch (*((PY_INT32_T *)str)) {
-                       case 'X'|'Y'<<8|'Z'<<16:        return EULER_ORDER_XYZ;
-                       case 'X'|'Z'<<8|'Y'<<16:        return EULER_ORDER_XZY;
-                       case 'Y'|'X'<<8|'Z'<<16:        return EULER_ORDER_YXZ;
-                       case 'Y'|'Z'<<8|'X'<<16:        return EULER_ORDER_YZX;
-                       case 'Z'|'X'<<8|'Y'<<16:        return EULER_ORDER_ZXY;
-                       case 'Z'|'Y'<<8|'X'<<16:        return EULER_ORDER_ZYX;
+                       case MAKE_ID3('X', 'Y', 'Z'): return EULER_ORDER_XYZ;
+                       case MAKE_ID3('X', 'Z', 'Y'): return EULER_ORDER_XZY;
+                       case MAKE_ID3('Y', 'X', 'Z'): return EULER_ORDER_YXZ;
+                       case MAKE_ID3('Y', 'Z', 'X'): return EULER_ORDER_YZX;
+                       case MAKE_ID3('Z', 'X', 'Y'): return EULER_ORDER_ZXY;
+                       case MAKE_ID3('Z', 'Y', 'X'): return EULER_ORDER_ZYX;
                }
+
+#undef MAKE_ID3
        }
 
        PyErr_Format(PyExc_ValueError,
@@ -123,8 +131,8 @@ static PyObject *Euler_ToTupleExt(EulerObject *self, int ndigits)
        return ret;
 }
 
-//-----------------------------METHODS----------------------------
-//return a quaternion representation of the euler
+/-----------------------------METHODS----------------------------
+ * return a quaternion representation of the euler */
 
 PyDoc_STRVAR(Euler_to_quaternion_doc,
 ".. method:: to_quaternion()\n"
@@ -143,10 +151,10 @@ static PyObject *Euler_to_quaternion(EulerObject *self)
 
        eulO_to_quat(quat, self->eul, self->order);
 
-       return Quaternion_CreatePyObject(quat, Py_NEW, NULL);
+       return Quaternion_CreatePyObject(quat, NULL);
 }
 
-//return a matrix representation of the euler
+/* return a matrix representation of the euler */
 PyDoc_STRVAR(Euler_to_matrix_doc,
 ".. method:: to_matrix()\n"
 "\n"
@@ -164,7 +172,7 @@ static PyObject *Euler_to_matrix(EulerObject *self)
 
        eulO_to_mat3((float (*)[3])mat, self->eul, self->order);
 
-       return Matrix_CreatePyObject(mat, 3, 3 , Py_NEW, NULL);
+       return Matrix_CreatePyObject(mat, 3, 3, NULL);
 }
 
 PyDoc_STRVAR(Euler_zero_doc,
@@ -205,7 +213,7 @@ static PyObject *Euler_rotate_axis(EulerObject *self, PyObject *args)
                return NULL;
        }
 
-       if (!(ELEM3(axis, 'X', 'Y', 'Z'))) {
+       if (!(ELEM(axis, 'X', 'Y', 'Z'))) {
                PyErr_SetString(PyExc_ValueError,
                                "Euler.rotate_axis(): "
                                "expected axis to be 'X', 'Y' or 'Z'");
@@ -226,7 +234,7 @@ static PyObject *Euler_rotate_axis(EulerObject *self, PyObject *args)
 PyDoc_STRVAR(Euler_rotate_doc,
 ".. method:: rotate(other)\n"
 "\n"
-"   Rotates the euler by another mathutils value.\n"
+"   Rotates the euler by another mathutils value.\n"
 "\n"
 "   :arg other: rotation component of mathutils value\n"
 "   :type other: :class:`Euler`, :class:`Quaternion` or :class:`Matrix`\n"
@@ -278,8 +286,8 @@ static PyObject *Euler_make_compatible(EulerObject *self, PyObject *value)
        Py_RETURN_NONE;
 }
 
-//----------------------------Euler.rotate()-----------------------
-// return a copy of the euler
+/----------------------------Euler.rotate()-----------------------
+ * return a copy of the euler */
 
 PyDoc_STRVAR(Euler_copy_doc,
 ".. function:: copy()\n"
@@ -297,11 +305,17 @@ static PyObject *Euler_copy(EulerObject *self)
        if (BaseMath_ReadCallback(self) == -1)
                return NULL;
 
-       return Euler_CreatePyObject(self->eul, self->order, Py_NEW, Py_TYPE(self));
+       return Euler_CreatePyObject(self->eul, self->order, Py_TYPE(self));
+}
+static PyObject *Euler_deepcopy(EulerObject *self, PyObject *args)
+{
+       if (!mathutils_deepcopy_args_check(args))
+               return NULL;
+       return Euler_copy(self);
 }
 
-//----------------------------print object (internal)--------------
-//print the object to screen
+/----------------------------print object (internal)--------------
+ * print the object to screen */
 
 static PyObject *Euler_repr(EulerObject *self)
 {
@@ -318,6 +332,7 @@ static PyObject *Euler_repr(EulerObject *self)
        return ret;
 }
 
+#ifndef MATH_STANDALONE
 static PyObject *Euler_str(EulerObject *self)
 {
        DynStr *ds;
@@ -332,6 +347,7 @@ static PyObject *Euler_str(EulerObject *self)
 
        return mathutils_dynstr_to_py(ds); /* frees ds */
 }
+#endif
 
 static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op)
 {
@@ -349,35 +365,36 @@ static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op)
        }
 
        switch (op) {
-       case Py_NE:
-               ok = !ok; /* pass through */
-       case Py_EQ:
-               res = ok ? Py_False : Py_True;
-               break;
-
-       case Py_LT:
-       case Py_LE:
-       case Py_GT:
-       case Py_GE:
-               res = Py_NotImplemented;
-               break;
-       default:
-               PyErr_BadArgument();
-               return NULL;
+               case Py_NE:
+                       ok = !ok;
+                       /* fall-through */
+               case Py_EQ:
+                       res = ok ? Py_False : Py_True;
+                       break;
+
+               case Py_LT:
+               case Py_LE:
+               case Py_GT:
+               case Py_GE:
+                       res = Py_NotImplemented;
+                       break;
+               default:
+                       PyErr_BadArgument();
+                       return NULL;
        }
 
-       return Py_INCREF(res), res;
+       return Py_INCREF_RET(res);
 }
 
-//---------------------SEQUENCE PROTOCOLS------------------------
-//----------------------------len(object)------------------------
-//sequence length
+/* ---------------------SEQUENCE PROTOCOLS------------------------ */
+/* ----------------------------len(object)------------------------ */
+/* sequence length */
 static int Euler_len(EulerObject *UNUSED(self))
 {
        return EULER_SIZE;
 }
-//----------------------------object[]---------------------------
-//sequence accessor (get)
+/* ----------------------------object[]--------------------------- */
+/* sequence accessor (get) */
 static PyObject *Euler_item(EulerObject *self, int i)
 {
        if (i < 0) i = EULER_SIZE - i;
@@ -395,16 +412,16 @@ static PyObject *Euler_item(EulerObject *self, int i)
        return PyFloat_FromDouble(self->eul[i]);
 
 }
-//----------------------------object[]-------------------------
-//sequence accessor (set)
+/* ----------------------------object[]------------------------- */
+/* sequence accessor (set) */
 static int Euler_ass_item(EulerObject *self, int i, PyObject *value)
 {
        float f = PyFloat_AsDouble(value);
 
-       if (f == -1 && PyErr_Occurred()) { // parsed item not a number
+       if (f == -1 && PyErr_Occurred()) {  /* parsed item not a number */
                PyErr_SetString(PyExc_TypeError,
                                "euler[attribute] = x: "
-                               "argument not a number");
+                               "assigned value not a number");
                return -1;
        }
 
@@ -424,8 +441,8 @@ static int Euler_ass_item(EulerObject *self, int i, PyObject *value)
 
        return 0;
 }
-//----------------------------object[z:y]------------------------
-//sequence slice (get)
+/* ----------------------------object[z:y]------------------------ */
+/* sequence slice (get) */
 static PyObject *Euler_slice(EulerObject *self, int begin, int end)
 {
        PyObject *tuple;
@@ -446,8 +463,8 @@ static PyObject *Euler_slice(EulerObject *self, int begin, int end)
 
        return tuple;
 }
-//----------------------------object[z:y]------------------------
-//sequence slice (set)
+/* ----------------------------object[z:y]------------------------ */
+/* sequence slice (set) */
 static int Euler_ass_slice(EulerObject *self, int begin, int end, PyObject *seq)
 {
        int i, size;
@@ -492,7 +509,7 @@ static PyObject *Euler_subscript(EulerObject *self, PyObject *item)
        else if (PySlice_Check(item)) {
                Py_ssize_t start, stop, step, slicelength;
 
-               if (PySlice_GetIndicesEx((void *)item, EULER_SIZE, &start, &stop, &step, &slicelength) < 0)
+               if (PySlice_GetIndicesEx(item, EULER_SIZE, &start, &stop, &step, &slicelength) < 0)
                        return NULL;
 
                if (slicelength <= 0) {
@@ -529,7 +546,7 @@ static int Euler_ass_subscript(EulerObject *self, PyObject *item, PyObject *valu
        else if (PySlice_Check(item)) {
                Py_ssize_t start, stop, step, slicelength;
 
-               if (PySlice_GetIndicesEx((void *)item, EULER_SIZE, &start, &stop, &step, &slicelength) < 0)
+               if (PySlice_GetIndicesEx(item, EULER_SIZE, &start, &stop, &step, &slicelength) < 0)
                        return -1;
 
                if (step == 1)
@@ -548,18 +565,18 @@ static int Euler_ass_subscript(EulerObject *self, PyObject *item, PyObject *valu
        }
 }
 
-//-----------------PROTCOL DECLARATIONS--------------------------
+/* -----------------PROTCOL DECLARATIONS-------------------------- */
 static PySequenceMethods Euler_SeqMethods = {
-       (lenfunc) Euler_len,                                    /* sq_length */
-       (binaryfunc) NULL,                                              /* sq_concat */
-       (ssizeargfunc) NULL,                                    /* sq_repeat */
-       (ssizeargfunc) Euler_item,                              /* sq_item */
-       (ssizessizeargfunc) NULL,                               /* sq_slice, deprecated  */
-       (ssizeobjargproc) Euler_ass_item,               /* sq_ass_item */
-       (ssizessizeobjargproc) NULL,                    /* sq_ass_slice, deprecated */
-       (objobjproc) NULL,                                              /* sq_contains */
-       (binaryfunc) NULL,                                              /* sq_inplace_concat */
-       (ssizeargfunc) NULL,                                    /* sq_inplace_repeat */
+       (lenfunc) Euler_len,                    /* sq_length */
+       (binaryfunc) NULL,                      /* sq_concat */
+       (ssizeargfunc) NULL,                    /* sq_repeat */
+       (ssizeargfunc) Euler_item,              /* sq_item */
+       (ssizessizeargfunc) NULL,               /* sq_slice, deprecated  */
+       (ssizeobjargproc) Euler_ass_item,       /* sq_ass_item */
+       (ssizessizeobjargproc) NULL,            /* sq_ass_slice, deprecated */
+       (objobjproc) NULL,                      /* sq_contains */
+       (binaryfunc) NULL,                      /* sq_inplace_concat */
+       (ssizeargfunc) NULL,                    /* sq_inplace_repeat */
 };
 
 static PyMappingMethods Euler_AsMapping = {
@@ -624,7 +641,7 @@ static PyGetSetDef Euler_getseters[] = {
 };
 
 
-//-----------------------METHOD DEFINITIONS ----------------------
+/* -----------------------METHOD DEFINITIONS ---------------------- */
 static struct PyMethodDef Euler_methods[] = {
        {"zero", (PyCFunction) Euler_zero, METH_NOARGS, Euler_zero_doc},
        {"to_matrix", (PyCFunction) Euler_to_matrix, METH_NOARGS, Euler_to_matrix_doc},
@@ -632,114 +649,141 @@ static struct PyMethodDef Euler_methods[] = {
        {"rotate_axis", (PyCFunction) Euler_rotate_axis, METH_VARARGS, Euler_rotate_axis_doc},
        {"rotate", (PyCFunction) Euler_rotate, METH_O, Euler_rotate_doc},
        {"make_compatible", (PyCFunction) Euler_make_compatible, METH_O, Euler_make_compatible_doc},
-       {"__copy__", (PyCFunction) Euler_copy, METH_NOARGS, Euler_copy_doc},
        {"copy", (PyCFunction) Euler_copy, METH_NOARGS, Euler_copy_doc},
+       {"__copy__", (PyCFunction) Euler_copy, METH_NOARGS, Euler_copy_doc},
+       {"__deepcopy__", (PyCFunction) Euler_deepcopy, METH_VARARGS, Euler_copy_doc},
        {NULL, NULL, 0, NULL}
 };
 
-//------------------PY_OBECT DEFINITION--------------------------
+/* ------------------PY_OBECT DEFINITION-------------------------- */
 PyDoc_STRVAR(euler_doc,
 "This object gives access to Eulers in Blender."
 );
 PyTypeObject euler_Type = {
        PyVarObject_HEAD_INIT(NULL, 0)
-       "mathutils.Euler",                              //tp_name
-       sizeof(EulerObject),                    //tp_basicsize
-       0,                                                              //tp_itemsize
-       (destructor)BaseMathObject_dealloc,             //tp_dealloc
-       NULL,                                                   //tp_print
-       NULL,                                                   //tp_getattr
-       NULL,                                                   //tp_setattr
-       NULL,                                                   //tp_compare
-       (reprfunc) Euler_repr,                  //tp_repr
-       NULL,                                                   //tp_as_number
-       &Euler_SeqMethods,                              //tp_as_sequence
-       &Euler_AsMapping,                               //tp_as_mapping
-       NULL,                                                   //tp_hash
-       NULL,                                                   //tp_call
-       (reprfunc) Euler_str,                   //tp_str
-       NULL,                                                   //tp_getattro
-       NULL,                                                   //tp_setattro
-       NULL,                                                   //tp_as_buffer
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, //tp_flags
-       euler_doc, //tp_doc
-       (traverseproc)BaseMathObject_traverse,  //tp_traverse
-       (inquiry)BaseMathObject_clear,  //tp_clear
-       (richcmpfunc)Euler_richcmpr,    //tp_richcompare
-       0,                                                              //tp_weaklistoffset
-       NULL,                                                   //tp_iter
-       NULL,                                                   //tp_iternext
-       Euler_methods,                                  //tp_methods
-       NULL,                                                   //tp_members
-       Euler_getseters,                                //tp_getset
-       NULL,                                                   //tp_base
-       NULL,                                                   //tp_dict
-       NULL,                                                   //tp_descr_get
-       NULL,                                                   //tp_descr_set
-       0,                                                              //tp_dictoffset
-       NULL,                                                   //tp_init
-       NULL,                                                   //tp_alloc
-       Euler_new,                                              //tp_new
-       NULL,                                                   //tp_free
-       NULL,                                                   //tp_is_gc
-       NULL,                                                   //tp_bases
-       NULL,                                                   //tp_mro
-       NULL,                                                   //tp_cache
-       NULL,                                                   //tp_subclasses
-       NULL,                                                   //tp_weaklist
-       NULL                                                    //tp_del
+       "Euler",                        /* tp_name */
+       sizeof(EulerObject),            /* tp_basicsize */
+       0,                              /* tp_itemsize */
+       (destructor)BaseMathObject_dealloc,     /* tp_dealloc */
+       NULL,                           /* tp_print */
+       NULL,                           /* tp_getattr */
+       NULL,                           /* tp_setattr */
+       NULL,                           /* tp_compare */
+       (reprfunc) Euler_repr,          /* tp_repr */
+       NULL,                           /* tp_as_number */
+       &Euler_SeqMethods,              /* tp_as_sequence */
+       &Euler_AsMapping,               /* tp_as_mapping */
+       NULL,                           /* tp_hash */
+       NULL,                           /* tp_call */
+#ifndef MATH_STANDALONE
+       (reprfunc) Euler_str,           /* tp_str */
+#else
+       NULL,                           /* tp_str */
+#endif
+       NULL,                           /* tp_getattro */
+       NULL,                           /* tp_setattro */
+       NULL,                           /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+       euler_doc, /* tp_doc */
+       (traverseproc)BaseMathObject_traverse,  /* tp_traverse */
+       (inquiry)BaseMathObject_clear,  /* tp_clear */
+       (richcmpfunc)Euler_richcmpr,    /* tp_richcompare */
+       0,                              /* tp_weaklistoffset */
+       NULL,                           /* tp_iter */
+       NULL,                           /* tp_iternext */
+       Euler_methods,                  /* tp_methods */
+       NULL,                           /* tp_members */
+       Euler_getseters,                /* tp_getset */
+       NULL,                           /* tp_base */
+       NULL,                           /* tp_dict */
+       NULL,                           /* tp_descr_get */
+       NULL,                           /* tp_descr_set */
+       0,                              /* tp_dictoffset */
+       NULL,                           /* tp_init */
+       NULL,                           /* tp_alloc */
+       Euler_new,                      /* tp_new */
+       NULL,                           /* tp_free */
+       NULL,                           /* tp_is_gc */
+       NULL,                           /* tp_bases */
+       NULL,                           /* tp_mro */
+       NULL,                           /* tp_cache */
+       NULL,                           /* tp_subclasses */
+       NULL,                           /* tp_weaklist */
+       NULL                            /* tp_del */
 };
-//------------------------Euler_CreatePyObject (internal)-------------
-//creates a new euler object
-/*pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER
- (i.e. it was allocated elsewhere by MEM_mallocN())
-  pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON
- (i.e. it must be created here with PyMEM_malloc())*/
-PyObject *Euler_CreatePyObject(float *eul, short order, int type, PyTypeObject *base_type)
+
+
+PyObject *Euler_CreatePyObject(
+        const float eul[3], const short order,
+        PyTypeObject *base_type)
 {
        EulerObject *self;
+       float *eul_alloc;
 
-       self = base_type ?      (EulerObject *)base_type->tp_alloc(base_type, 0) :
-                                               (EulerObject *)PyObject_GC_New(EulerObject, &euler_Type);
+       eul_alloc = PyMem_Malloc(EULER_SIZE * sizeof(float));
+       if (UNLIKELY(eul_alloc == NULL)) {
+               PyErr_SetString(PyExc_MemoryError,
+                               "Euler(): "
+                               "problem allocating data");
+               return NULL;
+       }
 
+       self = BASE_MATH_NEW(EulerObject, euler_Type, base_type);
        if (self) {
+               self->eul = eul_alloc;
+
                /* init callbacks as NULL */
                self->cb_user = NULL;
                self->cb_type = self->cb_subtype = 0;
 
-               if (type == Py_WRAP) {
-                       self->eul = eul;
-                       self->wrapped = Py_WRAP;
-               }
-               else if (type == Py_NEW) {
-                       self->eul = PyMem_Malloc(EULER_SIZE * sizeof(float));
-                       if (eul) {
-                               copy_v3_v3(self->eul, eul);
-                       }
-                       else {
-                               zero_v3(self->eul);
-                       }
-
-                       self->wrapped = Py_NEW;
+               if (eul) {
+                       copy_v3_v3(self->eul, eul);
                }
                else {
-                       Py_FatalError("Euler(): invalid type!");
+                       zero_v3(self->eul);
                }
 
+               self->flag = BASE_MATH_FLAG_DEFAULT;
+               self->order = order;
+       }
+       else {
+               PyMem_Free(eul_alloc);
+       }
+
+       return (PyObject *)self;
+}
+
+PyObject *Euler_CreatePyObject_wrap(
+        float eul[3], const short order,
+        PyTypeObject *base_type)
+{
+       EulerObject *self;
+
+       self = BASE_MATH_NEW(EulerObject, euler_Type, base_type);
+       if (self) {
+               /* init callbacks as NULL */
+               self->cb_user = NULL;
+               self->cb_type = self->cb_subtype = 0;
+
+               self->eul = eul;
+               self->flag = BASE_MATH_FLAG_DEFAULT | BASE_MATH_FLAG_IS_WRAP;
+
                self->order = order;
        }
 
        return (PyObject *)self;
 }
 
-PyObject *Euler_CreatePyObject_cb(PyObject *cb_user, short order, int cb_type, int cb_subtype)
+PyObject *Euler_CreatePyObject_cb(
+        PyObject *cb_user, const short order,
+        unsigned char cb_type, unsigned char cb_subtype)
 {
-       EulerObject *self = (EulerObject *)Euler_CreatePyObject(NULL, order, Py_NEW, NULL);
+       EulerObject *self = (EulerObject *)Euler_CreatePyObject(NULL, order, NULL);
        if (self) {
                Py_INCREF(cb_user);
-               self->cb_user =                 cb_user;
-               self->cb_type =                 (unsigned char)cb_type;
-               self->cb_subtype =              (unsigned char)cb_subtype;
+               self->cb_user         = cb_user;
+               self->cb_type         = cb_type;
+               self->cb_subtype      = cb_subtype;
                PyObject_GC_Track(self);
        }