soc-2008-mxcurioni: merged changes to revision 15705
[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, Py_NEWARGS},
162         { "getName", (PyCFunction) CValue::sPyGetName, Py_NEWARGS},
163         {NULL,NULL} //Sentinel
164 };
165
166 PyObject* CValue::PyGetName(PyObject* self,PyObject* args,PyObject* kwds)
167 {
168         PyObject* pyname = PyString_FromString(this->GetName());
169         return pyname;
170 }
171
172 /*#define CVALUE_DEBUG*/
173 #ifdef CVALUE_DEBUG
174 int gRefCount;
175 struct SmartCValueRef 
176 {
177         CValue *m_ref;
178         int m_count;
179         SmartCValueRef(CValue *ref)
180         {
181                 m_ref = ref;
182                 m_count = gRefCount++;
183         }
184 };
185
186 #include <vector>
187
188 std::vector<SmartCValueRef> gRefList;
189 #endif
190
191 #ifdef _DEBUG
192 //int gRefCountValue;
193 #endif
194
195 CValue::CValue(PyTypeObject *T)
196                 : PyObjectPlus(T),
197 #else
198 CValue::CValue()
199
200 #endif //NO_EXP_PYTHON_EMBEDDING
201         
202 m_pNamedPropertyArray(NULL),
203 m_refcount(1)
204 /*
205 pre: false
206 effect: constucts a CValue
207 */
208 {
209         //debug(gRefCountValue++)       // debugging
210 #ifdef _DEBUG
211         //gRefCountValue++;
212 #ifdef CVALUE_DEBUG
213         gRefList.push_back(SmartCValueRef(this));
214 #endif
215 #endif
216 }
217
218
219
220 CValue::~CValue()
221 /*
222 pre:
223 effect: deletes the object
224 */
225 {
226         ClearProperties();
227
228         assertd (m_refcount==0);
229 #ifdef CVALUE_DEBUG
230         std::vector<SmartCValueRef>::iterator it;
231         for (it=gRefList.begin(); it!=gRefList.end(); it++)
232         {
233                 if (it->m_ref == this)
234                 {
235                         *it = gRefList.back();
236                         gRefList.pop_back();
237                         break;
238                 }
239         }
240 #endif
241 }
242
243
244
245
246 #define VALUE_SUB(val1, val2) (val1)->Calc(VALUE_SUB_OPERATOR, val2)
247 #define VALUE_MUL(val1, val2) (val1)->Calc(VALUE_MUL_OPERATOR, val2)
248 #define VALUE_DIV(val1, val2) (val1)->Calc(VALUE_DIV_OPERATOR, val2)
249 #define VALUE_NEG(val1)       (val1)->Calc(VALUE_NEG_OPERATOR, val1)
250 #define VALUE_POS(val1)       (val1)->Calc(VALUE_POS_OPERATOR, val1)
251
252
253 STR_String CValue::op2str (VALUE_OPERATOR op)
254 {
255         //pre:
256         //ret: the stringrepresentation of operator op
257         
258         STR_String opmsg;
259         switch (op) {
260         case VALUE_ADD_OPERATOR:
261                 opmsg = " + ";
262                 break;
263         case VALUE_SUB_OPERATOR:
264                 opmsg = " - ";
265                 break;
266         case VALUE_MUL_OPERATOR:
267                 opmsg = " * ";
268                 break;
269         case VALUE_DIV_OPERATOR:
270                 opmsg = " / ";
271                 break;
272         case VALUE_NEG_OPERATOR:
273                 opmsg = " -";
274                 break;
275         case VALUE_POS_OPERATOR:
276                 opmsg = " +";
277                 break;
278         case VALUE_AND_OPERATOR:
279                 opmsg = " & ";
280                 break;
281         case VALUE_OR_OPERATOR:
282                 opmsg = " | ";
283                 break;
284         case VALUE_EQL_OPERATOR:
285                 opmsg = " = ";
286                 break;
287         case VALUE_NEQ_OPERATOR:
288                 opmsg = " != ";
289                 break;
290         case VALUE_NOT_OPERATOR:
291                 opmsg = " !";
292                 break;
293         default:
294                 opmsg="Error in Errorhandling routine.";
295                 //              AfxMessageBox("Invalid operator");
296                 break;
297         }
298         return opmsg;
299 }
300
301
302
303
304
305 //---------------------------------------------------------------------------------------------------------------------
306 //      Property Management
307 //---------------------------------------------------------------------------------------------------------------------
308
309
310
311 //
312 // Set property <ioProperty>, overwrites and releases a previous property with the same name if needed
313 //
314 void CValue::SetProperty(const STR_String & name,CValue* ioProperty)
315 {
316         // Check if somebody is setting an empty property
317         if (ioProperty==NULL)
318         {
319                 trace("Warning:trying to set empty property!");
320                 return;
321         }
322
323         // Make sure we have a property array
324         if (m_pNamedPropertyArray == NULL)
325                 m_pNamedPropertyArray = new std::map<STR_String,CValue *>;
326
327         // Try to replace property (if so -> exit as soon as we replaced it)
328         CValue* oldval = (*m_pNamedPropertyArray)[name];
329         if (oldval)
330         {
331                 oldval->Release();
332         }
333         
334         // Add property at end of array
335         (*m_pNamedPropertyArray)[name] = ioProperty->AddRef();//->Add(ioProperty);
336 }
337
338
339
340 //
341 // Get pointer to a property with name <inName>, returns NULL if there is no property named <inName>
342 //
343 CValue* CValue::GetProperty(const STR_String & inName)
344 {
345         // Check properties, as soon as we found it -> Return a pointer to the property
346         CValue* result = NULL;
347         if (m_pNamedPropertyArray)
348         {
349                 std::map<STR_String,CValue*>::iterator it = (*m_pNamedPropertyArray).find(inName);
350                 if (!( it==m_pNamedPropertyArray->end()))
351                 {
352                         result = (*it).second;
353                 }
354
355         }
356                 //for (int i=0; i<m_pValuePropertyArray->size(); i++)
357                 //      if ((*m_pValuePropertyArray)[i]->GetName() == inName)
358                 //              return (*m_pValuePropertyArray)[i];
359         
360         // Did not find property with name <inName>, return NULL property pointer
361         return result;
362 }
363
364
365
366 //
367 // Get text description of property with name <inName>, returns an empty string if there is no property named <inName>
368 //
369 STR_String CValue::GetPropertyText(const STR_String & inName,const STR_String& deftext)
370 {
371         CValue *property = GetProperty(inName);
372         if (property)
373                 return property->GetText();
374         else
375                 return deftext;//String::sEmpty;
376 }
377
378 float CValue::GetPropertyNumber(const STR_String& inName,float defnumber)
379 {
380         CValue *property = GetProperty(inName);
381         if (property)
382                 return property->GetNumber();
383         else
384                 return defnumber;
385 }
386
387
388
389 //
390 // Remove the property named <inName>, returns true if the property was succesfully removed, false if property was not found or could not be removed
391 //
392 bool CValue::RemoveProperty(const STR_String & inName)
393 {
394         // Check if there are properties at all which can be removed
395         if (m_pNamedPropertyArray == NULL)
396                 return false;
397
398         CValue* val = GetProperty(inName);
399         if (NULL != val) 
400         {
401                 val->Release();
402                 m_pNamedPropertyArray->erase(inName);
403                 return true;
404         }
405         return false;
406 }
407
408 //
409 // Get Property Names
410 //
411 vector<STR_String> CValue::GetPropertyNames()
412 {
413         vector<STR_String> result;
414         if(!m_pNamedPropertyArray) return result;
415         for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
416         !(it == m_pNamedPropertyArray->end());it++)
417         {
418                 result.push_back((*it).first);
419         }
420         return result;
421 }
422
423 //
424 // Clear all properties
425 //
426 void CValue::ClearProperties()
427 {               
428         // Check if we have any properties
429         if (m_pNamedPropertyArray == NULL)
430                 return;
431
432         // Remove all properties
433         for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
434         !(it == m_pNamedPropertyArray->end());it++)
435         {
436                 CValue* tmpval = (*it).second;
437                 //STR_String name = (*it).first;
438                 tmpval->Release();
439         }
440
441         // Delete property array
442         delete m_pNamedPropertyArray;
443         m_pNamedPropertyArray=NULL;
444 }
445
446
447
448 //
449 // Set all properties' modified flag to <inModified>
450 //
451 void CValue::SetPropertiesModified(bool inModified)
452 {
453         int numprops = GetPropertyCount();
454         for (int i=0; i<numprops; i++)
455                 GetProperty(i)->SetModified(inModified);
456 }
457
458
459
460 //
461 // Check if any of the properties in this value have been modified
462 //
463 bool CValue::IsAnyPropertyModified()
464 {
465         int numprops = GetPropertyCount();
466         for (int i=0;i<numprops;i++)
467                 if (GetProperty(i)->IsModified())
468                         return true;
469
470         return false;
471 }
472
473
474
475 //
476 // Get property number <inIndex>
477 //
478
479 CValue* CValue::GetProperty(int inIndex)
480 {
481
482         int count=0;
483         CValue* result = NULL;
484
485         if (m_pNamedPropertyArray)
486         {
487                 for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
488                 !(it == m_pNamedPropertyArray->end());it++)
489                 {
490                         if (count++==inIndex)
491                         {
492                                 result = (*it).second;
493                                 break;
494                         }
495                 }
496
497         }
498         return result;
499 }
500
501
502
503 //
504 // Get the amount of properties assiocated with this value
505 //
506 int CValue::GetPropertyCount()
507 {
508         if (m_pNamedPropertyArray)
509                 return m_pNamedPropertyArray->size();
510         else
511                 return 0;
512 }
513
514
515
516
517
518 void CValue::CloneProperties(CValue *replica)
519 {
520         
521         if (m_pNamedPropertyArray)
522         {
523                 replica->m_pNamedPropertyArray=NULL;
524                 for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
525                 !(it == m_pNamedPropertyArray->end());it++)
526                 {
527                         CValue *val = (*it).second->GetReplica();
528                         replica->SetProperty((*it).first,val);
529                         val->Release();
530                 }
531         }
532
533         
534 }
535
536 double*         CValue::GetVector3(bool bGetTransformedVec)
537 {
538         assertd(false); // don;t get vector from me
539         return m_sZeroVec;//::sZero;
540 }
541
542
543 /*---------------------------------------------------------------------------------------------------------------------
544         Reference Counting
545 ---------------------------------------------------------------------------------------------------------------------*/
546 //
547 // Add a reference to this value
548 //
549 CValue *CValue::AddRef()
550 {
551         // Increase global reference count, used to see at the end of the program
552         // if all CValue-derived classes have been dereferenced to 0
553         //debug(gRefCountValue++);
554 #ifdef _DEBUG
555         //gRefCountValue++;
556 #endif
557         m_refcount++; 
558         return this;
559 }
560
561
562
563 //
564 // Release a reference to this value (when reference count reaches 0, the value is removed from the heap)
565 //
566 int     CValue::Release()
567 {
568         // Decrease global reference count, used to see at the end of the program
569         // if all CValue-derived classes have been dereferenced to 0
570         //debug(gRefCountValue--);
571 #ifdef _DEBUG
572         //gRefCountValue--;
573 #endif
574         // Decrease local reference count, if it reaches 0 the object should be freed
575         if (--m_refcount > 0)
576         {
577                 // Reference count normal, return new reference count
578                 return m_refcount;
579         }
580         else
581         {
582                 // Reference count reached 0, delete ourselves and return 0
583 //              MT_assert(m_refcount==0, "Reference count reached sub-zero, object released too much");
584                 delete this;
585                 return 0;
586         }
587
588 }
589
590
591
592 //
593 // Disable reference counting for this value
594 //
595 void CValue::DisableRefCount()
596 {
597         assertd(m_refcount == 1);
598         m_refcount--;
599
600         //debug(gRefCountValue--);
601 #ifdef _DEBUG
602         //gRefCountValue--;
603 #endif
604         m_ValFlags.RefCountDisabled=true;
605 }
606
607
608
609 void CValue::AddDataToReplica(CValue *replica)
610 {
611         replica->m_refcount = 1;
612
613         //register with Python
614         _Py_NewReference(replica);
615
616 #ifdef _DEBUG
617         //gRefCountValue++;
618 #endif
619         replica->m_ValFlags.RefCountDisabled = false;
620
621         replica->ReplicaSetName(GetName());
622
623         //copy all props
624         CloneProperties(replica);
625 }
626
627
628
629 CValue* CValue::FindIdentifier(const STR_String& identifiername)
630 {
631
632         CValue* result = NULL;
633
634         int pos = 0;
635         // if a dot exists, explode the name into pieces to get the subcontext
636         if ((pos=identifiername.Find('.'))>=0)
637         {
638                 const STR_String rightstring = identifiername.Right(identifiername.Length() -1 - pos);
639                 const STR_String leftstring = identifiername.Left(pos);
640                 CValue* tempresult = GetProperty(leftstring);
641                 if (tempresult)
642                 {
643                         result=tempresult->FindIdentifier(rightstring);
644                 } 
645         } else
646         {
647                 result = GetProperty(identifiername);
648                 if (result)
649                         return result->AddRef();
650         }
651         if (!result)
652         {
653                 // warning here !!!
654                 result = new CErrorValue(identifiername+" not found");
655         }
656         return result;
657 }
658
659
660 #ifndef NO_EXP_PYTHON_EMBEDDING
661
662
663 static PyMethodDef      CValueMethods[] = 
664 {
665         //{ "new", CValue::PyMake , Py_NEWARGS},
666         { NULL,NULL}    // Sentinel
667 };
668
669
670 PyObject*       CValue::_getattr(const STR_String& attr)
671 {
672         CValue* resultattr = FindIdentifier(attr);
673         STR_String text;
674         if (resultattr)
675         {
676                 if (resultattr->IsError())
677                 {
678                         resultattr->Release();
679                 } else
680                 {
681                         // to avoid some compare problems, return a real pythonthing
682                         PyObject* pyconvert = resultattr->ConvertValueToPython();
683                         if (pyconvert)
684                         {
685                                 resultattr->Release();
686                                 return pyconvert;
687                         } else
688                         {
689                                 // also check if it's already in pythoninterpreter!
690                                 return resultattr;
691                         }
692                         
693                 }
694         }
695         _getattr_up(PyObjectPlus);
696 }
697
698 CValue* CValue::ConvertPythonToValue(PyObject* pyobj)
699 {
700
701         CValue* vallie = NULL;
702
703         PyTypeObject* type = pyobj->ob_type;
704
705         if (type == &PyList_Type)
706         {
707                 CListValue* listval = new CListValue();
708                 bool error = false;
709
710                 int i;
711                 int numitems = PyList_Size(pyobj);
712                 for (i=0;i<numitems;i++)
713                 {
714                         PyObject* listitem = PyList_GetItem(pyobj,i); /* borrowed ref */
715                         CValue* listitemval = ConvertPythonToValue(listitem);
716                         if (listitemval)
717                         {
718                                 listval->Add(listitemval);
719                         } else
720                         {
721                                 error = true;
722                         }
723                 }
724                 if (!error)
725                 {
726                         // jippie! could be converted
727                         vallie = listval;
728                 } else
729                 {
730                         // list could not be converted... bad luck
731                         listval->Release();
732                 }
733
734         } else
735         if (type == &PyFloat_Type)
736         {
737                 float fl;
738                 PyArg_Parse(pyobj,"f",&fl);
739                 vallie = new CFloatValue(fl);
740         } else
741         if (type==&PyInt_Type)
742         {
743                 int innie;
744                 PyArg_Parse(pyobj,"i",&innie);
745                 vallie = new CIntValue(innie);
746         } else
747         
748         if (type==&PyString_Type)
749         {
750                 vallie = new CStringValue(PyString_AsString(pyobj),"");
751         } else
752         if (type==&CValue::Type || type==&CListValue::Type)
753         {
754                 vallie = ((CValue*) pyobj)->AddRef();
755         }
756         return vallie;
757
758 }
759
760 int     CValue::_delattr(const STR_String& attr)
761 {
762         RemoveProperty(attr);
763         return 0;
764 }
765
766 int     CValue::_setattr(const STR_String& attr,PyObject* pyobj)
767 {
768         CValue* vallie = ConvertPythonToValue(pyobj);
769         if (vallie)
770         {
771                 CValue* oldprop = GetProperty(attr);
772                 
773                 if (oldprop)
774                 {
775                         oldprop->SetValue(vallie);
776                 } else
777                 {
778                         SetProperty(attr,vallie);
779                 }
780                 vallie->Release();
781         }
782         
783         //PyObjectPlus::_setattr(attr,value);
784         return 0;
785 };
786
787 PyObject*       CValue::ConvertKeysToPython( void )
788 {
789         PyObject *pylist = PyList_New( 0 );
790         PyObject *pystr;
791         
792         if (m_pNamedPropertyArray)
793         {
794                 for ( std::map<STR_String,CValue*>::iterator it = m_pNamedPropertyArray->begin();
795                 !(it == m_pNamedPropertyArray->end());it++)
796                 {
797                         pystr = PyString_FromString( (*it).first );
798                         PyList_Append(pylist, pystr);
799                         Py_DECREF( pystr );
800                 }
801         }
802         return pylist;
803 }
804
805 /*
806 PyObject*       CValue::PyMake(PyObject* ignored,PyObject* args)
807 {
808
809         //Py_Try(PyArg_ParseTuple(args,"s",&name));
810         Py_INCREF(Py_None);
811         return Py_None;//new CValue();
812 }
813 */
814
815 extern "C" {
816         void initCValue(void)
817         {
818                 Py_InitModule("CValue",CValueMethods);
819         }
820 }
821
822
823
824 #endif //NO_EXP_PYTHON_EMBEDDING
825
826 ///////////////////////////////////////////////////////////////////////////////////////////////
827 ///////////////////////////////////////////////////////////////////////////////////////////////
828 /* These implementations were moved out of the header */
829
830 void CValue::SetOwnerExpression(class CExpression* expr)
831 {
832         /* intentionally empty */
833 }
834
835 void CValue::SetColorOperator(VALUE_OPERATOR op)
836 {
837         /* intentionally empty */
838 }
839 void CValue::SetValue(CValue* newval)
840
841         // no one should get here
842         assertd(newval->GetNumber() == 10121969);       
843 }
844 ///////////////////////////////////////////////////////////////////////////////////////////////
845 ///////////////////////////////////////////////////////////////////////////////////////////////
846