1 /* $Id: CoinModelUseful.cpp 2083 2019-01-06 19:38:09Z unxusr $ */
2 // Copyright (C) 2005, International Business Machines
3 // Corporation and others.  All Rights Reserved.
4 // This code is licensed under the terms of the Eclipse Public License (EPL).
5 
6 #include "CoinPragma.hpp"
7 
8 #include <cstdlib>
9 #include <cmath>
10 #include <cassert>
11 #include <cfloat>
12 #include <string>
13 #include <cstdio>
14 #include <iostream>
15 
16 #include "CoinHelperFunctions.hpp"
17 
18 #include "CoinModelUseful.hpp"
19 
20 //#############################################################################
21 // Constructors / Destructor / Assignment
22 //#############################################################################
23 
24 //-------------------------------------------------------------------
25 // Default Constructor
26 //-------------------------------------------------------------------
CoinModelLink()27 CoinModelLink::CoinModelLink()
28   : row_(-1)
29   , column_(-1)
30   , value_(0.0)
31   , position_(-1)
32   , onRow_(true)
33 {
34 }
35 
36 //-------------------------------------------------------------------
37 // Copy constructor
38 //-------------------------------------------------------------------
CoinModelLink(const CoinModelLink & rhs)39 CoinModelLink::CoinModelLink(const CoinModelLink &rhs)
40   : row_(rhs.row_)
41   , column_(rhs.column_)
42   , value_(rhs.value_)
43   , position_(rhs.position_)
44   , onRow_(rhs.onRow_)
45 {
46 }
47 
48 //-------------------------------------------------------------------
49 // Destructor
50 //-------------------------------------------------------------------
~CoinModelLink()51 CoinModelLink::~CoinModelLink()
52 {
53 }
54 
55 //----------------------------------------------------------------
56 // Assignment operator
57 //-------------------------------------------------------------------
58 CoinModelLink &
operator =(const CoinModelLink & rhs)59 CoinModelLink::operator=(const CoinModelLink &rhs)
60 {
61   if (this != &rhs) {
62     row_ = rhs.row_;
63     column_ = rhs.column_;
64     value_ = rhs.value_;
65     position_ = rhs.position_;
66     onRow_ = rhs.onRow_;
67   }
68   return *this;
69 }
70 
71 namespace {
72 const int mmult[] = {
73   262139, 259459, 256889, 254291, 251701, 249133, 246709, 244247,
74   241667, 239179, 236609, 233983, 231289, 228859, 226357, 223829,
75   221281, 218849, 216319, 213721, 211093, 208673, 206263, 203773,
76   201233, 198637, 196159, 193603, 191161, 188701, 186149, 183761,
77   181303, 178873, 176389, 173897, 171469, 169049, 166471, 163871,
78   161387, 158941, 156437, 153949, 151531, 149159, 146749, 144299,
79   141709, 139369, 136889, 134591, 132169, 129641, 127343, 124853,
80   122477, 120163, 117757, 115361, 112979, 110567, 108179, 105727,
81   103387, 101021, 98639, 96179, 93911, 91583, 89317, 86939, 84521,
82   82183, 79939, 77587, 75307, 72959, 70793, 68447, 66103
83 };
84 const int lengthMult = static_cast< int >(sizeof(mmult) / sizeof(int));
85 }
86 
87 //#############################################################################
88 // Constructors / Destructor / Assignment
89 //#############################################################################
90 
91 //-------------------------------------------------------------------
92 // Default Constructor
93 //-------------------------------------------------------------------
CoinModelHash()94 CoinModelHash::CoinModelHash()
95   : names_(NULL)
96   , hash_(NULL)
97   , numberItems_(0)
98   , maximumItems_(0)
99   , lastSlot_(-1)
100 {
101 }
102 
103 //-------------------------------------------------------------------
104 // Copy constructor
105 //-------------------------------------------------------------------
CoinModelHash(const CoinModelHash & rhs)106 CoinModelHash::CoinModelHash(const CoinModelHash &rhs)
107   : names_(NULL)
108   , hash_(NULL)
109   , numberItems_(rhs.numberItems_)
110   , maximumItems_(rhs.maximumItems_)
111   , lastSlot_(rhs.lastSlot_)
112 {
113   if (maximumItems_) {
114     names_ = new char *[maximumItems_];
115     for (int i = 0; i < maximumItems_; i++) {
116       names_[i] = CoinStrdup(rhs.names_[i]);
117     }
118     hash_ = CoinCopyOfArray(rhs.hash_, 4 * maximumItems_);
119   }
120 }
121 
122 //-------------------------------------------------------------------
123 // Destructor
124 //-------------------------------------------------------------------
~CoinModelHash()125 CoinModelHash::~CoinModelHash()
126 {
127   for (int i = 0; i < maximumItems_; i++)
128     free(names_[i]);
129   delete[] names_;
130   delete[] hash_;
131 }
132 
133 //----------------------------------------------------------------
134 // Assignment operator
135 //-------------------------------------------------------------------
136 CoinModelHash &
operator =(const CoinModelHash & rhs)137 CoinModelHash::operator=(const CoinModelHash &rhs)
138 {
139   if (this != &rhs) {
140     for (int i = 0; i < maximumItems_; i++)
141       free(names_[i]);
142     delete[] names_;
143     delete[] hash_;
144     numberItems_ = rhs.numberItems_;
145     maximumItems_ = rhs.maximumItems_;
146     lastSlot_ = rhs.lastSlot_;
147     if (maximumItems_) {
148       names_ = new char *[maximumItems_];
149       for (int i = 0; i < maximumItems_; i++) {
150         names_[i] = CoinStrdup(rhs.names_[i]);
151       }
152       hash_ = CoinCopyOfArray(rhs.hash_, 4 * maximumItems_);
153     } else {
154       names_ = NULL;
155       hash_ = NULL;
156     }
157   }
158   return *this;
159 }
160 // Set number of items
setNumberItems(int number)161 void CoinModelHash::setNumberItems(int number)
162 {
163   assert(number >= 0 && number <= numberItems_);
164   numberItems_ = number;
165 }
166 // Resize hash (also re-hashs)
resize(int maxItems,bool forceReHash)167 void CoinModelHash::resize(int maxItems, bool forceReHash)
168 {
169   assert(numberItems_ <= maximumItems_);
170   if (maxItems <= maximumItems_ && !forceReHash)
171     return;
172   int n = maximumItems_;
173   maximumItems_ = maxItems;
174   char **names = new char *[maximumItems_];
175   int i;
176   for (i = 0; i < n; i++)
177     names[i] = names_[i];
178   for (; i < maximumItems_; i++)
179     names[i] = NULL;
180   delete[] names_;
181   names_ = names;
182   delete[] hash_;
183   int maxHash = 4 * maximumItems_;
184   hash_ = new CoinModelHashLink[maxHash];
185   int ipos;
186 
187   for (i = 0; i < maxHash; i++) {
188     hash_[i].index = -1;
189     hash_[i].next = -1;
190   }
191 
192   /*
193    * Initialize the hash table.  Only the index of the first name that
194    * hashes to a value is entered in the table; subsequent names that
195    * collide with it are not entered.
196    */
197   for (i = 0; i < numberItems_; ++i) {
198     if (names_[i]) {
199       ipos = hashValue(names_[i]);
200       if (hash_[ipos].index == -1) {
201         hash_[ipos].index = i;
202       }
203     }
204   }
205 
206   /*
207    * Now take care of the names that collided in the preceding loop,
208    * by finding some other entry in the table for them.
209    * Since there are as many entries in the table as there are names,
210    * there must be room for them.
211    */
212   lastSlot_ = -1;
213   for (i = 0; i < numberItems_; ++i) {
214     if (!names_[i])
215       continue;
216     char *thisName = names[i];
217     ipos = hashValue(thisName);
218 
219     while (true) {
220       int j1 = hash_[ipos].index;
221 
222       if (j1 == i)
223         break;
224       else {
225         char *thisName2 = names[j1];
226 
227         if (strcmp(thisName, thisName2) == 0) {
228           printf("** duplicate name %s\n", names[i]);
229           abort();
230           break;
231         } else {
232           int k = hash_[ipos].next;
233 
234           if (k == -1) {
235             while (true) {
236               ++lastSlot_;
237               if (lastSlot_ > numberItems_) {
238                 printf("** too many names\n");
239                 abort();
240                 break;
241               }
242               if (hash_[lastSlot_].index == -1) {
243                 break;
244               }
245             }
246             hash_[ipos].next = lastSlot_;
247             hash_[lastSlot_].index = i;
248             break;
249           } else {
250             ipos = k;
251             /* nothing worked - try it again */
252           }
253         }
254       }
255     }
256   }
257 }
258 // validate
validateHash() const259 void CoinModelHash::validateHash() const
260 {
261   for (int i = 0; i < numberItems_; ++i) {
262     if (names_[i]) {
263       assert(hash(names_[i]) >= 0);
264     }
265   }
266 }
267 // Returns index or -1
hash(const char * name) const268 int CoinModelHash::hash(const char *name) const
269 {
270   int found = -1;
271 
272   int ipos;
273 
274   /* default if we don't find anything */
275   if (!numberItems_)
276     return -1;
277 
278   ipos = hashValue(name);
279   while (true) {
280     int j1 = hash_[ipos].index;
281 
282     if (j1 >= 0) {
283       char *thisName2 = names_[j1];
284 
285       if (strcmp(name, thisName2) != 0) {
286         int k = hash_[ipos].next;
287 
288         if (k != -1)
289           ipos = k;
290         else
291           break;
292       } else {
293         found = j1;
294         break;
295       }
296     } else {
297       int k = hash_[ipos].next;
298 
299       if (k != -1)
300         ipos = k;
301       else
302         break;
303     }
304   }
305   return found;
306 }
307 // Adds to hash
addHash(int index,const char * name)308 void CoinModelHash::addHash(int index, const char *name)
309 {
310   // resize if necessary
311   if (numberItems_ >= maximumItems_)
312     resize(1000 + 3 * numberItems_ / 2);
313   assert(!names_[index]);
314   names_[index] = CoinStrdup(name);
315   int ipos = hashValue(name);
316   numberItems_ = CoinMax(numberItems_, index + 1);
317   if (hash_[ipos].index < 0) {
318     hash_[ipos].index = index;
319   } else {
320     while (true) {
321       int j1 = hash_[ipos].index;
322 
323       if (j1 == index)
324         break; // duplicate?
325       else {
326         if (j1 >= 0) {
327           char *thisName2 = names_[j1];
328           if (strcmp(name, thisName2) == 0) {
329             printf("** duplicate name %s\n", names_[index]);
330             abort();
331             break;
332           } else {
333             int k = hash_[ipos].next;
334 
335             if (k == -1) {
336               while (true) {
337                 ++lastSlot_;
338                 if (lastSlot_ > numberItems_) {
339                   printf("** too many names\n");
340                   abort();
341                   break;
342                 }
343                 if (hash_[lastSlot_].index < 0 && hash_[lastSlot_].next < 0) {
344                   break;
345                 }
346               }
347               hash_[ipos].next = lastSlot_;
348               hash_[lastSlot_].index = index;
349               hash_[lastSlot_].next = -1;
350               break;
351             } else {
352               ipos = k;
353             }
354           }
355         } else {
356           //slot available
357           hash_[ipos].index = index;
358         }
359       }
360     }
361   }
362 }
363 // Deletes from hash
deleteHash(int index)364 void CoinModelHash::deleteHash(int index)
365 {
366   if (index < numberItems_ && names_[index]) {
367 
368     int ipos = hashValue(names_[index]);
369 
370     while (ipos >= 0) {
371       int j1 = hash_[ipos].index;
372       if (j1 != index) {
373         ipos = hash_[ipos].next;
374       } else {
375         hash_[ipos].index = -1; // available
376         break;
377       }
378     }
379     assert(ipos >= 0);
380     free(names_[index]);
381     names_[index] = NULL;
382   }
383 }
384 // Returns name at position (or NULL)
385 const char *
name(int which) const386 CoinModelHash::name(int which) const
387 {
388   if (which < numberItems_)
389     return names_[which];
390   else
391     return NULL;
392 }
393 // Returns non const name at position (or NULL)
394 char *
getName(int which) const395 CoinModelHash::getName(int which) const
396 {
397   if (which < numberItems_)
398     return names_[which];
399   else
400     return NULL;
401 }
402 // Sets name at position (does not create)
setName(int which,char * name)403 void CoinModelHash::setName(int which, char *name)
404 {
405   if (which < numberItems_)
406     names_[which] = name;
407 }
408 // Returns a hash value
hashValue(const char * name) const409 int CoinModelHash::hashValue(const char *name) const
410 {
411 
412   int n = 0;
413   int j;
414   int length = static_cast< int >(strlen(name));
415   // may get better spread with unsigned
416   const unsigned char *name2 = reinterpret_cast< const unsigned char * >(name);
417   while (length) {
418     int length2 = CoinMin(length, lengthMult);
419     for (j = 0; j < length2; ++j) {
420       n += mmult[j] * name2[j];
421     }
422     name += length2;
423     length -= length2;
424   }
425   int maxHash = 4 * maximumItems_;
426   return (abs(n) % maxHash); /* integer abs */
427 }
428 //#############################################################################
429 // Constructors / Destructor / Assignment
430 //#############################################################################
431 //-------------------------------------------------------------------
432 // Default Constructor
433 //-------------------------------------------------------------------
CoinModelHash2()434 CoinModelHash2::CoinModelHash2()
435   : hash_(NULL)
436   , numberItems_(0)
437   , maximumItems_(0)
438   , lastSlot_(-1)
439 {
440 }
441 
442 //-------------------------------------------------------------------
443 // Copy constructor
444 //-------------------------------------------------------------------
CoinModelHash2(const CoinModelHash2 & rhs)445 CoinModelHash2::CoinModelHash2(const CoinModelHash2 &rhs)
446   : hash_(NULL)
447   , numberItems_(rhs.numberItems_)
448   , maximumItems_(rhs.maximumItems_)
449   , lastSlot_(rhs.lastSlot_)
450 {
451   if (maximumItems_) {
452     hash_ = CoinCopyOfArray(rhs.hash_, 4 * maximumItems_);
453   }
454 }
455 
456 //-------------------------------------------------------------------
457 // Destructor
458 //-------------------------------------------------------------------
~CoinModelHash2()459 CoinModelHash2::~CoinModelHash2()
460 {
461   delete[] hash_;
462 }
463 
464 //----------------------------------------------------------------
465 // Assignment operator
466 //-------------------------------------------------------------------
467 CoinModelHash2 &
operator =(const CoinModelHash2 & rhs)468 CoinModelHash2::operator=(const CoinModelHash2 &rhs)
469 {
470   if (this != &rhs) {
471     delete[] hash_;
472     numberItems_ = rhs.numberItems_;
473     maximumItems_ = rhs.maximumItems_;
474     lastSlot_ = rhs.lastSlot_;
475     if (maximumItems_) {
476       hash_ = CoinCopyOfArray(rhs.hash_, 4 * maximumItems_);
477     } else {
478       hash_ = NULL;
479     }
480   }
481   return *this;
482 }
483 // Set number of items
setNumberItems(CoinBigIndex number)484 void CoinModelHash2::setNumberItems(CoinBigIndex number)
485 {
486   assert(number >= 0 && (number <= numberItems_ || !numberItems_));
487   numberItems_ = number;
488 }
489 // Resize hash (also re-hashs)
resize(CoinBigIndex maxItems,const CoinModelTriple * triples,bool forceReHash)490 void CoinModelHash2::resize(CoinBigIndex maxItems, const CoinModelTriple *triples, bool forceReHash)
491 {
492   assert(numberItems_ <= maximumItems_ || !maximumItems_);
493   if (maxItems <= maximumItems_ && !forceReHash)
494     return;
495   if (maxItems > maximumItems_) {
496     maximumItems_ = maxItems;
497     delete[] hash_;
498     hash_ = new CoinModelHashLink2[4 * maximumItems_];
499   }
500   CoinBigIndex maxHash = 4 * maximumItems_;
501   CoinBigIndex ipos;
502   CoinBigIndex i;
503   for (i = 0; i < maxHash; i++) {
504     hash_[i].index = -1;
505     hash_[i].next = -1;
506   }
507 
508   /*
509    * Initialize the hash table.  Only the index of the first name that
510    * hashes to a value is entered in the table; subsequent names that
511    * collide with it are not entered.
512    */
513   for (i = 0; i < numberItems_; ++i) {
514     int row = static_cast< int >(rowInTriple(triples[i]));
515     int column = triples[i].column;
516     if (column >= 0) {
517       ipos = hashValue(row, column);
518       if (hash_[ipos].index == -1) {
519         hash_[ipos].index = i;
520       }
521     }
522   }
523 
524   /*
525    * Now take care of the entries that collided in the preceding loop,
526    * by finding some other entry in the table for them.
527    * Since there are as many entries in the table as there are entries,
528    * there must be room for them.
529    */
530   lastSlot_ = -1;
531   for (i = 0; i < numberItems_; ++i) {
532     int row = static_cast< int >(rowInTriple(triples[i]));
533     int column = triples[i].column;
534     if (column >= 0) {
535       ipos = hashValue(row, column);
536 
537       while (true) {
538         CoinBigIndex j1 = hash_[ipos].index;
539 
540         if (j1 == i)
541           break;
542         else {
543           int row2 = static_cast< int >(rowInTriple(triples[j1]));
544           int column2 = triples[j1].column;
545           if (row == row2 && column == column2) {
546             printf("** duplicate entry %d %d\n", row, column);
547             abort();
548             break;
549           } else {
550             CoinBigIndex k = hash_[ipos].next;
551 
552             if (k == -1) {
553               while (true) {
554                 ++lastSlot_;
555                 if (lastSlot_ > numberItems_) {
556                   printf("** too many entries\n");
557                   abort();
558                   break;
559                 }
560                 if (hash_[lastSlot_].index == -1) {
561                   break;
562                 }
563               }
564               hash_[ipos].next = lastSlot_;
565               hash_[lastSlot_].index = i;
566               break;
567             } else {
568               ipos = k;
569             }
570           }
571         }
572       }
573     }
574   }
575 }
576 // Returns index or -1
577 CoinBigIndex
hash(int row,int column,const CoinModelTriple * triples) const578 CoinModelHash2::hash(int row, int column, const CoinModelTriple *triples) const
579 {
580   CoinBigIndex found = -1;
581 
582   CoinBigIndex ipos;
583 
584   /* default if we don't find anything */
585   if (!numberItems_)
586     return -1;
587 
588   ipos = hashValue(row, column);
589   while (true) {
590     CoinBigIndex j1 = hash_[ipos].index;
591 
592     if (j1 >= 0) {
593       int row2 = static_cast< int >(rowInTriple(triples[j1]));
594       int column2 = triples[j1].column;
595       if (row != row2 || column != column2) {
596         CoinBigIndex k = hash_[ipos].next;
597 
598         if (k != -1)
599           ipos = k;
600         else
601           break;
602       } else {
603         found = j1;
604         break;
605       }
606     } else {
607       CoinBigIndex k = hash_[ipos].next;
608 
609       if (k != -1)
610         ipos = k;
611       else
612         break;
613     }
614   }
615   return found;
616 }
617 // Adds to hash
addHash(CoinBigIndex index,int row,int column,const CoinModelTriple * triples)618 void CoinModelHash2::addHash(CoinBigIndex index, int row, int column, const CoinModelTriple *triples)
619 {
620   // resize if necessary
621   if (numberItems_ >= maximumItems_ || index + 1 >= maximumItems_)
622     resize(CoinMax(1000 + 3 * numberItems_ / 2, index + 1), triples);
623   CoinBigIndex ipos = hashValue(row, column);
624   numberItems_ = CoinMax(numberItems_, index + 1);
625   assert(numberItems_ <= maximumItems_);
626   if (hash_[ipos].index < 0) {
627     hash_[ipos].index = index;
628   } else {
629     while (true) {
630       CoinBigIndex j1 = hash_[ipos].index;
631 
632       if (j1 == index) {
633         break; // duplicate??
634       } else {
635         if (j1 >= 0) {
636           int row2 = static_cast< int >(rowInTriple(triples[j1]));
637           int column2 = triples[j1].column;
638           if (row == row2 && column == column2) {
639             printf("** duplicate entry %d %d\n", row, column);
640             abort();
641             break;
642           } else {
643             CoinBigIndex k = hash_[ipos].next;
644 
645             if (k == -1) {
646               while (true) {
647                 ++lastSlot_;
648                 if (lastSlot_ > numberItems_) {
649                   printf("** too many entrys\n");
650                   abort();
651                   break;
652                 }
653                 if (hash_[lastSlot_].index < 0) {
654                   break;
655                 }
656               }
657               hash_[ipos].next = lastSlot_;
658               hash_[lastSlot_].index = index;
659               hash_[lastSlot_].next = -1;
660               break;
661             } else {
662               ipos = k;
663             }
664           }
665         } else {
666           // slot available
667           hash_[ipos].index = index;
668         }
669       }
670     }
671   }
672 }
673 // Deletes from hash
deleteHash(CoinBigIndex index,int row,int column)674 void CoinModelHash2::deleteHash(CoinBigIndex index, int row, int column)
675 {
676   if (index < numberItems_) {
677 
678     CoinBigIndex ipos = hashValue(row, column);
679 
680     while (ipos >= 0) {
681       CoinBigIndex j1 = hash_[ipos].index;
682       if (j1 != index) {
683         ipos = hash_[ipos].next;
684       } else {
685         hash_[ipos].index = -1; // available
686         break;
687       }
688     }
689   }
690 }
691 namespace {
692 const int mmult2[] = {
693   262139, 259459, 256889, 254291, 251701, 249133, 246709, 244247,
694   241667, 239179, 236609, 233983, 231289, 228859, 226357, 223829
695 };
696 }
697 // Returns a hash value
698 CoinBigIndex
hashValue(int row,int column) const699 CoinModelHash2::hashValue(int row, int column) const
700 {
701 
702   // Optimizer should take out one side of if
703   if (sizeof(int) == 4 * sizeof(char)) {
704     unsigned char tempChar[4];
705 
706     unsigned int n = 0;
707     int *temp = reinterpret_cast< int * >(tempChar);
708     *temp = row;
709     n += mmult2[0] * tempChar[0];
710     n += mmult2[1] * tempChar[1];
711     n += mmult2[2] * tempChar[2];
712     n += mmult[3] * tempChar[3];
713     *temp = column;
714     n += mmult2[0 + 8] * tempChar[0];
715     n += mmult2[1 + 8] * tempChar[1];
716     n += mmult2[2 + 8] * tempChar[2];
717     n += mmult2[3 + 8] * tempChar[3];
718     return n % (maximumItems_ << 1);
719   } else {
720     // ints are 8
721     unsigned char tempChar[8];
722 
723     int n = 0;
724     unsigned int j;
725     int *temp = reinterpret_cast< int * >(tempChar);
726     *temp = row;
727     for (j = 0; j < sizeof(int); ++j) {
728       int itemp = tempChar[j];
729       n += mmult2[j] * itemp;
730     }
731     *temp = column;
732     for (j = 0; j < sizeof(int); ++j) {
733       int itemp = tempChar[j];
734       n += mmult2[j + 8] * itemp;
735     }
736     CoinBigIndex maxHash = 4 * maximumItems_;
737     CoinBigIndex absN = abs(n);
738     CoinBigIndex returnValue = absN % maxHash;
739     return returnValue;
740   }
741 }
742 //#############################################################################
743 // Constructors / Destructor / Assignment
744 //#############################################################################
745 
746 //-------------------------------------------------------------------
747 // Default Constructor
748 //-------------------------------------------------------------------
CoinModelLinkedList()749 CoinModelLinkedList::CoinModelLinkedList()
750   : previous_(NULL)
751   , next_(NULL)
752   , first_(NULL)
753   , last_(NULL)
754   , numberMajor_(0)
755   , maximumMajor_(0)
756   , numberElements_(0)
757   , maximumElements_(0)
758   , type_(-1)
759 {
760 }
761 
762 //-------------------------------------------------------------------
763 // Copy constructor
764 //-------------------------------------------------------------------
CoinModelLinkedList(const CoinModelLinkedList & rhs)765 CoinModelLinkedList::CoinModelLinkedList(const CoinModelLinkedList &rhs)
766   : numberMajor_(rhs.numberMajor_)
767   , maximumMajor_(rhs.maximumMajor_)
768   , numberElements_(rhs.numberElements_)
769   , maximumElements_(rhs.maximumElements_)
770   , type_(rhs.type_)
771 {
772   if (maximumMajor_) {
773     previous_ = CoinCopyOfArray(rhs.previous_, maximumElements_);
774     next_ = CoinCopyOfArray(rhs.next_, maximumElements_);
775     first_ = CoinCopyOfArray(rhs.first_, maximumMajor_ + 1);
776     last_ = CoinCopyOfArray(rhs.last_, maximumMajor_ + 1);
777   } else {
778     previous_ = NULL;
779     next_ = NULL;
780     first_ = NULL;
781     last_ = NULL;
782   }
783 }
784 
785 //-------------------------------------------------------------------
786 // Destructor
787 //-------------------------------------------------------------------
~CoinModelLinkedList()788 CoinModelLinkedList::~CoinModelLinkedList()
789 {
790   delete[] previous_;
791   delete[] next_;
792   delete[] first_;
793   delete[] last_;
794 }
795 
796 //----------------------------------------------------------------
797 // Assignment operator
798 //-------------------------------------------------------------------
799 CoinModelLinkedList &
operator =(const CoinModelLinkedList & rhs)800 CoinModelLinkedList::operator=(const CoinModelLinkedList &rhs)
801 {
802   if (this != &rhs) {
803     delete[] previous_;
804     delete[] next_;
805     delete[] first_;
806     delete[] last_;
807     numberMajor_ = rhs.numberMajor_;
808     maximumMajor_ = rhs.maximumMajor_;
809     numberElements_ = rhs.numberElements_;
810     maximumElements_ = rhs.maximumElements_;
811     type_ = rhs.type_;
812     if (maximumMajor_) {
813       previous_ = CoinCopyOfArray(rhs.previous_, maximumElements_);
814       next_ = CoinCopyOfArray(rhs.next_, maximumElements_);
815       first_ = CoinCopyOfArray(rhs.first_, maximumMajor_ + 1);
816       last_ = CoinCopyOfArray(rhs.last_, maximumMajor_ + 1);
817     } else {
818       previous_ = NULL;
819       next_ = NULL;
820       first_ = NULL;
821       last_ = NULL;
822     }
823   }
824   return *this;
825 }
826 // Resize list - for row list maxMajor is maximum rows
resize(int maxMajor,CoinBigIndex maxElements)827 void CoinModelLinkedList::resize(int maxMajor, CoinBigIndex maxElements)
828 {
829   maxMajor = CoinMax(maxMajor, maximumMajor_);
830   maxElements = CoinMax(maxElements, maximumElements_);
831   if (maxMajor > maximumMajor_) {
832     CoinBigIndex *first = new CoinBigIndex[maxMajor + 1];
833     CoinBigIndex free;
834     if (maximumMajor_) {
835       CoinMemcpyN(first_, maximumMajor_, first);
836 #ifdef ZEROFAULT
837       memset(first + maximumMajor_, 0, (maxMajor - maximumMajor_) * sizeof(CoinBigIndex));
838 #endif
839       free = first_[maximumMajor_];
840       first[maximumMajor_] = -1;
841     } else {
842       free = -1;
843     }
844     first[maxMajor] = free;
845     delete[] first_;
846     first_ = first;
847     CoinBigIndex *last = new CoinBigIndex[maxMajor + 1];
848     if (maximumMajor_) {
849       CoinMemcpyN(last_, maximumMajor_, last);
850 #ifdef ZEROFAULT
851       memset(last + maximumMajor_, 0, (maxMajor - maximumMajor_) * sizeof(CoinBigIndex));
852 #endif
853       free = last_[maximumMajor_];
854       last[maximumMajor_] = -1;
855     } else {
856       free = -1;
857     }
858     last[maxMajor] = free;
859     delete[] last_;
860     last_ = last;
861     maximumMajor_ = maxMajor;
862   }
863   if (maxElements > maximumElements_) {
864     CoinBigIndex *previous = new CoinBigIndex[maxElements];
865     CoinMemcpyN(previous_, numberElements_, previous);
866 #ifdef ZEROFAULT
867     memset(previous + numberElements_, 0,
868       (maxElements - numberElements_) * sizeof(CoinBigIndex));
869 #endif
870     delete[] previous_;
871     previous_ = previous;
872     CoinBigIndex *next = new CoinBigIndex[maxElements];
873     CoinMemcpyN(next_, numberElements_, next);
874 #ifdef ZEROFAULT
875     memset(next + numberElements_, 0, (maxElements - numberElements_) * sizeof(CoinBigIndex));
876 #endif
877     delete[] next_;
878     next_ = next;
879     maximumElements_ = maxElements;
880   }
881 }
882 // Create list - for row list maxMajor is maximum rows
create(int maxMajor,CoinBigIndex maxElements,int numberMajor,int,int type,CoinBigIndex numberElements,const CoinModelTriple * triples)883 void CoinModelLinkedList::create(int maxMajor, CoinBigIndex maxElements,
884   int numberMajor, int /*numberMinor*/, int type,
885   CoinBigIndex numberElements, const CoinModelTriple *triples)
886 {
887   maxMajor = CoinMax(maxMajor, maximumMajor_);
888   maxMajor = CoinMax(maxMajor, numberMajor);
889   maxElements = CoinMax(maxElements, maximumElements_);
890   maxElements = CoinMax(maxElements, numberElements);
891   type_ = type;
892   assert(!previous_);
893   previous_ = new CoinBigIndex[maxElements];
894   next_ = new CoinBigIndex[maxElements];
895   maximumElements_ = maxElements;
896   assert(maxElements >= numberElements);
897   assert(maxMajor > 0 && !maximumMajor_);
898   first_ = new CoinBigIndex[maxMajor + 1];
899   last_ = new CoinBigIndex[maxMajor + 1];
900 #ifdef ZEROFAULT
901   memset(previous_, 0, maxElements * sizeof(CoinBigIndex));
902   memset(next_, 0, maxElements * sizeof(CoinBigIndex));
903   memset(first_, 0, (maxMajor + 1) * sizeof(CoinBigIndex));
904   memset(last_, 0, (maxMajor + 1) * sizeof(CoinBigIndex));
905 #endif
906   assert(numberElements >= 0);
907   numberElements_ = numberElements;
908   maximumMajor_ = maxMajor;
909   // do lists
910   int i;
911   for (i = 0; i < numberMajor; i++) {
912     first_[i] = -1;
913     last_[i] = -1;
914   }
915   first_[maximumMajor_] = -1;
916   last_[maximumMajor_] = -1;
917   int freeChain = -1;
918   for (i = 0; i < numberElements; i++) {
919     if (triples[i].column >= 0) {
920       int iMajor;
921       if (!type_) {
922         // for rows
923         iMajor = static_cast< int >(rowInTriple(triples[i]));
924       } else {
925         iMajor = triples[i].column;
926       }
927       assert(iMajor < numberMajor);
928       if (first_[iMajor] >= 0) {
929         // not first
930         CoinBigIndex j = last_[iMajor];
931         next_[j] = i;
932         previous_[i] = j;
933       } else {
934         // first
935         first_[iMajor] = i;
936         previous_[i] = -1;
937       }
938       last_[iMajor] = i;
939     } else {
940       // on deleted list
941       if (freeChain >= 0) {
942         next_[freeChain] = i;
943         previous_[i] = freeChain;
944       } else {
945         first_[maximumMajor_] = i;
946         previous_[i] = -1;
947       }
948       freeChain = i;
949     }
950   }
951   // Now clean up
952   if (freeChain >= 0) {
953     next_[freeChain] = -1;
954     last_[maximumMajor_] = freeChain;
955   }
956   for (i = 0; i < numberMajor; i++) {
957     CoinBigIndex k = last_[i];
958     if (k >= 0) {
959       next_[k] = -1;
960       last_[i] = k;
961     }
962   }
963   numberMajor_ = numberMajor;
964 }
965 /* Adds to list - easy case i.e. add row to row list
966    Returns where chain starts
967 */
968 CoinBigIndex
addEasy(int majorIndex,CoinBigIndex numberOfElements,const int * indices,const double * elements,CoinModelTriple * triples,CoinModelHash2 & hash)969 CoinModelLinkedList::addEasy(int majorIndex, CoinBigIndex numberOfElements, const int *indices,
970   const double *elements, CoinModelTriple *triples,
971   CoinModelHash2 &hash)
972 {
973   assert(majorIndex < maximumMajor_);
974   if (numberOfElements + numberElements_ > maximumElements_) {
975     resize(maximumMajor_, (3 * (numberElements_ + numberOfElements)) / 2 + 1000);
976   }
977   CoinBigIndex first = -1;
978   if (majorIndex >= numberMajor_) {
979     for (int i = numberMajor_; i <= majorIndex; i++) {
980       first_[i] = -1;
981       last_[i] = -1;
982     }
983   }
984   if (numberOfElements) {
985     bool doHash = hash.maximumItems() != 0;
986     CoinBigIndex lastFree = last_[maximumMajor_];
987     CoinBigIndex last = last_[majorIndex];
988     for (int i = 0; i < numberOfElements; i++) {
989       CoinBigIndex put;
990       if (lastFree >= 0) {
991         put = lastFree;
992         lastFree = previous_[lastFree];
993       } else {
994         put = numberElements_;
995         assert(put < maximumElements_);
996         numberElements_++;
997       }
998       if (type_ == 0) {
999         // row
1000         setRowAndStringInTriple(triples[put], majorIndex, false);
1001         triples[put].column = indices[i];
1002       } else {
1003         // column
1004         setRowAndStringInTriple(triples[put], indices[i], false);
1005         triples[put].column = majorIndex;
1006       }
1007       triples[put].value = elements[i];
1008       if (doHash)
1009         hash.addHash(put, static_cast< int >(rowInTriple(triples[put])), triples[put].column, triples);
1010       if (last >= 0) {
1011         next_[last] = put;
1012       } else {
1013         first_[majorIndex] = put;
1014       }
1015       previous_[put] = last;
1016       last = put;
1017     }
1018     next_[last] = -1;
1019     if (last_[majorIndex] < 0) {
1020       // first in row
1021       first = first_[majorIndex];
1022     } else {
1023       first = next_[last_[majorIndex]];
1024     }
1025     last_[majorIndex] = last;
1026     if (lastFree >= 0) {
1027       next_[lastFree] = -1;
1028       last_[maximumMajor_] = lastFree;
1029     } else {
1030       first_[maximumMajor_] = -1;
1031       last_[maximumMajor_] = -1;
1032     }
1033   }
1034   numberMajor_ = CoinMax(numberMajor_, majorIndex + 1);
1035   return first;
1036 }
1037 /* Adds to list - hard case i.e. add row to column list
1038  */
addHard(int minorIndex,CoinBigIndex numberOfElements,const int * indices,const double * elements,CoinModelTriple * triples,CoinModelHash2 & hash)1039 void CoinModelLinkedList::addHard(int minorIndex, CoinBigIndex numberOfElements, const int *indices,
1040   const double *elements, CoinModelTriple *triples,
1041   CoinModelHash2 &hash)
1042 {
1043   CoinBigIndex lastFree = last_[maximumMajor_];
1044   bool doHash = hash.maximumItems() != 0;
1045   for (int i = 0; i < numberOfElements; i++) {
1046     CoinBigIndex put;
1047     if (lastFree >= 0) {
1048       put = lastFree;
1049       lastFree = previous_[lastFree];
1050     } else {
1051       put = numberElements_;
1052       assert(put < maximumElements_);
1053       numberElements_++;
1054     }
1055     int other = indices[i];
1056     if (type_ == 0) {
1057       // row
1058       setRowAndStringInTriple(triples[put], other, false);
1059       triples[put].column = minorIndex;
1060     } else {
1061       // column
1062       setRowAndStringInTriple(triples[put], minorIndex, false);
1063       triples[put].column = other;
1064     }
1065     triples[put].value = elements[i];
1066     if (doHash)
1067       hash.addHash(put, static_cast< int >(rowInTriple(triples[put])), triples[put].column, triples);
1068     if (other >= numberMajor_) {
1069       // Need to fill in null values
1070       fill(numberMajor_, other + 1);
1071       numberMajor_ = other + 1;
1072     }
1073     CoinBigIndex last = last_[other];
1074     if (last >= 0) {
1075       next_[last] = put;
1076     } else {
1077       first_[other] = put;
1078     }
1079     previous_[put] = last;
1080     next_[put] = -1;
1081     last_[other] = put;
1082   }
1083   if (lastFree >= 0) {
1084     next_[lastFree] = -1;
1085     last_[maximumMajor_] = lastFree;
1086   } else {
1087     first_[maximumMajor_] = -1;
1088     last_[maximumMajor_] = -1;
1089   }
1090 }
1091 /* Adds to list - hard case i.e. add row to column list
1092    This is when elements have been added to other copy
1093 */
addHard(CoinBigIndex first,const CoinModelTriple * triples,CoinBigIndex firstFree,CoinBigIndex lastFree,const CoinBigIndex * next)1094 void CoinModelLinkedList::addHard(CoinBigIndex first, const CoinModelTriple *triples,
1095   CoinBigIndex firstFree, CoinBigIndex lastFree, const CoinBigIndex *next)
1096 {
1097   first_[maximumMajor_] = firstFree;
1098   last_[maximumMajor_] = lastFree;
1099   CoinBigIndex put = first;
1100   int minorIndex = -1;
1101   while (put >= 0) {
1102     assert(put < maximumElements_);
1103     numberElements_ = CoinMax(numberElements_, put + 1);
1104     int other;
1105     if (type_ == 0) {
1106       // row
1107       other = rowInTriple(triples[put]);
1108       if (minorIndex >= 0)
1109         assert(triples[put].column == minorIndex);
1110       else
1111         minorIndex = triples[put].column;
1112     } else {
1113       // column
1114       other = triples[put].column;
1115       if (minorIndex >= 0)
1116         assert(static_cast< int >(rowInTriple(triples[put])) == minorIndex);
1117       else
1118         minorIndex = rowInTriple(triples[put]);
1119     }
1120     assert(other < maximumMajor_);
1121     if (other >= numberMajor_) {
1122       // Need to fill in null values
1123       fill(numberMajor_, other + 1);
1124       numberMajor_ = other + 1;
1125     }
1126     CoinBigIndex last = last_[other];
1127     if (last >= 0) {
1128       next_[last] = put;
1129     } else {
1130       first_[other] = put;
1131     }
1132     previous_[put] = last;
1133     next_[put] = -1;
1134     last_[other] = put;
1135     put = next[put];
1136   }
1137 }
1138 /* Deletes from list - same case i.e. delete row from row list
1139 */
deleteSame(int which,CoinModelTriple * triples,CoinModelHash2 & hash,bool zapTriples)1140 void CoinModelLinkedList::deleteSame(int which, CoinModelTriple *triples,
1141   CoinModelHash2 &hash, bool zapTriples)
1142 {
1143   assert(which >= 0);
1144   if (which < numberMajor_) {
1145     CoinBigIndex lastFree = last_[maximumMajor_];
1146     CoinBigIndex put = first_[which];
1147     first_[which] = -1;
1148     while (put >= 0) {
1149       if (hash.numberItems()) {
1150         // take out of hash
1151         hash.deleteHash(put, static_cast< int >(rowInTriple(triples[put])), triples[put].column);
1152       }
1153       if (zapTriples) {
1154         triples[put].column = -1;
1155         triples[put].value = 0.0;
1156       }
1157       if (lastFree >= 0) {
1158         next_[lastFree] = put;
1159       } else {
1160         first_[maximumMajor_] = put;
1161       }
1162       previous_[put] = lastFree;
1163       lastFree = put;
1164       put = next_[put];
1165     }
1166     if (lastFree >= 0) {
1167       next_[lastFree] = -1;
1168       last_[maximumMajor_] = lastFree;
1169     } else {
1170       assert(last_[maximumMajor_] == -1);
1171     }
1172     last_[which] = -1;
1173   }
1174 }
1175 /* Deletes from list - other case i.e. delete row from column list
1176    This is when elements have been deleted from other copy
1177 */
updateDeleted(int,CoinModelTriple * triples,CoinModelLinkedList & otherList)1178 void CoinModelLinkedList::updateDeleted(int /*which*/, CoinModelTriple *triples,
1179   CoinModelLinkedList &otherList)
1180 {
1181   CoinBigIndex firstFree = otherList.firstFree();
1182   CoinBigIndex lastFree = otherList.lastFree();
1183   const CoinBigIndex *previousOther = otherList.previous();
1184   assert(maximumMajor_);
1185   if (lastFree >= 0) {
1186     // First free should be same
1187     if (first_[maximumMajor_] >= 0)
1188       assert(firstFree == first_[maximumMajor_]);
1189     CoinBigIndex last = last_[maximumMajor_];
1190     first_[maximumMajor_] = firstFree;
1191     // Maybe nothing to do
1192     if (last_[maximumMajor_] == lastFree)
1193       return;
1194     last_[maximumMajor_] = lastFree;
1195     int iMajor;
1196     if (!type_) {
1197       // for rows
1198       iMajor = static_cast< int >(rowInTriple(triples[lastFree]));
1199     } else {
1200       iMajor = triples[lastFree].column;
1201     }
1202     if (first_[iMajor] >= 0) {
1203       // take out
1204       CoinBigIndex previousThis = previous_[lastFree];
1205       CoinBigIndex nextThis = next_[lastFree];
1206       if (previousThis >= 0 && previousThis != last) {
1207         next_[previousThis] = nextThis;
1208 #ifndef NDEBUG
1209         int iTest;
1210         if (!type_) {
1211           // for rows
1212           iTest = static_cast< int >(rowInTriple(triples[previousThis]));
1213         } else {
1214           iTest = triples[previousThis].column;
1215         }
1216         assert(triples[previousThis].column >= 0);
1217         assert(iTest == iMajor);
1218 #endif
1219       } else {
1220         first_[iMajor] = nextThis;
1221       }
1222       if (nextThis >= 0) {
1223         previous_[nextThis] = previousThis;
1224 #ifndef NDEBUG
1225         int iTest;
1226         if (!type_) {
1227           // for rows
1228           iTest = static_cast< int >(rowInTriple(triples[nextThis]));
1229         } else {
1230           iTest = triples[nextThis].column;
1231         }
1232         assert(triples[nextThis].column >= 0);
1233         assert(iTest == iMajor);
1234 #endif
1235       } else {
1236         last_[iMajor] = previousThis;
1237       }
1238     }
1239     triples[lastFree].column = -1;
1240     triples[lastFree].value = 0.0;
1241     // Do first (by which I mean last)
1242     next_[lastFree] = -1;
1243     CoinBigIndex previous = previousOther[lastFree];
1244     while (previous != last) {
1245       if (previous >= 0) {
1246         if (!type_) {
1247           // for rows
1248           iMajor = static_cast< int >(rowInTriple(triples[previous]));
1249         } else {
1250           iMajor = triples[previous].column;
1251         }
1252         if (first_[iMajor] >= 0) {
1253           // take out
1254           CoinBigIndex previousThis = previous_[previous];
1255           CoinBigIndex nextThis = next_[previous];
1256           if (previousThis >= 0 && previousThis != last) {
1257             next_[previousThis] = nextThis;
1258 #ifndef NDEBUG
1259             int iTest;
1260             if (!type_) {
1261               // for rows
1262               iTest = static_cast< int >(rowInTriple(triples[previousThis]));
1263             } else {
1264               iTest = triples[previousThis].column;
1265             }
1266             assert(triples[previousThis].column >= 0);
1267             assert(iTest == iMajor);
1268 #endif
1269           } else {
1270             first_[iMajor] = nextThis;
1271           }
1272           if (nextThis >= 0) {
1273             previous_[nextThis] = previousThis;
1274 #ifndef NDEBUG
1275             int iTest;
1276             if (!type_) {
1277               // for rows
1278               iTest = static_cast< int >(rowInTriple(triples[nextThis]));
1279             } else {
1280               iTest = triples[nextThis].column;
1281             }
1282             assert(triples[nextThis].column >= 0);
1283             assert(iTest == iMajor);
1284 #endif
1285           } else {
1286             last_[iMajor] = previousThis;
1287           }
1288         }
1289         triples[previous].column = -1;
1290         triples[previous].value = 0.0;
1291         next_[previous] = lastFree;
1292       } else {
1293         assert(lastFree == firstFree);
1294       }
1295       previous_[lastFree] = previous;
1296       lastFree = previous;
1297       previous = previousOther[lastFree];
1298     }
1299     if (last >= 0) {
1300       next_[previous] = lastFree;
1301     } else {
1302       assert(firstFree == lastFree);
1303     }
1304     previous_[lastFree] = previous;
1305   }
1306 }
1307 /* Deletes one element from Row list
1308  */
deleteRowOne(CoinBigIndex position,CoinModelTriple * triples,CoinModelHash2 & hash)1309 void CoinModelLinkedList::deleteRowOne(CoinBigIndex position, CoinModelTriple *triples,
1310   CoinModelHash2 &hash)
1311 {
1312   int row = rowInTriple(triples[position]);
1313   assert(row < numberMajor_);
1314   if (hash.numberItems()) {
1315     // take out of hash
1316     hash.deleteHash(position, static_cast< int >(rowInTriple(triples[position])), triples[position].column);
1317   }
1318   CoinBigIndex previous = previous_[position];
1319   CoinBigIndex next = next_[position];
1320   // put on free list
1321   CoinBigIndex lastFree = last_[maximumMajor_];
1322   if (lastFree >= 0) {
1323     next_[lastFree] = position;
1324   } else {
1325     first_[maximumMajor_] = position;
1326     assert(last_[maximumMajor_] == -1);
1327   }
1328   last_[maximumMajor_] = position;
1329   previous_[position] = lastFree;
1330   next_[position] = -1;
1331   // Now take out of row
1332   if (previous >= 0) {
1333     next_[previous] = next;
1334   } else {
1335     first_[row] = next;
1336   }
1337   if (next >= 0) {
1338     previous_[next] = previous;
1339   } else {
1340     last_[row] = previous;
1341   }
1342 }
1343 /* Update column list for one element when
1344    one element deleted from row copy
1345 */
updateDeletedOne(CoinBigIndex position,const CoinModelTriple * triples)1346 void CoinModelLinkedList::updateDeletedOne(CoinBigIndex position, const CoinModelTriple *triples)
1347 {
1348   assert(maximumMajor_);
1349   int column = triples[position].column;
1350   assert(column >= 0 && column < numberMajor_);
1351   CoinBigIndex previous = previous_[position];
1352   CoinBigIndex next = next_[position];
1353   // put on free list
1354   CoinBigIndex lastFree = last_[maximumMajor_];
1355   if (lastFree >= 0) {
1356     next_[lastFree] = position;
1357   } else {
1358     first_[maximumMajor_] = position;
1359     assert(last_[maximumMajor_] == -1);
1360   }
1361   last_[maximumMajor_] = position;
1362   previous_[position] = lastFree;
1363   next_[position] = -1;
1364   // Now take out of column
1365   if (previous >= 0) {
1366     next_[previous] = next;
1367   } else {
1368     first_[column] = next;
1369   }
1370   if (next >= 0) {
1371     previous_[next] = previous;
1372   } else {
1373     last_[column] = previous;
1374   }
1375 }
1376 // Fills first,last with -1
fill(int first,int last)1377 void CoinModelLinkedList::fill(int first, int last)
1378 {
1379   for (int i = first; i < last; i++) {
1380     first_[i] = -1;
1381     last_[i] = -1;
1382   }
1383 }
1384 /* Puts in free list from other list */
synchronize(CoinModelLinkedList & other)1385 void CoinModelLinkedList::synchronize(CoinModelLinkedList &other)
1386 {
1387   CoinBigIndex first = other.first_[other.maximumMajor_];
1388   first_[maximumMajor_] = first;
1389   CoinBigIndex last = other.last_[other.maximumMajor_];
1390   last_[maximumMajor_] = last;
1391   CoinBigIndex put = first;
1392   while (put >= 0) {
1393     previous_[put] = other.previous_[put];
1394     next_[put] = other.next_[put];
1395     put = next_[put];
1396   }
1397 }
1398 // Checks that links are consistent
validateLinks(const CoinModelTriple * triples) const1399 void CoinModelLinkedList::validateLinks(const CoinModelTriple *triples) const
1400 {
1401   char *mark = new char[maximumElements_];
1402   memset(mark, 0, maximumElements_);
1403   CoinBigIndex lastElement = -1;
1404   int i;
1405   for (i = 0; i < numberMajor_; i++) {
1406     CoinBigIndex position = first_[i];
1407 #ifndef NDEBUG
1408     CoinBigIndex lastPosition = -1;
1409 #endif
1410     while (position >= 0) {
1411       assert(position == first_[i] || next_[previous_[position]] == position);
1412       assert(type_ || i == static_cast< int >(rowInTriple(triples[position]))); // i == iMajor for rows
1413       assert(!type_ || i == triples[position].column); // i == iMajor
1414       assert(triples[position].column >= 0);
1415       mark[position] = 1;
1416       lastElement = CoinMax(lastElement, position);
1417 #ifndef NDEBUG
1418       lastPosition = position;
1419 #endif
1420       position = next_[position];
1421     }
1422     assert(lastPosition == last_[i]);
1423   }
1424   for (i = 0; i <= lastElement; i++) {
1425     if (!mark[i])
1426       assert(triples[i].column == -1);
1427   }
1428   delete[] mark;
1429 }
1430 
1431 /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
1432 */
1433