game engine python api
[blender.git] / source / gameengine / Expressions / Value.cpp
1 // Value.cpp: implementation of the CValue class.
2 // developed at Eindhoven University of Technology, 1997
3 // by the OOPS team
4 //////////////////////////////////////////////////////////////////////
5 /*
6  * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
7  *
8  * Permission to use, copy, modify, distribute and sell this software
9  * and its documentation for any purpose is hereby granted without fee,
10  * provided that the above copyright notice appear in all copies and
11  * that both that copyright notice and this permission notice appear
12  * in supporting documentation.  Erwin Coumans makes no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  */
17 #include "Value.h"
18 #include "FloatValue.h"
19 #include "IntValue.h"
20 #include "VectorValue.h"
21 #include "VoidValue.h"
22 #include "StringValue.h"
23 #include "ErrorValue.h"
24 #include "ListValue.h"
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 //////////////////////////////////////////////////////////////////////
31 // Construction/Destruction
32 //////////////////////////////////////////////////////////////////////
33
34 double CValue::m_sZeroVec[3] = {0.0,0.0,0.0};
35
36 #ifndef NO_EXP_PYTHON_EMBEDDING
37
38 PyObject* cvalue_add(PyObject*v, PyObject*w)
39 {
40         return  ((CValue*)v)->Calc(VALUE_ADD_OPERATOR,(CValue*)w);
41 }
42 PyObject* cvalue_sub(PyObject*v, PyObject*w)
43 {
44         return  ((CValue*)v)->Calc(VALUE_SUB_OPERATOR,(CValue*)w);
45 }
46 PyObject* cvalue_mul(PyObject*v, PyObject*w)
47 {
48         return  ((CValue*)v)->Calc(VALUE_MUL_OPERATOR,(CValue*)w);
49 }
50 PyObject* cvalue_div(PyObject*v, PyObject*w)
51 {
52         return  ((CValue*)v)->Calc(VALUE_DIV_OPERATOR,(CValue*)w);
53 }
54 PyObject* cvalue_neg(PyObject*v)
55 {
56         return  ((CValue*)v)->Calc(VALUE_NEG_OPERATOR,(CValue*)v);
57 }
58 PyObject* cvalue_pos(PyObject*v)
59 {
60         return  ((CValue*)v)->Calc(VALUE_POS_OPERATOR,(CValue*)v);
61 }
62
63
64 int MyPyCompare (PyObject* v,PyObject* w)
65 {
66         CValue* eqval =  ((CValue*)v)->Calc(VALUE_EQL_OPERATOR,(CValue*)w);
67         STR_String txt = eqval->GetText();
68         eqval->Release();
69         if (txt=="TRUE")
70                 return 0;
71         CValue* lessval =  ((CValue*)v)->Calc(VALUE_LES_OPERATOR,(CValue*)w);
72         txt = lessval->GetText();
73         lessval->Release();
74         if (txt=="TRUE")
75                 return -1;
76
77         return 1;
78 }
79
80
81 int cvalue_coerce(PyObject** pv,PyObject** pw)
82 {
83         if (PyInt_Check(*pw)) {
84                 double db  = (double)PyInt_AsLong(*pw);
85                 *pw = new CIntValue((int) db);
86                 Py_INCREF(*pv);
87                 return 0;
88         }
89         else if (PyLong_Check(*pw)) {
90                 double db = PyLong_AsDouble(*pw);
91                 *pw = new CFloatValue(db);
92                 Py_INCREF(*pv);
93                 return 0;
94         }
95         else if (PyFloat_Check(*pw)) {
96                 double db = PyFloat_AsDouble(*pw);
97                 *pw = new CFloatValue(db);
98                 Py_INCREF(*pv);
99                 return 0;
100         } else if (PyString_Check(*pw)) {
101                 const STR_String str = PyString_AsString(*pw);
102                 *pw = new CStringValue(str,"");
103                 Py_INCREF(*pv);
104                 return 0;
105         }
106         return 1; /* Can't do it */
107
108 }
109 static PyNumberMethods cvalue_as_number = {
110         (binaryfunc)cvalue_add, /*nb_add*/
111         (binaryfunc)cvalue_sub, /*nb_subtract*/
112         (binaryfunc)cvalue_mul, /*nb_multiply*/
113         (binaryfunc)cvalue_div, /*nb_divide*/
114         0,//(binaryfunc)cvalue_remainder,       /*nb_remainder*/
115         0,//(binaryfunc)cvalue_divmod,  /*nb_divmod*/
116         0,//0,//0,//0,//(ternaryfunc)cvalue_pow, /*nb_power*/
117         (unaryfunc)cvalue_neg, /*nb_negative*/
118         0,//(unaryfunc)cvalue_pos, /*nb_positive*/
119         0,//(unaryfunc)cvalue_abs, /*nb_absolute*/
120         0,//(inquiry)cvalue_nonzero, /*nb_nonzero*/
121         0,              /*nb_invert*/
122         0,              /*nb_lshift*/
123         0,              /*nb_rshift*/
124         0,              /*nb_and*/
125         0,              /*nb_xor*/
126         0,              /*nb_or*/
127         (coercion)cvalue_coerce, /*nb_coerce*/
128         0,//(unaryfunc)cvalue_int, /*nb_int*/
129         0,//(unaryfunc)cvalue_long, /*nb_long*/
130         0,//(unaryfunc)cvalue_float, /*nb_float*/
131         0,              /*nb_oct*/
132         0,              /*nb_hex*/
133 };
134
135
136 PyTypeObject CValue::Type = {
137         PyObject_HEAD_INIT(&PyType_Type)
138         0,
139         "CValue",
140         sizeof(CValue),
141         0,
142         PyDestructor,
143         0,
144         __getattr,
145         __setattr,
146         &MyPyCompare,
147         __repr,
148         &cvalue_as_number,
149         0,
150         0,
151         0,
152         0
153 };
154
155 PyParentObject CValue::Parents[] = {
156         &CValue::Type,
157                 NULL
158 };
159
160 PyMethodDef CValue::Methods[] = {
161 //      { "printHello", (PyCFunction) CValue::sPyPrintHello, METH_VARARGS},
162         { "getName", (PyCFunction) CValue::sPyGetName, METH_NOARGS},
163         {NULL,NULL} //Sentinel
164 };
165
166 PyObject* CValue::PyGetName(PyObject* self)
167 {
168         return PyString_FromString(this->GetName());
169 }
170
171 /*#define CVALUE_DEBUG*/
172 #ifdef CVALUE_DEBUG
173 int gRefCount;
174 struct SmartCValueRef 
175 {
176         CValue *m_ref;
177         int m_count;
178         SmartCValueRef(CValue *ref)
179         {
180                 m_ref = ref;
181                 m_count = gRefCount++;
182         }
183 };
184
185 #include <vector>
186
187 std::vector<SmartCValueRef> gRefList;
188 #endif
189
190 #ifdef _DEBUG
191 //int gRefCountValue;
192 #endif
193
194 CValue::CValue(PyTypeObject *T)
195                 : PyObjectPlus(T),
196 #else
197 CValue::CValue()
198
199 #endif //NO_EXP_PYTHON_EMBEDDING
200         
201 m_pNamedPropertyArray(NULL),
202 m_refcount(1)
203 /*
204 pre: false
205 effect: constucts a CValue
206 */
207 {
208         //debug(gRefCountValue++)       // debugging
209 #ifdef _DEBUG
210         //gRefCountValue++;
211 #ifdef CVALUE_DEBUG
212         gRefList.push_back(SmartCValueRef(this));
213 #endif
214 #endif
215 }
216
217
218
219 CValue::~CValue()
220 /*
221 pre:
222 effect: deletes the object
223 */
224 {
225         ClearProperties();
226
227         assertd (m_refcount==0);
228 #ifdef CVALUE_DEBUG
229         std::vector<SmartCValueRef>::iterator it;
230         for (it=gRefList.begin(); it!=gRefList.end(); it++)
231         {
232                 if (it->m_ref == this)
233                 {
234                         *it = gRefList.back();
235                         gRefList.pop_back();
236                         break;
237                 }
238         }
239 #endif
240 }
241
242
243
244
245 #define VALUE_SUB(val1, val2) (val1)->Calc(VALUE_SUB_OPERATOR, val2)
246 #define VALUE_MUL(val1, val2) (val1)->Calc(VALUE_MUL_OPERATOR, val2)
247 #define VALUE_DIV(val1, val2) (val1)->Calc(VALUE_DIV_OPERATOR, val2)
248 #define VALUE_NEG(val1)       (val1)->Calc(VALUE_NEG_OPERATOR, val1)
249 #define VALUE_POS(val1)       (val1)->Calc(VALUE_POS_OPERATOR, val1)
250
251
252 STR_String CValue::op2str (VALUE_OPERATOR op)
253 {
254         //pre:
255         //ret: the stringrepresentation of operator op
256         
257         STR_String opmsg;
258         switch (op) {
259         case VALUE_ADD_OPERATOR:
260                 opmsg = " + ";
261                 break;
262         case VALUE_SUB_OPERATOR:
263                 opmsg = " - ";
264                 break;
265         case VALUE_MUL_OPERATOR:
266                 opmsg = " * ";
267                 break;
268         case VALUE_DIV_OPERATOR:
269                 opmsg = " / ";
270                 break;
271         case VALUE_NEG_OPERATOR:
272                 opmsg = " -";
273                 break;
274         case VALUE_POS_OPERATOR:
275                 opmsg = " +";
276                 break;
277         case VALUE_AND_OPERATOR:
278                 opmsg = " & ";
279                 break;
280         case VALUE_OR_OPERATOR:
281                 opmsg = " | ";
282                 break;
283         case VALUE_EQL_OPERATOR:
284                 opmsg = " = ";
285                 break;
286         case VALUE_NEQ_OPERATOR:
287                 opmsg = " != ";
288                 break;
289         case VALUE_NOT_OPERATOR:
290                 opmsg = " !";
291                 break;
292         default:
293                 opmsg="Error in Errorhandling routine.";
294                 //              AfxMessageBox("Invalid operator");
295                 break;
296         }
297         return opmsg;
298 }
299
300
301
302
303
304 //---------------------------------------------------------------------------------------------------------------------
305 //      Property Management
306 //---------------------------------------------------------------------------------------------------------------------
307
308
309
310 //
311 // Set property <ioProperty>, overwrites and releases a previous property with the same name if needed
312 //
313 void CValue::SetProperty(const STR_String & name,CValue* ioProperty)
314 {
315         // Check if somebody is setting an empty property
316         if (ioProperty==NULL)
317         {
318                 trace("Warning:trying to set empty property!");
319                 return;
320         }
321
322         // Make sure we have a property array
323         if (m_pNamedPropertyArray == NULL)
324                 m_pNamedPropertyArray = new std::map<STR_String,CValue *>;
325
326         // Try to replace property (if so -> exit as soon as we replaced it)
327         CValue* oldval = (*m_pNamedPropertyArray)[name];
328         if (oldval)
329         {
330                 oldval->Release();
331         }
332         
333         // Add property at end of array
334         (*m_pNamedPropertyArray)[name] = ioProperty->AddRef();//->Add(ioProperty);
335 }
336
337
338
339 //
340 // Get pointer to a property with name <inName>, returns NULL if there is no property named <inName>
341 //
342 CValue* CValue::GetProperty(const STR_String & inName)
343 {
344         // Check properties, as soon as we found it -> Return a pointer to the property
345         CValue* result = NULL;
346         if (m_pNamedPropertyArray)
347         {
348                 std::map<STR_String,CValue*>::iterator it = (*m_pNamedPropertyArray).find(inName);
349                 if (!( it==m_pNamedPropertyArray->end()))
350                 {
351                         result = (*it).second;
352                 }
353
354         }
355                 //for (int i=0; i<m_pValuePropertyArray->size(); i++)
356                 //      if ((*m_pValuePropertyArray)[i]->GetName() == inName)
357                 //              return (*m_pValuePropertyArray)[i];
358         
359         // Did not find property with name <inName>, return NULL property pointer
360         return result;
361 }
362
363
364
365 //
366 // Get text description of property with name <inName>, returns an empty string if there is no property named <inName>
367 //
368 STR_String CValue::GetPropertyText(const STR_String & inName,const STR_String& deftext)
369 {
370         CValue *property = GetProperty(inName);
371         if (property)
372                 return property->GetText();
373         else
374                 return deftext;//String::sEmpty;
375 }
376
377 float CValue::GetPropertyNumber(const STR_String& inName,float defnumber)
378 {
379         CValue *property = GetProperty(inName);
380         if (property)
381                 return property->GetNumber();
382         else
383                 return defnumber;
384 }
385
386
387
388 //
389 // Remove the property named <inName>, returns true if the property was succesfully removed, false if property was not found or could not be removed
390 //
391 bool CValue::RemoveProperty(const STR_String & inName)
392 {
393         // Check if there are properties at all which can be removed
394         if (m_pNamedPropertyArray == NULL)
395                 return false;
396
397         CValue* val = GetProperty(inName);
398         if (NULL != val) 
399         {
400                 val->Release();
401                 m_pNamedPropertyArray->erase(inName);
402                 return true;
403         }
404         return false;
405 }
406
407 //
408 // Get Property Names
409 //
410 vector<STR_String> CValue::GetPropertyNames()
411 {
412         vector<STR_String> result;
413         if(!m_pNamedPropertyArray) return result;
414         for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
415         !(it == m_pNamedPropertyArray->end());it++)
416         {
417                 result.push_back((*it).first);
418         }
419         return result;
420 }
421
422 //
423 // Clear all properties
424 //
425 void CValue::ClearProperties()
426 {               
427         // Check if we have any properties
428         if (m_pNamedPropertyArray == NULL)
429                 return;
430
431         // Remove all properties
432         for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
433         !(it == m_pNamedPropertyArray->end());it++)
434         {
435                 CValue* tmpval = (*it).second;
436                 //STR_String name = (*it).first;
437                 tmpval->Release();
438         }
439
440         // Delete property array
441         delete m_pNamedPropertyArray;
442         m_pNamedPropertyArray=NULL;
443 }
444
445
446
447 //
448 // Set all properties' modified flag to <inModified>
449 //
450 void CValue::SetPropertiesModified(bool inModified)
451 {
452         int numprops = GetPropertyCount();
453         for (int i=0; i<numprops; i++)
454                 GetProperty(i)->SetModified(inModified);
455 }
456
457
458
459 //
460 // Check if any of the properties in this value have been modified
461 //
462 bool CValue::IsAnyPropertyModified()
463 {
464         int numprops = GetPropertyCount();
465         for (int i=0;i<numprops;i++)
466                 if (GetProperty(i)->IsModified())
467                         return true;
468
469         return false;
470 }
471
472
473
474 //
475 // Get property number <inIndex>
476 //
477
478 CValue* CValue::GetProperty(int inIndex)
479 {
480
481         int count=0;
482         CValue* result = NULL;
483
484         if (m_pNamedPropertyArray)
485         {
486                 for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
487                 !(it == m_pNamedPropertyArray->end());it++)
488                 {
489                         if (count++==inIndex)
490                         {
491                                 result = (*it).second;
492                                 break;
493                         }
494                 }
495
496         }
497         return result;
498 }
499
500
501
502 //
503 // Get the amount of properties assiocated with this value
504 //
505 int CValue::GetPropertyCount()
506 {
507         if (m_pNamedPropertyArray)
508                 return m_pNamedPropertyArray->size();
509         else
510                 return 0;
511 }
512
513
514
515
516
517 void CValue::CloneProperties(CValue *replica)
518 {
519         
520         if (m_pNamedPropertyArray)
521         {
522                 replica->m_pNamedPropertyArray=NULL;
523                 for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
524                 !(it == m_pNamedPropertyArray->end());it++)
525                 {
526                         CValue *val = (*it).second->GetReplica();
527                         replica->SetProperty((*it).first,val);
528                         val->Release();
529                 }
530         }
531
532         
533 }
534
535 double*         CValue::GetVector3(bool bGetTransformedVec)
536 {
537         assertd(false); // don;t get vector from me
538         return m_sZeroVec;//::sZero;
539 }
540
541
542 /*---------------------------------------------------------------------------------------------------------------------
543         Reference Counting
544 ---------------------------------------------------------------------------------------------------------------------*/
545 //
546 // Add a reference to this value
547 //
548 CValue *CValue::AddRef()
549 {
550         // Increase global reference count, used to see at the end of the program
551         // if all CValue-derived classes have been dereferenced to 0
552         //debug(gRefCountValue++);
553 #ifdef _DEBUG
554         //gRefCountValue++;
555 #endif
556         m_refcount++; 
557         return this;
558 }
559
560
561
562 //
563 // Release a reference to this value (when reference count reaches 0, the value is removed from the heap)
564 //
565 int     CValue::Release()
566 {
567         // Decrease global reference count, used to see at the end of the program
568         // if all CValue-derived classes have been dereferenced to 0
569         //debug(gRefCountValue--);
570 #ifdef _DEBUG
571         //gRefCountValue--;
572 #endif
573         // Decrease local reference count, if it reaches 0 the object should be freed
574         if (--m_refcount > 0)
575         {
576                 // Reference count normal, return new reference count
577                 return m_refcount;
578         }
579         else
580         {
581                 // Reference count reached 0, delete ourselves and return 0
582 //              MT_assert(m_refcount==0, "Reference count reached sub-zero, object released too much");
583                 delete this;
584                 return 0;
585         }
586
587 }
588
589
590
591 //
592 // Disable reference counting for this value
593 //
594 void CValue::DisableRefCount()
595 {
596         assertd(m_refcount == 1);
597         m_refcount--;
598
599         //debug(gRefCountValue--);
600 #ifdef _DEBUG
601         //gRefCountValue--;
602 #endif
603         m_ValFlags.RefCountDisabled=true;
604 }
605
606
607
608 void CValue::AddDataToReplica(CValue *replica)
609 {
610         replica->m_refcount = 1;
611
612         //register with Python
613         _Py_NewReference(replica);
614
615 #ifdef _DEBUG
616         //gRefCountValue++;
617 #endif
618         replica->m_ValFlags.RefCountDisabled = false;
619
620         replica->ReplicaSetName(GetName());
621
622         //copy all props
623         CloneProperties(replica);
624 }
625
626
627
628 CValue* CValue::FindIdentifier(const STR_String& identifiername)
629 {
630
631         CValue* result = NULL;
632
633         int pos = 0;
634         // if a dot exists, explode the name into pieces to get the subcontext
635         if ((pos=identifiername.Find('.'))>=0)
636         {
637                 const STR_String rightstring = identifiername.Right(identifiername.Length() -1 - pos);
638                 const STR_String leftstring = identifiername.Left(pos);
639                 CValue* tempresult = GetProperty(leftstring);
640                 if (tempresult)
641                 {
642                         result=tempresult->FindIdentifier(rightstring);
643                 } 
644         } else
645         {
646                 result = GetProperty(identifiername);
647                 if (result)
648                         return result->AddRef();
649         }
650         if (!result)
651         {
652                 // warning here !!!
653                 result = new CErrorValue(identifiername+" not found");
654         }
655         return result;
656 }
657
658
659 #ifndef NO_EXP_PYTHON_EMBEDDING
660
661
662 static PyMethodDef      CValueMethods[] = 
663 {
664         //{ "new", CValue::PyMake , METH_VARARGS},
665         { NULL,NULL}    // Sentinel
666 };
667
668
669 PyObject*       CValue::_getattr(const STR_String& attr)
670 {
671         CValue* resultattr = FindIdentifier(attr);
672         STR_String text;
673         if (resultattr)
674         {
675                 if (resultattr->IsError())
676                 {
677                         resultattr->Release();
678                 } else
679                 {
680                         // to avoid some compare problems, return a real pythonthing
681                         PyObject* pyconvert = resultattr->ConvertValueToPython();
682                         if (pyconvert)
683                         {
684                                 resultattr->Release();
685                                 return pyconvert;
686                         } else
687                         {
688                                 // also check if it's already in pythoninterpreter!
689                                 return resultattr;
690                         }
691                         
692                 }
693         }
694         _getattr_up(PyObjectPlus);
695 }
696
697 CValue* CValue::ConvertPythonToValue(PyObject* pyobj)
698 {
699
700         CValue* vallie = NULL;
701
702         if (PyList_Check(pyobj))
703         {
704                 CListValue* listval = new CListValue();
705                 bool error = false;
706
707                 int i;
708                 int numitems = PyList_Size(pyobj);
709                 for (i=0;i<numitems;i++)
710                 {
711                         PyObject* listitem = PyList_GetItem(pyobj,i); /* borrowed ref */
712                         CValue* listitemval = ConvertPythonToValue(listitem);
713                         if (listitemval)
714                         {
715                                 listval->Add(listitemval);
716                         } else
717                         {
718                                 error = true;
719                         }
720                 }
721                 if (!error)
722                 {
723                         // jippie! could be converted
724                         vallie = listval;
725                 } else
726                 {
727                         // list could not be converted... bad luck
728                         listval->Release();
729                 }
730
731         } else
732         if (PyFloat_Check(pyobj))
733         {
734                 vallie = new CFloatValue( (float)PyFloat_AsDouble(pyobj) );
735         } else
736         if (PyInt_Check(pyobj))
737         {
738                 vallie = new CIntValue( (int)PyInt_AS_LONG(pyobj) );
739         } else
740         if (PyString_Check(pyobj))
741         {
742                 vallie = new CStringValue(PyString_AsString(pyobj),"");
743         } else
744         if (pyobj->ob_type==&CValue::Type || pyobj->ob_type==&CListValue::Type)
745         {
746                 vallie = ((CValue*) pyobj)->AddRef();
747         } else
748         {
749                 /* return an error value from the caller */
750                 PyErr_SetString(PyExc_TypeError, "This python value could not be assigned to a game engine property");
751         }
752         return vallie;
753
754 }
755
756 int     CValue::_delattr(const STR_String& attr)
757 {
758         RemoveProperty(attr);
759         return 0;
760 }
761
762 int     CValue::_setattr(const STR_String& attr,PyObject* pyobj)
763 {
764         CValue* vallie = ConvertPythonToValue(pyobj);
765         if (vallie)
766         {
767                 CValue* oldprop = GetProperty(attr);
768                 
769                 if (oldprop)
770                 {
771                         oldprop->SetValue(vallie);
772                 } else
773                 {
774                         SetProperty(attr,vallie);
775                 }
776                 vallie->Release();
777         } else
778         {
779                 return 1; /* ConvertPythonToValue sets the error message */
780         }
781         
782         //PyObjectPlus::_setattr(attr,value);
783         return 0;
784 };
785
786 PyObject*       CValue::ConvertKeysToPython( void )
787 {
788         PyObject *pylist = PyList_New( 0 );
789         PyObject *pystr;
790         
791         if (m_pNamedPropertyArray)
792         {
793                 for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
794                 !(it == m_pNamedPropertyArray->end());it++)
795                 {
796                         pystr = PyString_FromString( (*it).first );
797                         PyList_Append(pylist, pystr);
798                         Py_DECREF( pystr );
799                 }
800         }
801         return pylist;
802 }
803
804 /*
805 PyObject*       CValue::PyMake(PyObject* ignored,PyObject* args)
806 {
807
808         //if (!PyArg_ParseTuple(args,"s",&name)) return NULL;
809         Py_RETURN_NONE;//new CValue();
810 }
811 */
812
813 extern "C" {
814         void initCValue(void)
815         {
816                 Py_InitModule("CValue",CValueMethods);
817         }
818 }
819
820
821
822 #endif //NO_EXP_PYTHON_EMBEDDING
823
824 ///////////////////////////////////////////////////////////////////////////////////////////////
825 ///////////////////////////////////////////////////////////////////////////////////////////////
826 /* These implementations were moved out of the header */
827
828 void CValue::SetOwnerExpression(class CExpression* expr)
829 {
830         /* intentionally empty */
831 }
832
833 void CValue::SetColorOperator(VALUE_OPERATOR op)
834 {
835         /* intentionally empty */
836 }
837 void CValue::SetValue(CValue* newval)
838
839         // no one should get here
840         assertd(newval->GetNumber() == 10121969);       
841 }
842 ///////////////////////////////////////////////////////////////////////////////////////////////
843 ///////////////////////////////////////////////////////////////////////////////////////////////
844