998a5a2d396f0cc37e6d83f0d522cb16c5ed3a8c
[scilab.git] / scilab / modules / scicos_blocks / src / c / cevscpe.c
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2011 - Scilab Enterprises - Clement DAVID
4  *
5  *  This file must be used under the terms of the CeCILL.
6  *  This source file is licensed as described in the file COPYING, which
7  *  you should have received as part of this distribution.  The terms
8  *  are also available at
9  *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 #include <string.h>
14
15 #include "dynlib_scicos_blocks.h"
16 #include "scoUtils.h"
17
18 #include "MALLOC.h"
19 #include "elementary_functions.h"
20 #include "string.h"
21
22 #include "getGraphicObjectProperty.h"
23 #include "setGraphicObjectProperty.h"
24 #include "graphicObjectProperties.h"
25 #include "createGraphicObject.h"
26
27 #include "CurrentFigure.h"
28 #include "CurrentObject.h"
29
30 #include "scicos_block4.h"
31 #include "scicos.h"
32
33 #include "localization.h"
34 #ifdef _MSC_VER
35 #include "strdup_windows.h"
36 #endif
37
38 #include "FigureList.h"
39 #include "BuildObjects.h"
40 #include "AxesModel.h"
41
42 /*****************************************************************************
43  * Internal container structure
44  ****************************************************************************/
45
46 // optimization parameters (available draws without realloc)
47 #define DEFAULT_MAX_NUMBER_OF_POINTS 8
48
49 /**
50  * Container structure
51  */
52 typedef struct
53 {
54     struct
55     {
56         int *numberOfPoints;
57         int *maxNumberOfPoints;
58         double **data;
59     } internal;
60
61     struct
62     {
63         int periodCounter;
64
65         char const* cachedFigureUID;
66         char *cachedAxeUID;
67         char **cachedSegsUIDs;
68     } scope;
69 } sco_data;
70
71 /**
72  * Get (and allocate on demand) the internal data used on this scope
73  * \param block the block
74  * \return the scope data
75  */
76 static sco_data *getScoData(scicos_block * block);
77
78 /**
79  * Release any internal data
80  *
81  * \param block the block
82  */
83 static void freeScoData(scicos_block * block);
84
85 /**
86  * Append the data to the current data
87  *
88  * \param block the block
89  * \param input the input (0-indexed)
90  * \param t the current time
91  */
92 static void appendData(scicos_block * block, int input, double t);
93
94 /**
95  * Push the block data to the segs
96  *
97  * \param block the block
98  * \param input the selected input
99  *
100  */
101 static BOOL pushData(scicos_block * block, int input);
102
103 /*****************************************************************************
104  * Graphics utils
105  ****************************************************************************/
106
107 /**
108  * Get (and allocate on demand) the figure associated with the block
109  * \param block the block
110  * \return a valid figure UID or NULL on error
111  */
112 static char const* getFigure(scicos_block * block);
113
114 /**
115  * Get (and allocate on demand) the axe associated with the input
116  *
117  * \param pFigureUID the parent figure UID
118  * \param block the block
119  * \return a valid axe UID or NULL on error
120  */
121 static char *getAxe(char const* pFigureUID, scicos_block * block);
122
123 /**
124  * Get (and allocate on demand) the segs associated with the input
125  *
126  * \param pAxeUID the parent axe UID
127  * \param block the block
128  * \param input the current row index (0-indexed)
129  * \return a valid polyline UID or NULL on error
130  */
131 static char *getSegs(char *pAxeUID, scicos_block * block, int input);
132
133 /**
134  * Set the segs buffer size
135  *
136  * \param block the block
137  * \param maxNumberOfPoints the size of the buffer
138  */
139 static BOOL setSegsBuffers(scicos_block * block, int maxNumberOfPoints);
140
141 /**
142  * Set the axes bounds
143  *
144  * \param block the block
145  * \param periodCounter number of past periods since startup
146  */
147 static BOOL setBounds(scicos_block * block, int periodCounter);
148
149 /*****************************************************************************
150  * Simulation function
151  ****************************************************************************/
152
153 /** \fn void cscope(scicos_block * block,int flag)
154     \brief the computational function
155     \param block A pointer to a scicos_block
156     \param flag An int which indicates the state of the block (init, update, ending)
157 */
158 SCICOS_BLOCKS_IMPEXP void cevscpe(scicos_block * block, scicos_flag flag)
159 {
160     char const* pFigureUID;
161
162     double t;
163     int i;
164     int mask;
165     int nclk = block->nipar - 6;
166     sco_data *sco;
167
168     BOOL result;
169
170     switch (flag)
171     {
172
173         case Initialization:
174             sco = getScoData(block);
175             if (sco == NULL)
176             {
177                 set_block_error(-5);
178             }
179             pFigureUID = getFigure(block);
180             if (pFigureUID == NULL)
181             {
182                 // allocation error
183                 set_block_error(-5);
184                 break;
185             }
186
187             setSegsBuffers(block, DEFAULT_MAX_NUMBER_OF_POINTS);
188             break;
189
190         case StateUpdate:
191             pFigureUID = getFigure(block);
192             if (pFigureUID == NULL)
193             {
194                 // allocation error
195                 set_block_error(-5);
196                 break;
197             }
198
199             t = get_scicos_time();
200
201             // select only the masked indexes
202             for (i = 0; i < nclk; i++)
203             {
204                 mask = 1 << i;
205                 if ((block->nevprt & mask) == mask)
206                 {
207                     appendData(block, i, t);
208
209                     result = pushData(block, i);
210                     if (result == FALSE)
211                     {
212                         Coserror("%s: unable to push some data.", "cevscpe");
213                         break;
214                     }
215                 }
216             }
217             break;
218
219         case Ending:
220             freeScoData(block);
221             break;
222
223         default:
224             break;
225     }
226 }
227
228 /*-------------------------------------------------------------------------*/
229
230 /*****************************************************************************
231  *
232  * Container management
233  *
234  ****************************************************************************/
235
236 static sco_data *getScoData(scicos_block * block)
237 {
238     sco_data *sco = (sco_data *) * (block->work);
239     int i, j;
240
241     int nclk = block->nipar - 6;
242
243     if (sco == NULL)
244     {
245         /*
246          * Data allocation
247          */
248
249         sco = (sco_data *) MALLOC(sizeof(sco_data));
250         if (sco == NULL)
251             goto error_handler_sco;
252
253         sco->internal.numberOfPoints = (int *)CALLOC(nclk, sizeof(int));
254         if (sco->internal.numberOfPoints == NULL)
255         {
256             goto error_handler_numberOfPoints;
257         }
258         sco->internal.maxNumberOfPoints = (int *)MALLOC(nclk * sizeof(int));
259         if (sco->internal.numberOfPoints == NULL)
260         {
261             goto error_handler_maxNumberOfPoints;
262         }
263         for (i = 0; i < nclk; i++)
264         {
265             sco->internal.maxNumberOfPoints[i] = DEFAULT_MAX_NUMBER_OF_POINTS;
266         }
267
268         sco->internal.data = (double **)CALLOC(2 * nclk, sizeof(double *));
269         if (sco->internal.data == NULL)
270             goto error_handler_data;
271
272         for (i = 0; i < nclk; i++)
273         {
274             /*
275              * Alloc base pointer
276              */
277             sco->internal.data[2 * i + 0] = (double *)CALLOC(3 * DEFAULT_MAX_NUMBER_OF_POINTS, sizeof(double));
278             if (sco->internal.data[2 * i + 0] == NULL)
279                 goto error_handler_data_i;
280
281             /*
282              * Alloc direction pointer
283              */
284             sco->internal.data[2 * i + 1] = (double *)CALLOC(3 * DEFAULT_MAX_NUMBER_OF_POINTS, sizeof(double));
285             if (sco->internal.data[2 * i + 1] == NULL)
286             {
287                 FREE(sco->internal.data[2 * i + 0]);
288                 goto error_handler_data_i;
289             }
290         }
291
292         sco->scope.periodCounter = 0;
293         sco->scope.cachedFigureUID = NULL;
294         sco->scope.cachedAxeUID = NULL;
295         sco->scope.cachedSegsUIDs = (char **)CALLOC(nclk, sizeof(char *));
296         if (sco->scope.cachedSegsUIDs == NULL)
297         {
298             goto error_handler_data_i;
299         }
300
301         *(block->work) = sco;
302     }
303
304     return sco;
305
306     /*
307      * Error management (out of normal flow)
308      */
309
310 error_handler_data_i:
311     for (j = 0; j < i; j++)
312     {
313         FREE(sco->internal.data[2 * j + 0]);
314         FREE(sco->internal.data[2 * j + 1]);
315     }
316     FREE(sco->internal.data);
317 error_handler_data:
318     FREE(sco->internal.maxNumberOfPoints);
319 error_handler_maxNumberOfPoints:
320     FREE(sco->internal.numberOfPoints);
321 error_handler_numberOfPoints:
322     FREE(sco);
323 error_handler_sco:
324     // allocation error
325     set_block_error(-5);
326     return NULL;
327 }
328
329 static void freeScoData(scicos_block * block)
330 {
331     sco_data *sco = getScoData(block);
332     int i;
333
334     int nclk = block->nipar - 6;
335
336     if (sco != NULL)
337     {
338         for (i = 0; i < nclk; i++)
339         {
340             FREE(sco->internal.data[i]);
341         }
342         FREE(sco->internal.data);
343         FREE(sco->internal.maxNumberOfPoints);
344         FREE(sco->internal.numberOfPoints);
345
346         for (i = 0; i < nclk; i++)
347         {
348             FREE(sco->scope.cachedSegsUIDs[i]);
349         }
350         FREE(sco->scope.cachedAxeUID);
351
352         FREE(sco);
353     }
354 }
355
356 static sco_data *reallocScoData(scicos_block * block, int input, int numberOfPoints)
357 {
358     sco_data *sco = getScoData(block);
359
360     double *ptr;
361     int setLen;
362     int previousNumberOfPoints = sco->internal.maxNumberOfPoints[input];
363
364     /*
365      * Realloc base pointer
366      */
367     ptr = (double *)REALLOC(sco->internal.data[2 * input], 3 * numberOfPoints * sizeof(double));
368     if (ptr == NULL)
369         goto error_handler;
370
371     for (setLen = numberOfPoints - previousNumberOfPoints - 1; setLen >= 0; setLen--)
372     {
373         ptr[3 * (previousNumberOfPoints + setLen) + 0] = ptr[3 * (previousNumberOfPoints - 1) + 0];
374         ptr[3 * (previousNumberOfPoints + setLen) + 1] = ptr[3 * (previousNumberOfPoints - 1) + 1];
375         ptr[3 * (previousNumberOfPoints + setLen) + 2] = ptr[3 * (previousNumberOfPoints - 1) + 2];
376     }
377     sco->internal.data[2 * input] = ptr;
378
379     /*
380      * Realloc direction pointer
381      */
382     ptr = (double *)REALLOC(sco->internal.data[2 * input + 1], 3 * numberOfPoints * sizeof(double));
383     if (ptr == NULL)
384         goto error_handler;
385
386     for (setLen = numberOfPoints - previousNumberOfPoints - 1; setLen >= 0; setLen--)
387     {
388         ptr[3 * (previousNumberOfPoints + setLen) + 0] = ptr[3 * (previousNumberOfPoints - 1) + 0];
389         ptr[3 * (previousNumberOfPoints + setLen) + 1] = ptr[3 * (previousNumberOfPoints - 1) + 1];
390         ptr[3 * (previousNumberOfPoints + setLen) + 2] = ptr[3 * (previousNumberOfPoints - 1) + 2];
391     }
392     sco->internal.data[2 * input + 1] = ptr;
393
394     sco->internal.maxNumberOfPoints[input] = numberOfPoints;
395     return sco;
396
397 error_handler:
398     freeScoData(block);
399     // allocation error
400     set_block_error(-5);
401     return NULL;
402 }
403
404 static void appendData(scicos_block * block, int input, double t)
405 {
406     sco_data *sco = getScoData(block);
407     int maxNumberOfPoints = sco->internal.maxNumberOfPoints[input];
408     int numberOfPoints = sco->internal.numberOfPoints[input];
409
410     int i;
411     int nclk = block->nipar - 6;
412
413     /*
414      * Handle the case where the t is greater than the data_bounds
415      */
416     if (t > ((sco->scope.periodCounter + 1) * block->rpar[0]))
417     {
418         sco->scope.periodCounter++;
419
420         // reset the number of points for all the segs
421         numberOfPoints = 0;
422         for (i = 0; i < nclk; i++)
423         {
424             sco->internal.numberOfPoints[i] = 0;
425         }
426         if (setBounds(block, sco->scope.periodCounter) == FALSE)
427         {
428             set_block_error(-5);
429             freeScoData(block);
430             sco = NULL;
431         }
432     }
433
434     /*
435      * Handle the case where the scope has more points than maxNumberOfPoints
436      */
437     if (sco != NULL && numberOfPoints >= maxNumberOfPoints)
438     {
439         // on a full scope, re-alloc
440         maxNumberOfPoints = maxNumberOfPoints + DEFAULT_MAX_NUMBER_OF_POINTS;
441         sco = reallocScoData(block, input, maxNumberOfPoints);
442
443         // reconfigure related graphic objects
444         if (setSegsBuffers(block, maxNumberOfPoints) == FALSE)
445         {
446             set_block_error(-5);
447             freeScoData(block);
448             sco = NULL;
449         }
450     }
451
452     /*
453      * Update data
454      */
455     if (sco != NULL)
456     {
457         int setLen;
458
459         /*
460          * Base pointer
461          */
462         for (setLen = maxNumberOfPoints - numberOfPoints - 1; setLen >= 0; setLen--)
463         {
464             sco->internal.data[2 * input + 0][3 * (numberOfPoints + setLen) + 0] = t;   // x
465             sco->internal.data[2 * input + 0][3 * (numberOfPoints + setLen) + 1] = 0;   // y
466             sco->internal.data[2 * input + 0][3 * (numberOfPoints + setLen) + 2] = (double) input;   // z
467         }
468
469         /*
470          * Direction pointer
471          */
472         for (setLen = maxNumberOfPoints - numberOfPoints - 1; setLen >= 0; setLen--)
473         {
474             sco->internal.data[2 * input + 1][3 * (numberOfPoints + setLen) + 0] = t;   // x
475             sco->internal.data[2 * input + 1][3 * (numberOfPoints + setLen) + 1] = 0.8; // y
476             sco->internal.data[2 * input + 1][3 * (numberOfPoints + setLen) + 2] = (double) input;   // z
477         }
478
479         sco->internal.numberOfPoints[input]++;
480     }
481 }
482
483 static BOOL pushData(scicos_block * block, int input)
484 {
485     char const* pFigureUID;
486     char *pAxeUID;
487     char *pSegsUID;
488
489     int dataLen;
490     double *base;
491     double *direction;
492     sco_data *sco;
493
494     BOOL result = TRUE;
495
496     pFigureUID = getFigure(block);
497     pAxeUID = getAxe(pFigureUID, block);
498     pSegsUID = getSegs(pAxeUID, block, input);
499
500     sco = getScoData(block);
501     if (sco == NULL)
502         return FALSE;
503
504     // select the right input and row
505     base = sco->internal.data[2 * input];
506     direction = sco->internal.data[2 * input + 1];
507
508     dataLen = 3 * sco->internal.maxNumberOfPoints[input];
509
510     result &= setGraphicObjectProperty(pSegsUID, __GO_BASE__, base, jni_double_vector, dataLen);
511     result &= setGraphicObjectProperty(pSegsUID, __GO_DIRECTION__, direction, jni_double_vector, dataLen);
512
513     return result;
514 }
515
516 /*****************************************************************************
517  *
518  * Graphic utils
519  *
520  ****************************************************************************/
521
522 /**
523  * Set properties on the figure.
524  *
525  * \param pFigureUID the figure uid
526  * \param block the current block
527  */
528 static void setFigureSettings(char const* pFigureUID, scicos_block * block)
529 {
530     int nipar = GetNipar(block);
531     int *ipar = GetIparPtrs(block);
532
533     int win_pos[2];
534     int win_dim[2];
535
536     win_pos[0] = ipar[(nipar - 1) - 3];
537     win_pos[1] = ipar[(nipar - 1) - 2];
538     win_dim[0] = ipar[(nipar - 1) - 1];
539     win_dim[1] = ipar[nipar - 1];
540
541     if (win_pos[0] > 0 && win_pos[1] > 0)
542     {
543         setGraphicObjectProperty(pFigureUID, __GO_POSITION__, &win_pos, jni_int_vector, 2);
544     }
545
546     if (win_dim[0] > 0 && win_dim[1] > 0)
547     {
548         setGraphicObjectProperty(pFigureUID, __GO_SIZE__, &win_dim, jni_int_vector, 2);
549     }
550 };
551
552 /*****************************************************************************
553  *
554  * Graphic
555  *
556  ****************************************************************************/
557
558 static char const* getFigure(scicos_block * block)
559 {
560     signed int figNum;
561     char const* pFigureUID = NULL;
562     char *pAxe = NULL;
563     int i__1 = 1;
564     sco_data *sco = getScoData(block);
565
566     // fast path for an existing object
567     if (sco->scope.cachedFigureUID != NULL)
568     {
569         return sco->scope.cachedFigureUID;
570     }
571
572     figNum = block->ipar[0];
573
574     // with a negative id, use the block number indexed from a constant.
575     if (figNum < 0)
576     {
577         figNum = 20000 + get_block_number();
578     }
579
580     pFigureUID = getFigureFromIndex(figNum);
581     // create on demand
582     if (pFigureUID == NULL)
583     {
584         pFigureUID = createNewFigureWithAxes();
585         setGraphicObjectProperty(pFigureUID, __GO_ID__, &figNum, jni_int, 1);
586
587         // the stored uid is a reference to the figure map, not to the current figure
588         pFigureUID = getFigureFromIndex(figNum);
589         sco->scope.cachedFigureUID = pFigureUID;
590
591         // set configured parameters
592         setFigureSettings(pFigureUID, block);
593
594         // allocate the axes through the getter
595         pAxe = getAxe(pFigureUID, block);
596
597         /*
598          * Setup according to block settings
599          */
600         setLabel(pAxe, __GO_X_AXIS_LABEL__, "t");
601         setLabel(pAxe, __GO_Y_AXIS_LABEL__, "y");
602
603         setGraphicObjectProperty(pAxe, __GO_X_AXIS_VISIBLE__, &i__1, jni_bool, 1);
604         setGraphicObjectProperty(pAxe, __GO_Y_AXIS_VISIBLE__, &i__1, jni_bool, 1);
605
606         setBounds(block, 0);
607     }
608
609     if (sco->scope.cachedFigureUID == NULL)
610     {
611         sco->scope.cachedFigureUID = pFigureUID;
612     }
613     return pFigureUID;
614 }
615
616 static char *getAxe(char const* pFigureUID, scicos_block * block)
617 {
618     char *pAxe;
619     int i;
620
621     int nclk = block->nipar - 6;
622     sco_data *sco = getScoData(block);
623
624     // fast path for an existing object
625     if (sco->scope.cachedAxeUID != NULL)
626     {
627         return sco->scope.cachedAxeUID;
628     }
629
630     pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, 0);
631
632     /*
633      * Allocate if necessary
634      */
635     if (pAxe == NULL)
636     {
637         cloneAxesModel(pFigureUID);
638         pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, 0);
639     }
640
641     /*
642      * Setup on first access
643      */
644     if (pAxe != NULL)
645     {
646         // allocate the segs through the getter
647         for (i = 0; i < nclk; i++)
648         {
649             getSegs(pAxe, block, i);
650         }
651     }
652
653     /*
654      * then cache with a local storage
655      */
656     if (pAxe != NULL && sco->scope.cachedAxeUID == NULL)
657     {
658         sco->scope.cachedAxeUID = strdup(pAxe);
659         releaseGraphicObjectProperty(__GO_PARENT__, pAxe, jni_string, 1);
660     }
661     return sco->scope.cachedAxeUID;
662 }
663
664 static char *getSegs(char *pAxeUID, scicos_block * block, int input)
665 {
666     char *pSegs;
667     BOOL b__true = TRUE;
668     double d__1 = 1.0;
669     double d__0 = 0.0;
670
671     int color;
672
673     sco_data *sco = getScoData(block);
674
675     // fast path for an existing object
676     if (sco->scope.cachedSegsUIDs != NULL && sco->scope.cachedSegsUIDs[input] != NULL)
677     {
678         return sco->scope.cachedSegsUIDs[input];
679     }
680
681     pSegs = findChildWithKindAt(pAxeUID, __GO_SEGS__, input);
682
683     /*
684      * Allocate if necessary
685      */
686     if (pSegs == NULL)
687     {
688         pSegs = createGraphicObject(__GO_SEGS__);
689
690         if (pSegs != NULL)
691         {
692             createDataObject(pSegs, __GO_SEGS__);
693             setGraphicObjectRelationship(pAxeUID, pSegs);
694         }
695     }
696
697     /*
698      * Setup on first access
699      */
700     if (pSegs != NULL)
701     {
702         setGraphicObjectProperty(pSegs, __GO_NUMBER_ARROWS__, &sco->internal.maxNumberOfPoints[input], jni_int, 1);
703
704         // Setup properties
705         setGraphicObjectProperty(pSegs, __GO_LINE_THICKNESS__, &d__1, jni_double, 1);
706         setGraphicObjectProperty(pSegs, __GO_ARROW_SIZE__, &d__0, jni_double, 1);
707
708         color = block->ipar[2 + input];
709         if (color > 0)
710         {
711             setGraphicObjectProperty(pSegs, __GO_LINE_MODE__, &b__true, jni_bool, 1);
712             setGraphicObjectProperty(pSegs, __GO_SEGS_COLORS__, &color, jni_int_vector, 1);
713         }
714         else
715         {
716             color = -color;
717             setGraphicObjectProperty(pSegs, __GO_MARK_MODE__, &b__true, jni_bool, 1);
718             setGraphicObjectProperty(pSegs, __GO_MARK_STYLE__, &color, jni_int, 1);
719         }
720
721         {
722             int iClipState = 1; //on
723             setGraphicObjectProperty(pSegs, __GO_CLIP_STATE__, &iClipState, jni_int, 1);
724         }
725     }
726
727     /*
728      * then cache with a local storage
729      */
730     if (pSegs != NULL && sco->scope.cachedSegsUIDs != NULL && sco->scope.cachedSegsUIDs[input] == NULL)
731     {
732         sco->scope.cachedSegsUIDs[input] = strdup(pSegs);
733         releaseGraphicObjectProperty(__GO_PARENT__, pSegs, jni_string, 1);
734     }
735     return sco->scope.cachedSegsUIDs[input];
736 }
737
738 static BOOL setSegsBuffers(scicos_block * block, int maxNumberOfPoints)
739 {
740     char const* pFigureUID;
741     char *pAxeUID;
742     char *pSegsUID;
743
744     int i;
745     int nclk = block->nipar - 6;
746     BOOL result = TRUE;
747
748     int color;
749
750     pFigureUID = getFigure(block);
751     pAxeUID = getAxe(pFigureUID, block);
752     for (i = 0; i < nclk; i++)
753     {
754         pSegsUID = getSegs(pAxeUID, block, i);
755         result &= setGraphicObjectProperty(pSegsUID, __GO_NUMBER_ARROWS__, &maxNumberOfPoints, jni_int, 1);
756
757         /*
758          * Update color due to bug #9902
759          * http://bugzilla.scilab.org/show_bug.cgi?id=9902
760          */
761         color = block->ipar[2 + i];
762         if (color > 0)
763         {
764             setGraphicObjectProperty(pSegsUID, __GO_SEGS_COLORS__, &color, jni_int_vector, 1);
765         }
766     }
767
768     return result;
769 }
770
771 static BOOL setBounds(scicos_block * block, int periodCounter)
772 {
773     char const* pFigureUID;
774     char *pAxeUID;
775
776     double dataBounds[6];
777     double period = block->rpar[0];
778
779     dataBounds[0] = periodCounter * period; // xMin
780     dataBounds[1] = (periodCounter + 1) * period;   // xMax
781     dataBounds[2] = 0;          // yMin
782     dataBounds[3] = 1;          // yMax
783     dataBounds[4] = -1.0;       // zMin
784     dataBounds[5] = 1.0;        // zMax
785
786     pFigureUID = getFigure(block);
787     pAxeUID = getAxe(pFigureUID, block);
788     return setGraphicObjectProperty(pAxeUID, __GO_DATA_BOUNDS__, dataBounds, jni_double_vector, 6);
789 }