1 #ifndef BMALGO__H__INCLUDED__
2 #define BMALGO__H__INCLUDED__
3 /*
4 Copyright(c) 2002-2017 Anatoliy Kuznetsov(anatoliy_kuznetsov at yahoo.com)
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9 
10     http://www.apache.org/licenses/LICENSE-2.0
11 
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 
18 For more information please visit:  http://bitmagic.io
19 */
20 
21 /*! \file bmalgo.h
22     \brief Algorithms for bvector<> (main include)
23 */
24 
25 #ifndef BM__H__INCLUDED__
26 // BitMagic utility headers do not include main "bm.h" declaration
27 // #include "bm.h" or "bm64.h" explicitly
28 # error missing include (bm.h or bm64.h)
29 #endif
30 
31 #include "bmfunc.h"
32 #include "bmdef.h"
33 
34 #include "bmalgo_impl.h"
35 
36 
37 
38 namespace bm
39 {
40 
41 /*!
42     \brief Computes bitcount of AND operation of two bitsets
43     \param bv1 - Argument bit-vector.
44     \param bv2 - Argument bit-vector.
45     \return bitcount of the result
46     \ingroup  setalgo
47 */
48 template<class BV>
count_and(const BV & bv1,const BV & bv2)49 typename BV::size_type count_and(const BV& bv1, const BV& bv2) BMNOEXCEPT
50 {
51     return bm::distance_and_operation(bv1, bv2);
52 }
53 
54 /*!
55    \brief Computes if there is any bit in AND operation of two bitsets
56    \param bv1 - Argument bit-vector.
57    \param bv2 - Argument bit-vector.
58    \return non zero value if there is any bit
59    \ingroup  setalgo
60 */
61 template<class BV>
any_and(const BV & bv1,const BV & bv2)62 typename BV::size_type any_and(const BV& bv1, const BV& bv2) BMNOEXCEPT
63 {
64     distance_metric_descriptor dmd(bm::COUNT_AND);
65 
66     distance_operation_any(bv1, bv2, &dmd, &dmd + 1);
67     return dmd.result;
68 }
69 
70 
71 
72 /*!
73    \brief Computes bitcount of XOR operation of two bitsets
74    \param bv1 - Argument bit-vector.
75    \param bv2 - Argument bit-vector.
76    \return bitcount of the result
77    \ingroup  setalgo
78 */
79 template<class BV>
80 bm::distance_metric_descriptor::size_type
count_xor(const BV & bv1,const BV & bv2)81 count_xor(const BV& bv1, const BV& bv2) BMNOEXCEPT
82 {
83     distance_metric_descriptor dmd(bm::COUNT_XOR);
84 
85     distance_operation(bv1, bv2, &dmd, &dmd + 1);
86     return dmd.result;
87 }
88 
89 /*!
90    \brief Computes if there is any bit in XOR operation of two bitsets
91    \param bv1 - Argument bit-vector.
92    \param bv2 - Argument bit-vector.
93    \return non zero value if there is any bit
94    \ingroup  setalgo
95 */
96 template<class BV>
any_xor(const BV & bv1,const BV & bv2)97 typename BV::size_type any_xor(const BV& bv1, const BV& bv2) BMNOEXCEPT
98 {
99     distance_metric_descriptor dmd(bm::COUNT_XOR);
100 
101     distance_operation_any(bv1, bv2, &dmd, &dmd + 1);
102     return dmd.result;
103 }
104 
105 
106 
107 /*!
108    \brief Computes bitcount of SUB operation of two bitsets
109    \param bv1 - Argument bit-vector.
110    \param bv2 - Argument bit-vector.
111    \return bitcount of the result
112    \ingroup  setalgo
113 */
114 template<class BV>
count_sub(const BV & bv1,const BV & bv2)115 typename BV::size_type count_sub(const BV& bv1, const BV& bv2) BMNOEXCEPT
116 {
117     distance_metric_descriptor dmd(bm::COUNT_SUB_AB);
118 
119     distance_operation(bv1, bv2, &dmd, &dmd + 1);
120     return dmd.result;
121 }
122 
123 
124 /*!
125    \brief Computes if there is any bit in SUB operation of two bitsets
126    \param bv1 - Argument bit-vector.
127    \param bv2 - Argument bit-vector.
128    \return non zero value if there is any bit
129    \ingroup  setalgo
130 */
131 template<class BV>
any_sub(const BV & bv1,const BV & bv2)132 typename BV::size_type any_sub(const BV& bv1, const BV& bv2) BMNOEXCEPT
133 {
134     distance_metric_descriptor dmd(bm::COUNT_SUB_AB);
135 
136     distance_operation_any(bv1, bv2, &dmd, &dmd + 1);
137     return dmd.result;
138 }
139 
140 
141 /*!
142    \brief Computes bitcount of OR operation of two bitsets
143    \param bv1 - Argument bit-vector.
144    \param bv2 - Argument bit-vector.
145    \return bitcount of the result
146    \ingroup  setalgo
147 */
148 template<class BV>
count_or(const BV & bv1,const BV & bv2)149 typename BV::size_type count_or(const BV& bv1, const BV& bv2) BMNOEXCEPT
150 {
151     distance_metric_descriptor dmd(bm::COUNT_OR);
152 
153     distance_operation(bv1, bv2, &dmd, &dmd + 1);
154     return dmd.result;
155 }
156 
157 /*!
158    \brief Computes if there is any bit in OR operation of two bitsets
159    \param bv1 - Argument bit-vector.
160    \param bv2 - Argument bit-vector.
161    \return non zero value if there is any bit
162    \ingroup  setalgo
163 */
164 template<class BV>
any_or(const BV & bv1,const BV & bv2)165 typename BV::size_type any_or(const BV& bv1, const BV& bv2) BMNOEXCEPT
166 {
167     distance_metric_descriptor dmd(bm::COUNT_OR);
168 
169     distance_operation_any(bv1, bv2, &dmd, &dmd + 1);
170     return dmd.result;
171 }
172 
173 
174 
175 #define BM_SCANNER_OP(x) \
176 if (0 != (block = blk_blk[j+x])) \
177 { \
178     if (BM_IS_GAP(block)) \
179     { \
180         bm::for_each_gap_blk(BMGAP_PTR(block), (r+j+x)*bm::bits_in_block,\
181                              bit_functor); \
182     } \
183     else \
184     { \
185         bm::for_each_bit_blk(block, (r+j+x)*bm::bits_in_block,bit_functor); \
186     } \
187 }
188 
189 
190 /**
191     @brief bit-vector visitor scanner to traverse each 1 bit using C++ visitor
192 
193     @param bv - bit vector to scan
194     @param bit_functor - visitor: should support add_bits(), add_range()
195 
196     \ingroup setalgo
197     @sa for_each_bit_range visit_each_bit
198 */
199 template<class BV, class Func>
for_each_bit(const BV & bv,Func & bit_functor)200 void for_each_bit(const BV&    bv,
201                   Func&        bit_functor)
202 {
203     typedef typename BV::size_type size_type;
204 
205     const typename BV::blocks_manager_type& bman = bv.get_blocks_manager();
206     bm::word_t*** blk_root = bman.top_blocks_root();
207 
208     if (!blk_root)
209         return;
210 
211     unsigned tsize = bman.top_block_size();
212     for (unsigned i = 0; i < tsize; ++i)
213     {
214         bm::word_t** blk_blk = blk_root[i];
215         if (!blk_blk)
216             continue;
217 
218         if ((bm::word_t*)blk_blk == FULL_BLOCK_FAKE_ADDR)
219             blk_blk = FULL_SUB_BLOCK_REAL_ADDR;
220 
221         const bm::word_t* block;
222         size_type r = size_type(i) * bm::set_sub_array_size;
223         unsigned j = 0;
224         do
225         {
226         #ifdef BM64_AVX2
227             if (!avx2_test_all_zero_wave(blk_blk + j))
228             {
229                 BM_SCANNER_OP(0)
230                 BM_SCANNER_OP(1)
231                 BM_SCANNER_OP(2)
232                 BM_SCANNER_OP(3)
233             }
234             j += 4;
235         #elif defined(BM64_SSE4)
236             if (!sse42_test_all_zero_wave(blk_blk + j))
237             {
238                 BM_SCANNER_OP(0)
239                 BM_SCANNER_OP(1)
240             }
241             j += 2;
242         #else
243             BM_SCANNER_OP(0)
244             ++j;
245         #endif
246 
247         } while (j < bm::set_sub_array_size);
248 
249     }  // for i
250 }
251 
252 /**
253     @brief bit-vector range visitor to traverse each 1 bit
254 
255     @param bv - bit vector to scan
256     @param right - start of closed interval [from..to]
257     @param left   - end of close interval [from..to]
258     @param bit_functor - visitor: should support add_bits(), add_range()
259 
260     \ingroup setalgo
261     @sa for_each_bit
262 */
263 template<class BV, class Func>
for_each_bit_range(const BV & bv,typename BV::size_type left,typename BV::size_type right,Func & bit_functor)264 void for_each_bit_range(const BV&             bv,
265                        typename BV::size_type left,
266                        typename BV::size_type right,
267                        Func&                  bit_functor)
268 {
269     if (left > right)
270         bm::xor_swap(left, right);
271     if (right == bm::id_max)
272         --right;
273     BM_ASSERT(left < bm::id_max && right < bm::id_max);
274 
275     bm::for_each_bit_range_no_check(bv, left, right, bit_functor);
276 }
277 
278 
279 #undef BM_SCANNER_OP
280 
281 
282 /// private adaptor for C-style callbacks
283 ///
284 /// @internal
285 ///
286 template <class VCBT, class size_type>
287 struct bit_vitor_callback_adaptor
288 {
289     typedef VCBT bit_visitor_callback_type;
290 
bit_vitor_callback_adaptorbit_vitor_callback_adaptor291     bit_vitor_callback_adaptor(void* h, bit_visitor_callback_type cb_func)
292         : handle_(h), func_(cb_func)
293     {}
294 
add_bitsbit_vitor_callback_adaptor295     void add_bits(size_type offset, const unsigned char* bits, unsigned size)
296     {
297         for (unsigned i = 0; i < size; ++i)
298             func_(handle_, offset + bits[i]);
299     }
add_rangebit_vitor_callback_adaptor300     void add_range(size_type offset, size_type size)
301     {
302         for (size_type i = 0; i < size; ++i)
303             func_(handle_, offset + i);
304     }
305 
306     void* handle_;
307     bit_visitor_callback_type func_;
308 };
309 
310 
311 /// Functor for bit-copy (for testing)
312 ///
313 /// @internal
314 ///
315 template <class BV>
316 struct bit_vistor_copy_functor
317 {
318     typedef typename BV::size_type size_type;
319 
bit_vistor_copy_functorbit_vistor_copy_functor320     bit_vistor_copy_functor(BV& bv)
321         : bv_(bv)
322     {
323         bv_.init();
324     }
325 
add_bitsbit_vistor_copy_functor326     void add_bits(size_type offset, const unsigned char* bits, unsigned size)
327     {
328         BM_ASSERT(size);
329         for (unsigned i = 0; i < size; ++i)
330             bv_.set_bit_no_check(offset + bits[i]);
331     }
add_rangebit_vistor_copy_functor332     void add_range(size_type offset, size_type size)
333     {
334         BM_ASSERT(size);
335         bv_.set_range(offset, offset + size - 1);
336     }
337 
338     BV& bv_;
339     bit_visitor_callback_type func_;
340 };
341 
342 
343 
344 /**
345     @brief bvector visitor scanner to traverse each 1 bit using C callback
346 
347     @param bv - bit vector to scan
348     @param handle_ptr - handle to private memory used by callback
349     @param callback_ptr - callback function
350 
351     \ingroup setalgo
352 
353     @sa bit_visitor_callback_type
354 */
355 template<class BV>
visit_each_bit(const BV & bv,void * handle_ptr,bit_visitor_callback_type callback_ptr)356 void visit_each_bit(const BV&                 bv,
357                     void*                     handle_ptr,
358                     bit_visitor_callback_type callback_ptr)
359 {
360     typedef typename BV::size_type size_type;
361     bm::bit_vitor_callback_adaptor<bit_visitor_callback_type, size_type>
362             func(handle_ptr, callback_ptr);
363     bm::for_each_bit(bv, func);
364 }
365 
366 /**
367     @brief bvector visitor scanner to traverse each bits in range (C callback)
368 
369     @param bv - bit vector to scan
370     @param left - from [left..right]
371     @param right - to [left..right]
372     @param handle_ptr - handle to private memory used by callback
373     @param callback_ptr - callback function
374 
375     \ingroup setalgo
376 
377     @sa bit_visitor_callback_type for_each_bit
378 */
379 template<class BV>
visit_each_bit_range(const BV & bv,typename BV::size_type left,typename BV::size_type right,void * handle_ptr,bit_visitor_callback_type callback_ptr)380 void visit_each_bit_range(const BV&                 bv,
381                           typename BV::size_type    left,
382                           typename BV::size_type    right,
383                           void*                     handle_ptr,
384                           bit_visitor_callback_type callback_ptr)
385 {
386     typedef typename BV::size_type size_type;
387     bm::bit_vitor_callback_adaptor<bit_visitor_callback_type, size_type>
388             func(handle_ptr, callback_ptr);
389     bm::for_each_bit_range(bv, left, right, func);
390 }
391 
392 /**
393     @brief Algorithm to identify bit-vector ranges (splits) for the rank
394 
395     Rank range split algorithm walks the bit-vector to create list of
396     non-overlapping ranges [s1..e1],[s2..e2]...[sN...eN] with requested
397     (rank) number of 1 bits. All ranges should be the same popcount weight,
398     except the last one, which may have less.
399     Scan is progressing from left to right so result ranges will be
400     naturally sorted.
401 
402     @param bv       - bit vector to perform the range split scan
403     @param rank     - requested number of bits in each range
404                       if 0 it will create single range [first..last]
405                       to cover the whole bv
406     @param target_v - [out] STL(or STL-like) vector of pairs to keep pairs results
407 
408     \ingroup setalgo
409  */
410 template<typename BV, typename PairVect>
rank_range_split(const BV & bv,typename BV::size_type rank,PairVect & target_v)411 void rank_range_split(const BV&              bv,
412                       typename BV::size_type rank,
413                       PairVect&              target_v)
414 {
415     target_v.resize(0);
416     typename BV::size_type first, last, pos;
417     bool found = bv.find_range(first, last);
418     if (!found) // empty bit-vector
419         return;
420 
421     if (!rank) // if rank is not defined, include the whole vector [first..last]
422     {
423         typename PairVect::value_type pv;
424         pv.first = first; pv.second = last;
425         target_v.push_back(pv);
426         return;
427     }
428 
429     while (1)
430     {
431         typename PairVect::value_type pv;
432         found = bv.find_rank(rank, first, pos);
433         if (found)
434         {
435             pv.first = first; pv.second = pos;
436             target_v.push_back(pv);
437             if (pos >= last)
438                 break;
439             first = pos + 1;
440             continue;
441         }
442         // insufficient rank (last range)
443         found = bv.any_range(first, last);
444         if (found)
445         {
446             pv.first = first; pv.second = last;
447             target_v.push_back(pv);
448         }
449         break;
450     } // while
451 
452 }
453 
454 
455 
456 /**
457     Algorithms for rank compression of bit-vector
458 
459     1. Source vector (bv_src) is a subset of index vector (bv_idx)
460     2. As a subset it can be collapsed using bit-rank method, where each position
461     in the source vector is defined by population count (range)
462     [0..index_position] (count_range())
463     As a result all integer set of source vector gets re-mapped in
464     accord with the index vector.
465 
466     \ingroup setalgo
467 */
468 template<typename BV>
469 class rank_compressor
470 {
471 public:
472     typedef BV                         bvector_type;
473     typedef typename BV::size_type     size_type;
474     typedef typename BV::rs_index_type rs_index_type;
475     enum buffer_cap
476     {
477         n_buffer_cap = 1024
478     };
479 public:
480 
481     /**
482     Rank decompression
483     */
484     void decompress(BV& bv_target, const BV& bv_idx, const BV& bv_src);
485 
486     /**
487     Rank compression algorithm based on two palallel iterators/enumerators
488     set of source vector gets re-mapped in accord with the index/rank vector.
489 
490     \param bv_target - target bit-vector
491     \param bv_idx    - index (rank) vector used for address recalculation
492     \param bv_src    - source vector for re-mapping
493     */
494     void compress(BV& bv_target, const BV& bv_idx, const BV& bv_src);
495 
496     /**
497     \brief Source vector priority + index based rank
498 
499     @sa compress
500     */
501     void compress_by_source(BV& bv_target,
502                                 const BV& bv_idx,
503                                 const rs_index_type& bc_idx,
504                                 const BV& bv_src);
505 };
506 
507 
508 // ------------------------------------------------------------------------
509 //
510 // ------------------------------------------------------------------------
511 
512 
513 template<class BV>
compress(BV & bv_target,const BV & bv_idx,const BV & bv_src)514 void rank_compressor<BV>::compress(BV& bv_target,
515                                    const BV& bv_idx,
516                                    const BV& bv_src)
517 {
518     bv_target.clear();
519     bv_target.init();
520 
521     if (&bv_idx == &bv_src)
522     {
523         bv_target = bv_src;
524         return;
525     }
526     size_type ibuffer[n_buffer_cap];
527     size_type b_size;
528 
529     typedef typename BV::enumerator enumerator_t;
530     enumerator_t en_s = bv_src.first();
531     enumerator_t en_i = bv_idx.first();
532 
533     size_type r_idx = b_size = 0;
534     size_type i, s;
535 
536     for (; en_i.valid(); )
537     {
538         if (!en_s.valid())
539             break;
540         i = *en_i; s = *en_s;
541 
542         BM_ASSERT(s >= i);
543         BM_ASSERT(bv_idx.test(i));
544 
545         if (i == s)
546         {
547             ibuffer[b_size++] = r_idx++;
548             if (b_size == n_buffer_cap)
549             {
550                 bv_target.set(ibuffer, b_size, bm::BM_SORTED);
551                 b_size = 0;
552             }
553             ++en_i; ++en_s;
554             continue;
555         }
556         BM_ASSERT(s > i);
557 
558         size_type dist = s - i;
559         if (dist >= 64) // sufficiently far away, jump
560         {
561             size_type r_dist = bv_idx.count_range(i + 1, s);
562             r_idx += r_dist;
563             en_i.go_to(s);
564             BM_ASSERT(en_i.valid());
565         }
566         else  // small distance, iterate to close the gap
567         {
568             for (; s > i; ++r_idx)
569             {
570                 ++en_i;
571                 i = *en_i;
572             } // for
573             BM_ASSERT(en_i.valid());
574         }
575     } // for
576 
577     if (b_size)
578     {
579         bv_target.set(ibuffer, b_size, bm::BM_SORTED);
580     }
581 
582 }
583 
584 // ------------------------------------------------------------------------
585 
586 template<class BV>
decompress(BV & bv_target,const BV & bv_idx,const BV & bv_src)587 void rank_compressor<BV>::decompress(BV& bv_target,
588                                      const BV& bv_idx,
589                                      const BV& bv_src)
590 {
591     bv_target.clear();
592     bv_target.init();
593 
594     if (&bv_idx == &bv_src)
595     {
596         bv_target = bv_src;
597         return;
598     }
599 
600     size_type r_idx, i, s, b_size;
601     size_type ibuffer[n_buffer_cap];
602 
603     b_size = r_idx = 0;
604 
605     typedef typename BV::enumerator enumerator_t;
606     enumerator_t en_s = bv_src.first();
607     enumerator_t en_i = bv_idx.first();
608     for (; en_i.valid(); )
609     {
610         if (!en_s.valid())
611             break;
612         s = *en_s;
613         i = *en_i;
614         if (s == r_idx)
615         {
616             ibuffer[b_size++] = i;
617             if (b_size == n_buffer_cap)
618             {
619                 bv_target.set(ibuffer, b_size, bm::BM_SORTED);
620                 b_size = 0;
621             }
622             ++en_i; ++en_s; ++r_idx;
623             continue;
624         }
625         // source is "faster" than index, need to re-align
626         BM_ASSERT(s > r_idx);
627         size_type rank = s - r_idx + 1u;
628         size_type new_pos = 0;
629 
630         if (rank < 256)
631         {
632             en_i.skip(s - r_idx);
633             BM_ASSERT(en_i.valid());
634             new_pos = *en_i;
635         }
636         else
637         {
638             bv_idx.find_rank(rank, i, new_pos);
639             BM_ASSERT(new_pos);
640             en_i.go_to(new_pos);
641             BM_ASSERT(en_i.valid());
642         }
643 
644         r_idx = s;
645         ibuffer[b_size++] = new_pos;
646         if (b_size == n_buffer_cap)
647         {
648             bv_target.set(ibuffer, b_size, bm::BM_SORTED);
649             b_size = 0;
650         }
651         ++en_i; ++en_s; ++r_idx;
652 
653     } // for en
654 
655     if (b_size)
656     {
657         bv_target.set(ibuffer, b_size, bm::BM_SORTED);
658     }
659 }
660 
661 // ------------------------------------------------------------------------
662 
663 template<class BV>
compress_by_source(BV & bv_target,const BV & bv_idx,const rs_index_type & bc_idx,const BV & bv_src)664 void rank_compressor<BV>::compress_by_source(BV& bv_target,
665                                              const BV& bv_idx,
666                                              const rs_index_type& bc_idx,
667                                              const BV& bv_src)
668 {
669     /// Rank compressor visitor (functor)
670     /// @internal
671     struct visitor_func
672     {
673         visitor_func(bvector_type&        bv_out,
674                      const bvector_type&  bv_index,
675                      const rs_index_type& bc_index)
676         : bv_target_(bv_out),
677           bv_index_(bv_index),
678           bc_index_(bc_index)
679         {}
680 
681         void add_bits(size_type arr_offset, const unsigned char* bits, unsigned bits_size)
682         {
683             for (unsigned i = 0; i < bits_size; ++i)
684             {
685                 size_type idx = arr_offset + bits[i];
686                 BM_ASSERT(bv_index_.test(idx));
687 
688                 size_type r_idx = bv_index_.count_to(idx, bc_index_) - 1;
689                 bv_target_.set_bit_no_check(r_idx);
690             }
691         }
692         void add_range(size_type arr_offset, size_type sz)
693         {
694             for (size_type i = 0; i < sz; ++i)
695             {
696                 size_type idx = i + arr_offset;
697                 BM_ASSERT(bv_index_.test(idx));
698 
699                 size_type r_idx = bv_index_.count_to(idx, bc_index_) - 1;
700                 bv_target_.set_bit_no_check(r_idx);
701             }
702         }
703 
704         bvector_type&           bv_target_;
705         const bvector_type&     bv_index_;
706         const rs_index_type&    bc_index_;
707     };
708     // ------------------------------------
709 
710     bv_target.clear();
711     bv_target.init();
712 
713     if (&bv_idx == &bv_src)
714     {
715         bv_target = bv_src;
716         return;
717     }
718     visitor_func func(bv_target, bv_idx, bc_idx);
719     bm::for_each_bit(bv_src, func);
720 }
721 
722 
723 
724 
725 } // bm
726 
727 
728 #endif
729