comparison liboctave/ArrayN-idx.h @ 4513:508238e65af7

[project @ 2003-09-19 21:40:57 by jwe]
author jwe
date Fri, 19 Sep 2003 21:41:21 +0000
parents e944fbe3fff2
children 4c8a2e4e0717
comparison
equal deleted inserted replaced
4512:b55eaa010770 4513:508238e65af7
24 #include "Array-flags.h" 24 #include "Array-flags.h"
25 #include "Range.h" 25 #include "Range.h"
26 #include "idx-vector.h" 26 #include "idx-vector.h"
27 #include "lo-error.h" 27 #include "lo-error.h"
28 28
29 template <class T> 29 #if 0
30 void
31 ArrayN<T>::maybe_delete_elements (Array<idx_vector>& idx, const T& rfv)
32 {
33 int n_idx = idx.length ();
34
35 Array<int> lhs_dims = dims ();
36
37 Array<int> idx_is_colon (n_idx, 0);
38 Array<int> idx_is_colon_equiv (n_idx, 0);
39
40 // Initialization of colon arrays.
41
42 for (int i = 0; i < n_idx; i++)
43 {
44 idx_is_colon_equiv(i) = idx(i).is_colon_equiv (lhs_dims(i), 1);
45
46 idx_is_colon(i) = idx(i).is_colon ();
47 }
48
49 if (all_ones (idx_is_colon) || all_ones (idx_is_colon_equiv))
50 {
51 // A(:,:,:) -- we are deleting elements in all dimensions, so
52 // the result is [](0x0x0).
53
54 Array<int> zeros (n_idx, 0);
55
56 resize (zeros, rfv);
57 }
58
59 else if (num_ones (idx_is_colon) == n_idx - 1
60 && num_ones (idx_is_colon_equiv) == n_idx)
61 {
62 // A(:,:,j) -- we are deleting elements in one dimension by
63 // enumerating them.
64 //
65 // If we enumerate all of the elements, we should have zero
66 // elements in that dimension with the same number of elements
67 // in the other dimensions that we started with.
68
69 Array<int> temp_dims (n_idx,0);
70
71 for (int i = 0; i < n_idx; i++)
72 {
73 if (idx_is_colon (i))
74 temp_dims (i) = lhs_dims (i);
75
76 else
77 temp_dims (i) = 0;
78 }
79 resize (temp_dims);
80 }
81 else if (num_ones (idx_is_colon) == n_idx - 1)
82 {
83 // We have colons in all indices except for one.
84 // This index tells us which slice to delete
85
86 int non_col = 0;
87
88 // Find the non-colon column.
89
90 for (int i = 0; i < n_idx; i++)
91 {
92 if (! idx_is_colon (i))
93 non_col = i;
94 }
95
96 // The length of the non-colon dimension.
97
98 int non_col_dim = lhs_dims (non_col);
99
100 idx(non_col).sort (true);
101
102 int num_to_delete = idx(non_col).length (lhs_dims (non_col));
103
104 if (num_to_delete > 0)
105 {
106 int temp = num_ones(lhs_dims);
107
108 if (non_col_dim == 1)
109 temp--;
110
111 if (temp == n_idx - 1 && num_to_delete == non_col_dim)
112 {
113 // We have A with (1x1x4), where A(1,:,1:4)
114 // Delete all (0x0x0)
115
116 Array<int> zero_dims (n_idx, 0);
117
118 resize (zero_dims, rfv);
119 }
120 else
121 {
122 // New length of non-colon dimension
123 // (calculated in the next for loop)
124
125 int new_dim = non_col_dim;
126
127 int iidx = 0;
128
129 for (int j = 0; j < non_col_dim; j++)
130 if (j == idx(non_col).elem (iidx))
131 {
132 iidx++;
133
134 new_dim--;
135
136 if (iidx == num_to_delete)
137 break;
138 }
139
140 // Creating the new nd array after deletions.
141
142 if (new_dim > 0)
143 {
144 // Calculate number of elements in new array.
145
146 int num_new_elem=1;
147
148 for (int i = 0; i < n_idx; i++)
149 {
150 if (i == non_col)
151 num_new_elem *= new_dim;
152
153 else
154 num_new_elem *= lhs_dims(i);
155 }
156
157 T *new_data = new T [num_new_elem];
158
159 Array<int> result_idx (lhs_dims.length (), 0);
160 Array<int> elt_idx;
161
162 Array<int> lhs_inc (lhs_dims.length ());
163
164 for (int i = 0; i < lhs_dims.length (); i++)
165 lhs_inc(i) = lhs_dims(i) + 1;
166
167 Array<int> new_lhs_dim = lhs_dims;
168
169 new_lhs_dim(non_col) = new_dim;
170
171 int num_elem = 1;
172
173 int numidx = 0;
174
175 int n = length ();
176
177 for (int i =0; i < lhs_dims.length (); i++)
178 if (i != non_col)
179 num_elem *= lhs_dims (i);
180
181 num_elem *= idx(non_col).capacity ();
182
183 for (int i = 0; i < n; i++)
184 {
185 if (numidx < num_elem
186 && is_in (result_idx(non_col), idx(non_col)))
187 numidx++;
188
189 else
190 {
191 Array<int> temp_result_idx = result_idx;
192
193 int num_lgt
194 = how_many_lgt (result_idx(non_col), idx(non_col));
195
196 temp_result_idx(non_col) -= num_lgt;
197
198 int kidx
199 = ::compute_index (temp_result_idx, new_lhs_dim);
200
201 new_data[kidx] = elem (result_idx);
202 }
203
204 increment_index (result_idx, lhs_dims);
205 }
206
207 if (--(Array<T>::rep)->count <= 0)
208 delete Array<T>::rep;
209
210 Array<T>::rep =
211 new typename Array<T>::ArrayRep (new_data, num_new_elem);
212
213 dimensions = new_lhs_dim;
214
215 set_max_indices (new_lhs_dim.length ());
216 }
217 }
218 }
219 }
220 else if (num_ones(idx_is_colon) < n_idx)
221 {
222 (*current_liboctave_error_handler)
223 ("A null assignment can have only one non-colon index.");
224 }
225 }
226 30
227 template <class T> 31 template <class T>
228 ArrayN<T> 32 ArrayN<T>
229 ArrayN<T>::value (void) 33 ArrayN<T>::value (void)
230 { 34 {
258 clear_index (); 62 clear_index ();
259 63
260 return retval; 64 return retval;
261 } 65 }
262 66
263 template <class T> 67 #endif
264 ArrayN<T>
265 ArrayN<T>::index (idx_vector& ra_idx, int resize_ok, const T& rfv) const
266 {
267 ArrayN<T> retval;
268 assert (0);
269 return retval;
270 }
271 68
272 static inline Array<int>
273 freeze (Array<idx_vector>& ra_idx, const Array<int>& dimensions, int resize_ok)
274 {
275 Array<int> retval;
276
277 int n = ra_idx.length ();
278
279 assert (n == dimensions.length ());
280
281 retval.resize (n);
282
283 for (int i = 0; i < n; i++)
284 retval(i) = ra_idx(i).freeze (dimensions(i), "XXX FIXME XXX", resize_ok);
285
286 return retval;
287 }
288
289 static inline bool
290 vector_equivalent (const Array<int>& ra_idx)
291 {
292 int n = ra_idx.length ();
293
294 bool found_first = false;
295
296 for (int i = 0; i < n; i++)
297 {
298 if (ra_idx(i) != 1)
299 {
300 if (! found_first)
301 found_first = true;
302 else
303 return false;
304 }
305 }
306
307 return true;
308 }
309
310 static inline bool
311 equal_arrays (const Array<int> a, const Array<int> b)
312 {
313 bool retval = true;
314
315 if (a.length () != b.length ())
316 retval = false;
317 else
318 {
319 for (int i = 0; i < a.length (); i++)
320 {
321 if (a(i) != b(i))
322 retval = false;
323 }
324 }
325
326 return retval;
327 }
328
329 static inline bool
330 all_ok (const Array<idx_vector>& ra_idx)
331 {
332 bool retval = true;
333
334 int n = ra_idx.length ();
335
336 for (int i = 0; i < n; i++)
337 {
338 if (! ra_idx(i))
339 {
340 retval = false;
341 break;
342 }
343 }
344
345 return retval;
346 }
347
348 static inline bool
349 any_orig_empty (const Array<idx_vector>& ra_idx)
350 {
351 bool retval = false;
352
353 int n = ra_idx.length ();
354
355 for (int i = 0; i < n; i++)
356 {
357 if (ra_idx(i).orig_empty ())
358 {
359 retval = true;
360 break;
361 }
362 }
363
364 return retval;
365 }
366
367 static inline bool
368 any_zero_len (const Array<int>& frozen_lengths)
369 {
370 bool retval = false;
371
372 int n = frozen_lengths.length ();
373
374 for (int i = 0; i < n; i++)
375 {
376 if (frozen_lengths(i) == 0)
377 {
378 retval = true;
379 break;
380 }
381 }
382
383 return retval;
384 }
385
386 static inline Array<int>
387 get_zero_len_size (const Array<int>& frozen_lengths,
388 const Array<int>& dimensions)
389 {
390 Array<int> retval;
391 assert (0);
392 return retval;
393 }
394
395 static inline bool
396 all_colon_equiv (const Array<idx_vector>& ra_idx,
397 const Array<int>& frozen_lengths)
398 {
399 bool retval = true;
400
401 int idx_n = ra_idx.length ();
402
403 int n = frozen_lengths.length ();
404
405 assert (idx_n == n);
406
407 for (int i = 0; i < n; i++)
408 {
409 if (! ra_idx(i).is_colon_equiv (frozen_lengths(i)))
410 {
411 retval = false;
412 break;
413 }
414 }
415
416 return retval;
417 }
418
419 static inline bool
420 is_in (int num, const idx_vector& idx)
421 {
422 int n = idx.capacity ();
423
424 for (int i = 0; i < n; i++)
425 if (idx.elem (i) == num)
426 return true;
427
428 return false;
429 }
430
431 static inline int
432 how_many_lgt (const int num, idx_vector& idxv)
433 {
434 int retval = 0;
435
436 int n = idxv.capacity ();
437
438 for (int i = 0; i < n; i++)
439 if (num > idxv.elem (i))
440 retval++;
441
442 return retval;
443 }
444
445 static inline bool
446 all_ones (const Array<int> arr)
447 {
448 bool retval = true;
449
450 for (int i = 0; i < arr.length (); i++)
451 {
452 if (arr(i) != 1)
453 {
454 retval = false;
455 break;
456 }
457 }
458
459 return retval;
460 }
461
462 static Array<int>
463 get_elt_idx (const Array<idx_vector>& ra_idx, const Array<int>& result_idx)
464 {
465 int n = ra_idx.length ();
466
467 Array<int> retval (n);
468
469 for (int i = 0; i < n; i++)
470 retval(i) = ra_idx(i).elem (result_idx(i));
471
472 return retval;
473 }
474
475 template <class T>
476 ArrayN<T>
477 ArrayN<T>::index (Array<idx_vector>& ra_idx, int resize_ok, const T& rfv) const
478 {
479 ArrayN<T> retval;
480
481 int n_dims = dimensions.length ();
482
483 Array<int> frozen_lengths = freeze (ra_idx, dimensions, resize_ok);
484
485 if (frozen_lengths.length () == n_dims)
486 {
487 if (all_ok (ra_idx))
488 {
489 if (any_orig_empty (ra_idx))
490 {
491 retval.resize (frozen_lengths);
492 }
493 else if (any_zero_len (frozen_lengths))
494 {
495 Array<int> new_size = get_zero_len_size (frozen_lengths,
496 dimensions);
497
498 retval.resize (new_size);
499 }
500 else if (all_colon_equiv (ra_idx, frozen_lengths))
501 {
502 retval = *this;
503 }
504 else
505 {
506 #if 0
507 retval.resize (frozen_lengths);
508
509 int n = Array<T>::get_size (frozen_lengths);
510
511 Array<int> result_idx (n_dims, 0);
512
513 for (int i = 0; i < n; i++)
514 {
515 Array<int> elt_idx = get_elt_idx (result_idx);
516
517 if (elt_idx > orig_len)
518 retval.elem (result_idx) = rfv;
519 else
520 retval.elem (result_idx) = elem (elt_idx);
521
522 increment_index (result_idx, frozen_lengths);
523 }
524 #endif
525 }
526 }
527 // idx_vector::freeze() printed an error message for us.
528 }
529 else
530 (*current_liboctave_error_handler)
531 ("invalid number of dimensions for N-dimensional array index");
532
533 return retval;
534 }
535
536 #define MAYBE_RESIZE_ND_DIMS \
537 do \
538 { \
539 if (n_idx >= lhs_dims.length () && ! rhs_is_empty) \
540 { \
541 Array<int> max_idx (n_idx); \
542 Array<int> new_idx (n_idx); \
543 \
544 for (int i = 0; i < n_idx; i++) \
545 { \
546 if (lhs_dims.length () == 0 || i >= lhs_dims.length ()) \
547 new_idx(i) = idx(i).max () + 1; \
548 else \
549 { \
550 if (i < rhs_dims.length ()) \
551 max_idx(i) = idx(i).is_colon () ? rhs_dims(i) : idx(i).max () + 1; \
552 else \
553 max_idx(i) = idx(i).max () + 1; \
554 \
555 new_idx(i) = max_idx(i) > lhs_dims(i) ? max_idx(i) : lhs_dims(i); \
556 } \
557 } \
558 \
559 lhs.resize (new_idx, rfv); \
560 lhs_dims = lhs.dims (); \
561 } \
562 } \
563 while (0)
564
565 template <class LT, class RT>
566 int
567 assign (ArrayN<LT>& lhs, const ArrayN<RT>& rhs, const LT& rfv)
568 {
569 int retval = 1;
570
571 int n_idx = lhs.index_count ();
572
573 Array<int> lhs_dims = lhs.dims ();
574 Array<int> rhs_dims = rhs.dims ();
575
576 idx_vector *tmp = lhs.get_idx ();
577
578 Array<idx_vector> idx = conv_to_array (tmp, n_idx);
579
580 // This needs to be defined before MAYBE_RESIZE_ND_DIMS.
581
582 bool rhs_is_empty = rhs_dims.length () == 0 ? true : any_zero_len (rhs_dims);
583
584 // Maybe expand to more dimensions.
585
586 MAYBE_RESIZE_ND_DIMS;
587
588 Array<int> idx_is_colon (n_idx, 0);
589 Array<int> idx_is_colon_equiv (n_idx, 0);
590
591 for (int i = 0; i < n_idx; i++)
592 {
593 idx_is_colon_equiv(i) = idx(i).is_colon_equiv (lhs_dims(i), 1);
594
595 idx_is_colon(i) = idx(i).is_colon ();
596 }
597
598 int resize_ok = 1;
599
600 Array<int> frozen_len;
601
602 if (n_idx == lhs_dims.length ())
603 frozen_len = freeze (idx, lhs_dims, resize_ok);
604
605 bool rhs_is_scalar = is_scalar (rhs_dims);
606
607 bool idx_is_empty = any_zero_len (frozen_len);
608
609 if (rhs_is_empty)
610 {
611 lhs.maybe_delete_elements (idx, rfv);
612 }
613 else if (rhs_is_scalar)
614 {
615 if (n_idx == 0)
616 (*current_liboctave_error_handler)
617 ("number of indices is zero.");
618
619 else if (n_idx < lhs_dims.length ())
620 {
621 // Number of indices is less than dimensions.
622
623 if (any_ones (idx_is_colon)|| any_ones (idx_is_colon_equiv))
624 {
625 (*current_liboctave_error_handler)
626 ("number of indices is less than number of dimensions, one or more indices are colons.");
627 }
628 else
629 {
630 // Fewer indices than dimensions, no colons.
631
632 bool resize = false;
633
634 // Subtract one since the last idx do not tell us
635 // anything about dimensionality.
636
637 for (int i = 0; i < idx.length () - 1; i++)
638 {
639 // Subtract one since idx counts from 0 while dims
640 // count from 1.
641
642 if (idx(i).elem (0) + 1 > lhs_dims(i))
643 resize = true;
644 }
645
646 if (resize)
647 {
648 Array<int> new_dims (lhs_dims.length ());
649
650 for (int i = 0; i < lhs_dims.length (); i++)
651 {
652 if (i < idx.length () - 1
653 && idx(i).elem (0) + 1 > lhs_dims(i))
654 new_dims(i) = idx(i).elem (0)+1;
655 else
656 new_dims(i) = lhs_dims(i);
657 }
658
659 lhs.resize (new_dims, rfv);
660
661 lhs_dims = lhs.dims ();
662 }
663
664 Array<int> one_arg_temp (1, 0);
665
666 RT scalar = rhs.elem (one_arg_temp);
667
668 Array<int> int_arr = conv_to_int_array (idx);
669
670 int numelem = get_scalar_idx (int_arr, lhs_dims);
671
672 if (numelem > lhs.length () || numelem < 0)
673 (*current_liboctave_error_handler)
674 ("attempt to grow array along ambiguous dimension.");
675 else
676 lhs.Array<LT>::checkelem (numelem) = scalar;
677 }
678 }
679 else
680 {
681 // Scalar to matrix assignment with as many indices as lhs
682 // dimensions.
683
684 int n = ArrayN<LT>::get_size (frozen_len);
685
686 Array<int> result_idx (lhs_dims.length (), 0);
687
688 Array<int> elt_idx;
689
690 Array<int> one_arg_temp(1,0);
691 RT scalar = rhs.elem (one_arg_temp);
692
693 for (int i = 0; i < n; i++)
694 {
695 elt_idx = get_elt_idx (idx, result_idx);
696
697 Array<int> lhs_inc(lhs_dims.length());
698
699 for (int i = 0; i < lhs_dims.length (); i++)
700 lhs_inc(i) = lhs_dims(i) + 1;
701
702 if (index_in_bounds(elt_idx, lhs_inc))
703 lhs.checkelem (elt_idx) = scalar;
704 else
705 lhs.checkelem (elt_idx) = rfv;
706
707 increment_index (result_idx, frozen_len);
708 }
709 }
710 }
711 else if (rhs_dims.length () >= 2)
712 {
713 // RHS is matrix or higher dimension.
714
715 // Subtracting number of dimensions of length 1 will catch
716 // cases where: A(2,1,2)=3 A(:,1,:)=[2,3;4,5]
717
718 if (rhs_dims.length () != num_ones(idx_is_colon_equiv) - num_ones(lhs_dims))
719 {
720 (*current_liboctave_error_handler)
721 ("dimensions do not match in matrix assignment.");
722 }
723 else
724 {
725 bool dim_ok(true);
726
727 int jj = 0;
728
729 // Check that RHS dimensions are the same length as the
730 // corresponding LHS dimensions.
731
732 for (int j = 0; j < idx_is_colon.length (); j++)
733 {
734 if (idx_is_colon(j) || idx_is_colon_equiv(j))
735 {
736 if (rhs_dims(jj) < lhs_dims(j))
737 {
738 dim_ok = false;
739
740 break;
741 }
742
743 jj++;
744 }
745 }
746
747 if (! dim_ok)
748 (*current_liboctave_error_handler)
749 ("subscripted assignment dimension mismatch.");
750 else
751 {
752 Array<int> new_dims (n_idx);
753
754 bool resize = false;
755
756 int ii = 0;
757
758 // Update idx vectors.
759
760 for (int i = 0; i < n_idx; i++)
761 {
762 if (idx(i).is_colon ())
763 {
764 // Add appropriate idx_vector to idx(i) since
765 // index with : contains no indexes.
766
767 frozen_len(i) = lhs_dims(i) > rhs_dims(ii) ? lhs_dims(i) : rhs_dims(ii);
768
769 new_dims(i) = lhs_dims(i) > rhs_dims(ii) ? lhs_dims(i) : rhs_dims(ii);
770
771 ii++;
772
773 Range idxrange (1, frozen_len(i), 1);
774
775 idx_vector idxv (idxrange);
776
777 idx(i) = idxv;
778 }
779 else
780 {
781 new_dims(i) = lhs_dims(i) > idx(i).max () + 1 ? lhs_dims(i) : idx(i).max () + 1;
782
783 if (frozen_len(i) > 1)
784 ii++;
785 }
786 if (new_dims(i) != lhs_dims(i))
787 resize = true;
788 }
789
790 // Resize LHS if dimensions have changed.
791
792 if (resize)
793 {
794 lhs.resize (new_dims, rfv);
795
796 lhs_dims = lhs.dims ();
797 }
798
799 // Number of elements which need to be set.
800
801 int n = ArrayN<LT>::get_size (frozen_len);
802
803 Array<int> result_idx (lhs_dims.length (), 0);
804 Array<int> elt_idx;
805
806 Array<int> result_rhs_idx (rhs_dims.length (), 0);
807 Array<int> frozen_rhs (rhs_dims.length(), 0);
808
809 for (int i = 0; i < rhs_dims.length (); i++)
810 frozen_rhs(i) = rhs_dims(i);
811
812 Array<int> lhs_inc (lhs_dims.length ());
813
814 for (int i = 0; i < lhs_dims.length (); i++)
815 lhs_inc(i) = lhs_dims(i) + 1;
816
817 for (int i = 0; i < n; i++)
818 {
819 elt_idx = get_elt_idx (idx, result_idx);
820
821 if (index_in_bounds (elt_idx, lhs_inc))
822 {
823 int s = compute_index (result_rhs_idx,rhs_dims);
824
825 lhs.checkelem (elt_idx) = rhs.Array<RT>::elem (s);
826
827 increment_index (result_rhs_idx, frozen_rhs);
828 }
829 else
830 lhs.checkelem (elt_idx) = rfv;
831
832 increment_index (result_idx, frozen_len);
833 }
834 }
835 }
836 }
837 else if (idx_is_empty)
838 {
839 // Assignment to matrix with at least one empty index.
840
841 if (! rhs_is_empty || ! rhs_is_scalar)
842 {
843 (*current_liboctave_error_handler)
844 ("A([], []) = X: X must be an empty matrix or a scalar");
845
846 retval = 0;
847 }
848 }
849 else if (lhs_dims.length () != rhs_dims.length ())
850 {
851 (*current_liboctave_error_handler)
852 ("A(I) = X: X must be a scalar or a matrix with the same size as I");
853 retval = 0;
854 }
855
856 return retval;
857 }
858
859 static inline int
860 get_scalar_idx (Array<int>& idx, Array<int>& dims)
861 {
862 int retval (-1);
863
864 int n = idx.length ();
865
866 if (n > 0)
867 {
868 retval = idx(--n);
869
870 while (--n >= 0)
871 {
872 retval *= dims (n);
873
874 retval += idx(n);
875 }
876 }
877 return retval;
878 }
879
880 static inline int
881 num_ones (const Array<int> ra_idx)
882 {
883 int retval (0);
884 for (int i = 0; i < ra_idx.length (); i++)
885 {
886 if (ra_idx (i) == 1)
887 retval++;
888 }
889 return retval;
890 }
891
892 static inline bool
893 is_scalar (const Array<int>& dim)
894 {
895 bool retval = true;
896
897 int n = dim.length ();
898
899 if (n == 0)
900 {
901 retval = false;
902 }
903 else
904 {
905 for (int i = 0; i < n; i ++)
906 {
907 if (dim (i) != 1)
908 {
909 retval = false;
910
911 break;
912 }
913 }
914 }
915 return retval;
916 }
917
918 static inline bool
919 any_ones (const Array<int> arr)
920 {
921 bool retval = false;
922
923 for (int i = 0; i < arr.length (); i++)
924 {
925 if (arr (i) == 1)
926 {
927 retval = true;
928
929 break;
930 }
931 }
932 return retval;
933 }
934
935 static inline int
936 compute_index (const Array<int>& ra_idx, const Array<int>& dims)
937 {
938 int retval = -1;
939
940 int n = dims.length ();
941
942 if (n > 0 && n == ra_idx.length ())
943 {
944 retval = ra_idx(--n);
945
946 while (--n >= 0)
947 {
948 retval *= dims(n);
949
950 retval += ra_idx(n);
951 }
952 }
953 else
954 (*current_liboctave_error_handler)
955 ("ArrayN<T>::compute_index: invalid ra_idxing operation");
956
957 return retval;
958 }
959
960 static inline Array<int>
961 conv_to_int_array (const Array<idx_vector>& a)
962 {
963 Array<int> retval (a.length ());
964
965 for (int i = 0; i < a.length (); i++)
966 retval (i) = a(i).elem (0);
967
968 return retval;
969 }
970
971 static inline Array<idx_vector>
972 conv_to_array (const idx_vector *tmp, const int len)
973 {
974 Array<idx_vector> retval (len);
975
976 for (int i = 0; i < len; i++)
977 retval (i) = tmp[i];
978
979 return retval;
980 }
981 /* 69 /*
982 ;;; Local Variables: *** 70 ;;; Local Variables: ***
983 ;;; mode: C++ *** 71 ;;; mode: C++ ***
984 ;;; End: *** 72 ;;; End: ***
985 */ 73 */