1 #ifndef NETGEN_CORE_TABLE_HPP
2 #define NETGEN_CORE_TABLE_HPP
3 
4 /**************************************************************************/
5 /* File:   table.hpp                                                      */
6 /* Author: Joachim Schoeberl                                              */
7 /* Date:   25. Mar. 2000                                                  */
8 /**************************************************************************/
9 
10 #include <atomic>
11 #include <iostream>
12 #include <optional>
13 
14 #include "array.hpp"
15 #include "bitarray.hpp"
16 #include "memtracer.hpp"
17 #include "ngcore_api.hpp"
18 #include "profiler.hpp"
19 #include "taskmanager.hpp"
20 
21 namespace ngcore
22 {
23 
24 
25   template <class T, class IndexType = size_t>
26   class FlatTable
27   {
28   protected:
29     static constexpr IndexType BASE = IndexBASE<IndexType>();
30     /// number of rows
31     size_t size;
32     /// pointer to first in row
33     size_t * index;
34     /// array of data
35     T * data;
36 
37   public:
38     FlatTable() = delete;
39 
FlatTable(size_t as,size_t * aindex,T * adata)40     NETGEN_INLINE FlatTable(size_t as, size_t * aindex, T * adata)
41       : size(as), index(aindex), data(adata) { ; }
42 
43     /// Size of table
Size() const44     NETGEN_INLINE size_t Size() const { return size; }
45 
46     /// Access entry
operator [](IndexType i) const47     NETGEN_INLINE const FlatArray<T> operator[] (IndexType i) const
48     {
49       i = i-BASE;
50       return FlatArray<T> (index[i+1]-index[i], data+index[i]);
51     }
52 
Data() const53     NETGEN_INLINE T * Data() const { return data; }
54 
AsArray() const55     NETGEN_INLINE FlatArray<T> AsArray() const
56     {
57       return FlatArray<T> (index[size]-index[0], data+index[0]);
58     }
59 
IndexArray() const60     NETGEN_INLINE FlatArray<size_t> IndexArray() const
61     {
62       return FlatArray<size_t, IndexType> (size+1, index);
63     }
64 
65     /// takes range starting from position start of end-start elements
Range(size_t start,size_t end) const66     NETGEN_INLINE FlatTable<T> Range (size_t start, size_t end) const
67     {
68       return FlatTable<T> (end-start, index+start-BASE, data);
69     }
70 
71     /// takes range starting from position start of end-start elements
Range(T_Range<size_t> range) const72     NETGEN_INLINE FlatTable<T> Range (T_Range<size_t> range) const
73     {
74       return FlatTable<T> (range.Size(), index+range.First()-BASE, data);
75     }
76 
Range() const77     NETGEN_INLINE T_Range<IndexType> Range () const
78     {
79       return T_Range<IndexType> (BASE, size+BASE);
80     }
81 
82     class Iterator
83     {
84       const FlatTable & tab;
85       size_t row;
86     public:
Iterator(const FlatTable & _tab,size_t _row)87       Iterator (const FlatTable & _tab, size_t _row) : tab(_tab), row(_row) { ; }
operator ++()88       Iterator & operator++ () { ++row; return *this; }
operator *() const89       FlatArray<T> operator* () const { return tab[row]; }
operator !=(const Iterator & it2)90       bool operator!= (const Iterator & it2) { return row != it2.row; }
91     };
92 
begin() const93     Iterator begin() const { return Iterator(*this, BASE); }
end() const94     Iterator end() const { return Iterator(*this, BASE+size); }
95   };
96 
97   NGCORE_API extern size_t * TablePrefixSum32 (FlatArray<unsigned int> entrysize);
98   NGCORE_API extern size_t * TablePrefixSum64 (FlatArray<size_t> entrysize);
99 
100 
TablePrefixSum(FlatArray<unsigned int> entrysize)101   NETGEN_INLINE size_t * TablePrefixSum (FlatArray<unsigned int> entrysize)
102   { return TablePrefixSum32 (entrysize); }
TablePrefixSum(FlatArray<int> entrysize)103   NETGEN_INLINE size_t * TablePrefixSum (FlatArray<int> entrysize)
104   { return TablePrefixSum32 (FlatArray<unsigned> (entrysize.Size(), (unsigned int*)(int*)(entrysize.Addr(0)))); }
TablePrefixSum(FlatArray<std::atomic<int>> entrysize)105   NETGEN_INLINE size_t * TablePrefixSum (FlatArray<std::atomic<int>> entrysize)
106   { return TablePrefixSum32 (FlatArray<unsigned> (entrysize.Size(), (unsigned int*)(std::atomic<int>*)entrysize.Addr(0))); }
TablePrefixSum(FlatArray<size_t> entrysize)107   NETGEN_INLINE size_t * TablePrefixSum (FlatArray<size_t> entrysize)
108   { return TablePrefixSum64 (entrysize); }
109 
110 
111   /**
112      A compact Table container.
113      A table contains size entries of variable size.
114      The entry sizes must be known at construction.
115   */
116   template <class T, class IndexType = size_t>
117   class Table : public FlatTable<T, IndexType>
118   {
119   protected:
120 
121     using FlatTable<T,IndexType>::size;
122     using FlatTable<T,IndexType>::index;
123     using FlatTable<T,IndexType>::data;
124 
125   public:
126     ///
Table()127     NETGEN_INLINE Table () : FlatTable<T,IndexType> (0,nullptr,nullptr) { ; }
128     /// Construct table of uniform entrysize
Table(size_t asize,size_t entrysize)129     NETGEN_INLINE Table (size_t asize, size_t entrysize)
130       : FlatTable<T,IndexType>( asize, new size_t[asize+1], new T[asize*entrysize] )
131     {
132       for (size_t i : IntRange(size+1))
133         index[i] = i*entrysize;
134     }
135 
136     /// Construct table of variable entrysize
137     template <typename TI>
Table(FlatArray<TI,IndexType> entrysize)138     NETGEN_INLINE Table (FlatArray<TI,IndexType> entrysize)
139       : FlatTable<T,IndexType> (0, nullptr, nullptr)
140     {
141       size  = entrysize.Size();
142       index = TablePrefixSum (FlatArray<TI> (entrysize.Size(), entrysize.Data()));
143       size_t cnt = index[size];
144       data = new T[cnt];
145     }
146 
Table(const Table & tab2)147     explicit NETGEN_INLINE Table (const Table & tab2)
148       : FlatTable<T,IndexType>(0, nullptr, nullptr)
149     {
150       size = tab2.Size();
151 
152       index = new size_t[size+1];
153       for (size_t i = 0; i <= size; i++)
154         index[i] = tab2.index[i];
155 
156       size_t cnt = index[size];
157       data = new T[cnt];
158       for (size_t i = 0; i < cnt; i++)
159         data[i] = tab2.data[i];
160     }
161 
Table(Table && tab2)162     NETGEN_INLINE Table (Table && tab2)
163       : FlatTable<T,IndexType>(0, nullptr, nullptr)
164     {
165       tab2.mt.Free(tab2.GetMemUsage());
166       Swap (size, tab2.size);
167       Swap (index, tab2.index);
168       Swap (data, tab2.data);
169     }
170 
DoArchive(Archive & ar)171     void DoArchive(Archive& ar)
172     {
173       ar & size;
174       if(size == 0)
175         return;
176       if(ar.Input())
177         {
178           index = new IndexType[size+1];
179           mt.Alloc(sizeof(IndexType) * (size+1));
180         }
181       ar.Do(index, size+1);
182       if(ar.Input())
183         {
184           data = new T[index[size]];
185           mt.Alloc(sizeof(T) * index[size]);
186         }
187       ar.Do(data, index[size]);
188     }
189 
operator =(Table && tab2)190     NETGEN_INLINE Table & operator= (Table && tab2)
191     {
192       mt.Swap(GetMemUsage(), tab2.mt, tab2.GetMemUsage());
193       Swap (size, tab2.size);
194       Swap (index, tab2.index);
195       Swap (data, tab2.data);
196       return *this;
197     }
198 
199 
200 
201     /// Delete data
~Table()202     NETGEN_INLINE ~Table ()
203     {
204       mt.Free(GetMemUsage());
205       delete [] data;
206       delete [] index;
207     }
208 
209     /// Size of table
210     using FlatTable<T,IndexType>::Size;
211 
212     /// number of elements in all rows
NElements() const213     NETGEN_INLINE size_t NElements() const { return index[size]; }
214 
215     using FlatTable<T,IndexType>::operator[];
216 
StartMemoryTracing(int)217     NETGEN_INLINE void StartMemoryTracing (int /* mem_id */)
218     {
219       mt.Alloc(GetMemUsage());
220     }
GetMemoryTracer() const221     const MemoryTracer& GetMemoryTracer() const { return mt; }
222 
223   private:
GetMemUsage() const224     size_t GetMemUsage() const { return size == 0 ? 0 : sizeof(T)*index[size] + sizeof(IndexType) * size+1; }
225     MemoryTracer mt;
226   };
227 
228 
229   /// Print table
230   template <class T, typename IndexType>
operator <<(ostream & s,const Table<T,IndexType> & table)231   inline ostream & operator<< (ostream & s, const Table<T,IndexType> & table)
232   {
233     for (auto i : table.Range())
234       {
235         s << i << ":";
236         for (auto el : table[i])
237           s << " " << el;
238         s << "\n";
239       }
240     s << std::flush;
241     return s;
242   }
243 
244 
245 
246 
247   template <class T, typename IndexType=size_t>
248   class TableCreator
249   {
250   protected:
251     int mode;    // 1 .. cnt, 2 .. cnt entries, 3 .. fill table
252     std::atomic<size_t> nd;
253     Array<std::atomic<int>,IndexType> cnt;
254     Table<T,IndexType> table;
255   public:
TableCreator()256     TableCreator()
257     { nd = 0; mode = 1; }
TableCreator(size_t acnt)258     TableCreator (size_t acnt)
259     { nd = acnt; SetMode(2); }
260 
MoveTable()261     Table<T,IndexType> MoveTable()
262     {
263       return std::move(table);
264     }
265 
Done()266     bool Done () { return mode > 3; }
operator ++(int)267     void operator++(int) { SetMode (mode+1); }
268 
GetMode() const269     int GetMode () const { return mode; }
SetMode(int amode)270     void SetMode (int amode)
271     {
272       mode = amode;
273       if (mode == 2)
274 	{
275 	  // cnt.SetSize(nd);  // atomic has no copy
276           cnt = Array<std::atomic<int>,IndexType> (nd);
277           for (auto & ci : cnt) ci.store (0, std::memory_order_relaxed);
278 	}
279       if (mode == 3)
280 	{
281           table = Table<T,IndexType> (cnt);
282           // for (auto & ci : cnt) ci = 0;
283           for (auto & ci : cnt) ci.store (0, std::memory_order_relaxed);
284           // cnt = 0;
285 	}
286     }
287 
SetSize(size_t _nd)288     void SetSize (size_t _nd)
289     {
290       if (mode == 1)
291         nd = _nd;
292       else
293         {
294           if (nd != _nd)
295             throw Exception ("cannot change size of table-creator");
296         }
297     }
298 
Add(IndexType blocknr,const T & data)299     void Add (IndexType blocknr, const T & data)
300     {
301       switch (mode)
302 	{
303 	case 1:
304           {
305             size_t oldval = nd;
306             while (blocknr+1>nd) {
307               nd.compare_exchange_weak (oldval, blocknr+1);
308               oldval = nd;
309             }
310             break;
311           }
312 	case 2:
313 	  cnt[blocknr]++;
314 	  break;
315 	case 3:
316           int ci = cnt[blocknr]++;
317           table[blocknr][ci] = data;
318 	  break;
319 	}
320     }
321 
322 
Add(IndexType blocknr,IntRange range)323     void Add (IndexType blocknr, IntRange range)
324     {
325       switch (mode)
326 	{
327 	case 1:
328           {
329             size_t oldval = nd;
330             while (blocknr+1>nd) {
331               nd.compare_exchange_weak (oldval, blocknr+1);
332               oldval = nd;
333             }
334             break;
335           }
336 	case 2:
337 	  cnt[blocknr] += range.Size();
338 	  break;
339 	case 3:
340           size_t ci = ( cnt[blocknr] += range.Size() ) - range.Size();
341 	  for (size_t j = 0; j < range.Size(); j++)
342             table[blocknr][ci+j] = range.First()+j;
343 	  break;
344 	}
345     }
346 
Add(IndexType blocknr,const FlatArray<int> & dofs)347     void Add (IndexType blocknr, const FlatArray<int> & dofs)
348     {
349       switch (mode)
350 	{
351 	case 1:
352           {
353             size_t oldval = nd;
354             while (blocknr+1>nd) {
355               nd.compare_exchange_weak (oldval, blocknr+1);
356               oldval = nd;
357             }
358             break;
359           }
360 	case 2:
361 	  cnt[blocknr] += dofs.Size();
362 	  break;
363 	case 3:
364           size_t ci = ( cnt[blocknr] += dofs.Size() ) - dofs.Size();
365 	  for (size_t j = 0; j < dofs.Size(); j++)
366             table[blocknr][ci+j] = dofs[j];
367 	  break;
368 	}
369     }
370   };
371 
372   template <typename TEntry, typename TIndex, typename TRange, typename TFunc>
CreateTable(const TRange & range,const TFunc & func,std::optional<size_t> cnt)373   Table<TEntry, TIndex> CreateTable( const TRange & range, const TFunc & func, std::optional< size_t > cnt )
374   {
375       static Timer timer("CreateTable");
376       RegionTimer rt(timer);
377       std::unique_ptr<TableCreator<TEntry, TIndex>> pcreator;
378 
379       if(cnt)
380           pcreator = std::make_unique<TableCreator<TEntry, TIndex>>(*cnt);
381       else
382           pcreator = std::make_unique<TableCreator<TEntry, TIndex>>();
383 
384       auto & creator = *pcreator;
385 
386       for ( ; !creator.Done(); creator++)
387         ParallelForRange
388           (range, [&] (auto myrange)
389            {
390              for (auto i : myrange)
391                func(creator, i);
392            }, TasksPerThread(4)
393           );
394 
395     return creator.MoveTable();
396   }
397 
398   template <typename TEntry, typename TIndex, typename TRange, typename TFunc>
CreateSortedTable(const TRange & range,const TFunc & func,std::optional<size_t> cnt)399   Table<TEntry, TIndex> CreateSortedTable( const TRange & range, const TFunc & func, std::optional< size_t > cnt )
400   {
401     static Timer timer("CreateSortedTable");
402     RegionTimer rt(timer);
403     Table<TEntry, TIndex> table = CreateTable<TEntry, TIndex>(range, func, cnt);
404     ParallelForRange
405       (table.Range(), [&] (auto myrange)
406        {
407          for (auto i : myrange)
408            QuickSort(table[i]);
409        }, TasksPerThread(4)
410       );
411 
412     return table;
413   }
414 
415   class NGCORE_API FilteredTableCreator : public TableCreator<int>
416   {
417   protected:
418     const BitArray* takedofs;
419   public:
FilteredTableCreator(const BitArray * atakedofs)420     FilteredTableCreator(const BitArray* atakedofs)
421       : TableCreator<int>(), takedofs(atakedofs) { };
FilteredTableCreator(int acnt,const BitArray * atakedofs)422     FilteredTableCreator(int acnt, const BitArray* atakedofs)
423       : TableCreator<int>(acnt),takedofs(atakedofs) { };
424     void Add (size_t blocknr, int data);
425     void Add (size_t blocknr, IntRange range);
426     void Add (size_t blocknr, FlatArray<int> dofs);
427   };
428 
429 
430 
431   /**
432      A dynamic table class.
433 
434      A DynamicTable contains entries of variable size. Entry sizes can
435      be increased dynamically.
436   */
437   template <class T, class IndexType = size_t>
438   class DynamicTable
439   {
440   protected:
441     static constexpr IndexType BASE = IndexBASE<IndexType>();
442 
443     struct linestruct
444     {
445       int size;
446       int maxsize;
447       T * col;
448     };
449 
450     Array<linestruct, IndexType> data;
451     T * oneblock = nullptr;
452 
453   public:
454     /// Creates table of size size
DynamicTable(int size=0)455     DynamicTable (int size = 0)
456       : data(size)
457     {
458       for (auto & d : data)
459         {
460           d.maxsize = 0;
461           d.size = 0;
462           d.col = nullptr;
463         }
464       oneblock = nullptr;
465     }
466 
467     /// Creates table with a priori fixed entry sizes.
DynamicTable(const Array<int,IndexType> & entrysizes)468     DynamicTable (const Array<int, IndexType> & entrysizes)
469       : data(entrysizes.Size())
470     {
471       size_t cnt = 0;
472       // size_t n = entrysizes.Size();
473 
474       for (auto es : entrysizes)
475         cnt += es;
476       oneblock = new T[cnt];
477 
478       cnt = 0;
479       for (auto i : data.Range())
480         {
481           data[i].maxsize = entrysizes[i];
482           data[i].size = 0;
483           data[i].col = &oneblock[cnt];
484           cnt += entrysizes[i];
485         }
486     }
487 
DynamicTable(DynamicTable && tab2)488     DynamicTable (DynamicTable && tab2)
489     {
490       Swap (data, tab2.data);
491       Swap (oneblock, tab2.oneblock);
492     }
493 
~DynamicTable()494     ~DynamicTable ()
495     {
496       if (oneblock)
497         delete [] oneblock;
498       else
499         for (auto & d : data)
500           delete [] d.col;
501     }
502 
operator =(DynamicTable && tab2)503     DynamicTable & operator= (DynamicTable && tab2)
504     {
505       Swap (data, tab2.data);
506       Swap (oneblock, tab2.oneblock);
507       return *this;
508     }
509 
510     /// Changes Size of table to size, deletes data
SetSize(int size)511     void SetSize (int size)
512     {
513       for (auto & d : data)
514         delete [] d.col;
515 
516       data.SetSize(size);
517       for (auto & d : data)
518         {
519           d.maxsize = 0;
520           d.size = 0;
521           d.col = nullptr;
522         }
523     }
524 
ChangeSize(size_t size)525     void ChangeSize (size_t size)
526     {
527       if (oneblock)
528         throw Exception ("cannot change size of oneblock dynamic table");
529 
530       size_t oldsize = data.Size();
531       if (size == oldsize)
532         return;
533 
534       if (size < oldsize)
535         for (int i = size; i < oldsize; i++)
536           delete [] data[i+BASE].col;
537 
538       data.SetSize(size);
539 
540       for (int i = oldsize; i < size; i++)
541         {
542           data[i+BASE].maxsize = 0;
543           data[i+BASE].size = 0;
544           data[i+BASE].col = nullptr;
545         }
546     }
547 
548 
549 
550     ///
IncSize(IndexType i)551     void IncSize (IndexType i)
552     {
553       NETGEN_CHECK_RANGE(i,BASE,data.Size()+BASE);
554 
555       linestruct & line = data[i];
556 
557       if (line.size == line.maxsize)
558         {
559           T * p;
560           if constexpr (std::is_default_constructible<T>::value)
561             p = new T[(2*line.maxsize+5)];
562           else
563             p = reinterpret_cast<T*>(new char[(2*line.maxsize+5)*sizeof(T)]);
564           for (size_t i = 0; i < line.maxsize; i++)
565             p[i] = std::move(line.col[i]);
566           // memcpy (p, line.col, line.maxsize * sizeof(T));
567           delete [] line.col;
568           line.col = p;
569           line.maxsize = 2*line.maxsize+5;
570         }
571 
572       line.size++;
573     }
574 
DecSize(IndexType i)575     void DecSize (IndexType i)
576     {
577       NETGEN_CHECK_RANGE(i,BASE,data.Size()+BASE);
578       linestruct & line = data[i];
579 
580 #ifdef NETGEN_ENABLE_CHECK_RANGE
581       if (line.size == 0)
582         throw Exception ("BaseDynamicTable::Dec: EntrySize < 0");
583 #endif
584 
585       line.size--;
586     }
587 
588 
589     /// Inserts element acont into row i. Does not test if already used.
Add(IndexType i,const T & acont)590     void Add (IndexType i, const T & acont)
591     {
592       if (data[i].size == data[i].maxsize)
593         this->IncSize (i);
594       else
595         data[i].size++;
596       data[i].col[data[i].size-1] = acont;
597     }
598 
599     /// Inserts element acont into row i, iff not yet exists.
AddUnique(IndexType i,const T & cont)600     void AddUnique (IndexType i, const T & cont)
601     {
602       int es = EntrySize (i);
603       T * line = data[i].col;
604       for (int j = 0; j < es; j++)
605         if (line[j] == cont)
606           return;
607       Add (i, cont);
608     }
609 
610 
611     /// Inserts element acont into row i. Does not test if already used.
AddEmpty(IndexType i)612     void AddEmpty (IndexType i)
613     {
614       IncSize (i);
615     }
616 
617     /** Set the nr-th element in the i-th row to acont.
618         Does not check for overflow. */
Set(IndexType i,int nr,const T & acont)619     void Set (IndexType i, int nr, const T & acont)
620     {
621       data[i].col[nr] = acont;
622     }
623 
624 
625     /** Returns the nr-th element in the i-th row.
626         Does not check for overflow. */
Get(IndexType i,int nr) const627     const T & Get (IndexType i, int nr) const
628     {
629       return data[i].col[nr];
630     }
631 
632 
633     /** Returns pointer to the first element in row i. */
GetLine(IndexType i) const634     const T * GetLine (IndexType i) const
635     {
636       return data[i].col;
637     }
638 
639     /// Returns size of the table.
Size() const640     size_t Size () const
641     {
642       return data.Size();
643     }
644 
Range() const645     auto Range () const
646     {
647       return data.Range();
648     }
649 
650     /// Returns size of the i-th row.
EntrySize(IndexType i) const651     int EntrySize (IndexType i) const
652     {
653       return data[i].size;
654     }
655 
656     ///
DecEntrySize(IndexType i)657     void DecEntrySize (IndexType i)
658     {
659       DecSize(i);
660     }
661 
662     /// Access entry i
operator [](IndexType i)663     FlatArray<T> operator[] (IndexType i)
664     {
665       return FlatArray<T> (data[i].size, data[i].col);
666     }
667 
668     /*
669       typedef const FlatArray<T> ConstFlatArray;
670       /// Access entry i
671       ConstFlatArray operator[] (int i) const
672       { return FlatArray<T> (data[i].size, static_cast<T*> (data[i].col)); }
673     */
operator [](IndexType i) const674     FlatArray<T> operator[] (IndexType i) const
675     {
676       return FlatArray<T> (data[i].size, data[i].col);
677     }
678   };
679 
680 
681   /// Print table
682   template <class T>
operator <<(ostream & s,const DynamicTable<T> & table)683   inline ostream & operator<< (ostream & s, const DynamicTable<T> & table)
684   {
685     for (auto i : Range(table))
686       {
687         s << i << ":";
688         for (int j = 0; j < table[i].Size(); j++)
689           s << " " << table[i][j];
690         s << "\n";
691       }
692     s << std::flush;
693     return s;
694   }
695 
696 
697   //   Helper function to calculate coloring of a set of indices for parallel processing of independent elements/points/etc.
698   //   Assigns a color to each of colors.Size() elements, such that two elements with the same color don't share a common 'dof',
699   //   the mapping from element to dofs is provided by the function getDofs(int) -> iterable<int>
700   //
701   //   Returns the number of used colors
702   template <typename Tmask>
ComputeColoring(FlatArray<int> colors,size_t ndofs,Tmask const & getDofs)703   int ComputeColoring( FlatArray<int> colors, size_t ndofs, Tmask const & getDofs)
704   {
705     static Timer timer("ComputeColoring - "+Demangle(typeid(Tmask).name())); RegionTimer rt(timer);
706     static_assert(sizeof(unsigned int)==4, "Adapt type of mask array");
707     size_t n = colors.Size();
708 
709     Array<unsigned int> mask(ndofs);
710 
711     size_t colored_blocks = 0;
712 
713     // We are coloring with 32 colors at once and use each bit to mask conflicts
714     unsigned int check = 0;
715     unsigned int checkbit = 0;
716 
717     int current_color = 0;
718     colors = -1;
719     int maxcolor = 0;
720 
721     while(colored_blocks<n)
722     {
723         mask = 0;
724         for (auto i : Range(n) )
725         {
726             if(colors[i]>-1) continue;
727             check = 0;
728             const auto & dofs = getDofs(i);
729 
730             // Check if adjacent dofs are already marked by current color
731             for (auto dof : dofs)
732                 check|=mask[dof];
733 
734             // Did we find a free color?
735             if(check != 0xFFFFFFFF)
736             {
737                 checkbit = 1;
738                 int color = current_color;
739                 // find the actual color, which is free (out of 32)
740                 while (check & checkbit)
741                 {
742                     color++;
743                     checkbit *= 2;
744                 }
745                 colors[i] = color;
746                 maxcolor = color > maxcolor ? color : maxcolor;
747                 colored_blocks++;
748                 // mask all adjacent dofs with the found color
749                 for (auto dof : dofs)
750                     mask[dof] |= checkbit;
751             }
752         }
753         current_color+=32;
754     }
755     return maxcolor+1;
756   }
757 
758 
759   typedef DynamicTable<int> IntTable;
760 
761 } // namespace ngcore
762 
763 #endif // NETGEN_CORE_TABLE_HPP
764