Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Projects

Boost , Chapter 1. Boost.Icl , Chapter 1. Boost.Icl

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Проекты являются примерами использования интервальных контейнеров, которые выходят за рамки небольших игрушек фрагментов кода. Представленный здесь код касается более серьезных приложений, которые подходят к качеству программирования реального мира. В то же время он направлен на то, чтобы направить читателя глубже в различные аспекты библиотеки. Чтобы не перегружать читателя деталями реализации, код в проекты пытается быть минимальный. Он сосредоточен на основных аспектах проектов и не предназначен для того, чтобы быть полным и зрелым, как сам библиотечный код. Потому что это минимально, код проекта живет в namespace mini.

Фишки - это просто наборы. Наборы неподписанных интегралов, чтобы быть более точными. Префикс бит обычно только указывает, что представление этих наборов организовано в сжатой форме, которая эксплуатирует факт, что мы можем переключаться на один бит в машинных словах. Таким образом, битуты, как известно, очень малы и, следовательно, эффективны. Эффективность битет обычно сочетается с предварительным условием, что диапазон значений элементов относительно мал, как [0.32] или [0.64], значения, которые обычно могут быть представлены в одном или небольшом количестве машинных слов. Если бы мы хотели представить набор, содержащий два значения {1, 100000}, нам было бы гораздо лучше использовать другие наборы, такие как std::set.

bitsets:

typedef interval_map<IntegralT, SomeBitSet<N>, partial_absorber,
                     std::less, inplace_bit_add, inplace_bit_and> IntervalBitmap;

Такой IntervalBitmap представляет k*N биты для каждого сегмента.

[a, a+k)->'1111....1111' // N bits associated: Represents a total of k*N bits.  

Для интервала [a, a+k) установлены, прежде всего, биты. Но мы также можем иметь отдельные nests или кластеры битс.

[b,  b+1)->'01001011...1'
[b+1,b+2)->'11010001...0'
. . .

и мы можем преодолевать интервалы между равными битовыми последовательностями, которые представляют собой периодические шаблоны.

[c,d)->'010101....01'  // Every second bit is set              in range [c,d)
[d,e)->'001100..0011'  // Every two bits alterate              in range [d,e)
[e,f)->'bit-sequence'  // 'bit-sequence' reoccurs every N bits in range [e,f)

IntervalBitmap может представлять N*(2^M) элементы, если M является количеством битов интегрального типа IntegralT. В отличие от битсетов, которые обычно представляют неподписаны интегральные числа, большой битсет также может варьироваться от отрицательных чисел. Есть области, где необходимы такие большие реализации битов. Например, компактное представление больших таблиц распределения файлов. Что еще предстоит сделать для проекта Large Bitset, так это кодировать обертку class large_bitset около IntervalBitmap, чтобы large_bitset выглядел и чувствовал себя как обычный класс.

Чтобы ускорить аппетит к просмотру реализации, сначала несколько вариантов использования. В следующих примерах мы будем использовать natk для неподписанных интегралов и bitsk для битов, содержащих k.

Начнем с большого. В первом примере...

void test_large()
{
    const nat64 much = 0xffffffffffffffffull;
    large_bitset<> venti; // ... the largest, I can think of ;)
    venti += discrete_interval<nat64>(0, much);
    cout << "----- Test function test_large() -----------------------------------------------\n";
    cout << "We have just turned on the awesome amount of 18,446,744,073,709,551,616 bits ;-)\n";
    venti.show_segments();

Мы тестируем лимиты. Сначала мы установили все биты, а затем выключили последний бит.

    cout << "---- Let's swich off the very last bit -----------------------------------------\n";
    venti -= much;
    venti.show_segments();
    cout << "---- Venti is plenty ... let's do something small: A tall ----------------------\n\n";
}

Выход программы (a мало beautified):

----- Test function test_large() -----------------------------------------------
We have just turned on the awesome amount of 18,446,744,073,709,551,616 bits ;-)
[                 0, 288230376151711744) -> 1111111111111111111111111111111111111111111111111111111111111111
---- Let's swich off the very last bit -----------------------------------------
[                 0, 288230376151711743) -> 1111111111111111111111111111111111111111111111111111111111111111
[288230376151711743, 288230376151711744) -> 1111111111111111111111111111111111111111111111111111111111111110
---- Venti is plenty ... let's do something small: A tall ----------------------

Более читаемая версия large_bitset. В функции test_ small() мы применяем еще несколько операций.

void test_small()
{
    large_bitset<nat32, bits8> tall; // small is tall ...
        // ... because even this 'small' large_bitset 
        // can represent up to 2^32 == 4,294,967,296 bits.
    cout << "----- Test function test_small() -----------\n";
    cout << "-- Switch on all bits in range [0,64] ------\n";
    tall += discrete_interval<nat>(0, 64);
    tall.show_segments();
    cout << "--------------------------------------------\n";
    cout << "-- Turn off bits: 25,27,28 -----------------\n";
    (((tall -= 25) -= 27) -= 28) ;
    tall.show_segments();
    cout << "--------------------------------------------\n";
    cout << "-- Flip bits in range [24,30) --------------\n";
    tall ^= discrete_interval<nat>::right_open(24,30);
    tall.show_segments();
    cout << "--------------------------------------------\n";
    cout << "-- Remove the first 10 bits ----------------\n";
    tall -= discrete_interval<nat>::right_open(0,10);
    tall.show_segments();
    cout << "-- Remove even bits in range [0,72) --------\n";
    int bit;
    for(bit=0; bit<72; bit++) if(!(bit%2)) tall -= bit;
    tall.show_segments();
    cout << "--    Set odd  bits in range [0,72) --------\n";
    for(bit=0; bit<72; bit++) if(bit%2) tall += bit;
    tall.show_segments();
    cout << "--------------------------------------------\n\n";
}

.. производить этот вывод:

----- Test function test_small() -----------
-- Switch on all bits in range [0,64] ------
[0,8)->11111111
[8,9)->10000000
--------------------------------------------
-- Turn off bits: 25,27,28 -----------------
[0,3)->11111111
[3,4)->10100111
[4,8)->11111111
[8,9)->10000000
--------------------------------------------
-- Flip bits in range [24,30) --------------
[0,3)->11111111
[3,4)->01011011
[4,8)->11111111
[8,9)->10000000
--------------------------------------------
-- Remove the first 10 bits ----------------
[1,2)->00111111
[2,3)->11111111
[3,4)->01011011
[4,8)->11111111
[8,9)->10000000
-- Remove even bits in range [0,72) --------
[1,2)->00010101
[2,3)->01010101
[3,4)->01010001
[4,8)->01010101
--    Set odd  bits in range [0,72) --------
[0,9)->01010101
--------------------------------------------

Наконец, мы представляем немного picturesque пример, который демонстрирует, что large_bitset может также служить в качестве сжимающейся бит-карты, с которой мы можем «покрасить».

void test_picturesque()
{
    typedef large_bitset<nat, bits8> Bit8Set;
    Bit8Set square, stare;
    square += discrete_interval<nat>(0,8);
    for(int i=1; i<5; i++)
    {
        square += 8*i;
        square += 8*i+7;
    }
    square += discrete_interval<nat>(41,47);
    cout << "----- Test function test_picturesque() -----\n";
    cout << "-------- empty face:       "
         << square.interval_count()           << " intervals -----\n";
    square.show_matrix(" *");
    stare += 18; stare += 21;
    stare += discrete_interval<nat>(34,38);
    cout << "-------- compressed smile: "
         << stare.interval_count()            << " intervals -----\n";
    stare.show_matrix(" *");
    cout << "-------- staring bitset:   "
         << (square + stare).interval_count() << " intervals -----\n";
    (square + stare).show_matrix(" *");
    cout << "--------------------------------------------\n";
}

Обратите внимание, что у нас есть два large_bitsets для outline и interior. Обе части сжаты, но мы можем сочинять оба по оператор +, потому что доступны правильные позиции. Это результат программы:

----- Test function test_picturesque() -----
-------- empty face:       3 intervals -----
********
*      *
*      *
*      *
*      *
 ******
-------- compressed smile: 2 intervals -----
  *  *
  ****
-------- staring bitset:   6 intervals -----
********
*      *
* *  * *
*      *
* **** *
 ******
--------------------------------------------

Таким образом, вам может быть любопытно, как этот шаблон класса закодирован поверх interval_map, используя только около 250 строк кода. Это показано в следующих разделах.

Чтобы начать, давайте снова посмотрим на основной тип данных, который будет предоставлять основные функциональные возможности:

typedef interval_map<DomainT, BitSetT, partial_absorber,
                     std::less, inplace_bit_add, inplace_bit_and> IntervalBitmap;

DomainT is supposed to be an integral type, the bitset type BitSetT will be a wrapper class around an unsigned integral type. BitSetT has to implement bitwise operators that will be called by the functors inplace_bit_add<BitSetT> and inplace_bit_and<BitSetT>. The type trait of interval_map is partial_absorber, which means that it is partial and that empty BitSetTs are not stored in the map. This is desired and keeps the interval_map minimal, storing only bitsets, that contain at least one bit switched on. Functor template inplace_bit_add for parameter Combine indicates that we do not expect operator += as addition but the bitwise operator |=. For template parameter Section which is instaniated by inplace_bit_and we expect the bitwise &= operator.

Код проекта заключен в namespace mini. Название указывает на то, что реализация является минимальным примером реализации. Название класса битет будет биты или мини::биты, если они квалифицированы.

Для использования в качестве кодоменного параметра шаблона класса interval_map, mini::биты должны выполнять все функции, которые необходимы для кодомена_типа в целом, которые являются конструктором по умолчанию биты() и равенство оператор>> Из фанкторов inplace_bit_add и inplace_bit_and есть инверсные фанкторы inplace_bit_subtract и inplace_bit_xor. Эти фанкторы используют операторов |= &= ^= и ~. Наконец, если мы хотим применить лексикографическое и подмножественное сравнение на big_bitset, нам также нужен оператор <. Все операторы, которые нам нужны, могут быть реализованы для mini::bits в нескольких строках:

template<class NaturalT> class bits
{
public:
    typedef NaturalT word_type;
    static const int       digits = std::numeric_limits<NaturalT>::digits;
    static const word_type w1     = static_cast<NaturalT>(1) ;
    bits():_bits(){}
    explicit bits(word_type value):_bits(value){}
    word_type word()const{ return _bits; }
    bits& operator |= (const bits& value){_bits |= value._bits; return *this;}
    bits& operator &= (const bits& value){_bits &= value._bits; return *this;}
    bits& operator ^= (const bits& value){_bits ^= value._bits; return *this;}
    bits  operator ~  ()const { return bits(~_bits); }
    bool operator  <  (const bits& value)const{return _bits < value._bits;}
    bool operator  == (const bits& value)const{return _bits == value._bits;}
    bool contains(word_type element)const{ return ((w1 << element) & _bits) != 0; }
    std::string as_string(const char off_on[2] = " 1")const;
private:
    word_type _bits;
};

Наконец, есть один важный фрагмент мета-информации, мы должны предоставить: mini::bits должен быть признан Set по коду icl. В противном случае мы не можем использовать тот факт, что карта наборов является моделью Set, и полученный большой битет не будет вести себя как набор. Итак, мы должны сказать, что mini::биты должны быть наборами:

namespace boost { namespace icl
{
    template<class NaturalT>
    struct is_set<mini::bits<NaturalT> >
    {
        typedef is_set<mini::bits<NaturalT> > type;
        BOOST_STATIC_CONSTANT(bool, value = true);
    };
    template<class NaturalT>
    struct has_set_semantics<mini::bits<NaturalT> >
    {
        typedef has_set_semantics<mini::bits<NaturalT> > type;
        BOOST_STATIC_CONSTANT(bool, value = true);
    };
}}

Это делается путем добавления частичной специализации шаблона к шаблону icl::is_set. Для распространения этого типа шаблона trait и итоговых значений inclusion_compare нам нужны эти #includes для реализации mini::bits:

                                               // These includes are needed ...
#include <string>                              // for conversion to output and to
#include <boost/icl/type_traits/has_set_semantics.hpp>//declare that bits has the
                                               // behavior of a set.

Завершив наш mini::биты реализация, мы можем начать кодировать класс обертывания, который скрывает эффективную интервальную карту мини-::битов и выставляет простое и удобное поведение для мира пользователей.

Давайте начнем с необходимого #includes на этот раз:

#include <iostream>                   // to organize output
#include <limits>                     // limits and associated constants
#include <boost/operators.hpp>        // to define operators with minimal effort
#include "meta_log.hpp"               // a meta logarithm
#include "bits.hpp"                   // a minimal bitset implementation
#include <boost/icl/interval_map.hpp> // base of large bitsets
namespace mini // minimal implementations for example projects
{

Кроме того, boost/icl/interval_map.hpp и bits.hpp наиболее важным является boost/операторы.hpp. Мы используем эту библиотеку, чтобы еще больше минимизировать код и обеспечить довольно обширную функциональность оператора с использованием очень небольшого кода.

Для краткого и краткого обозначения наиболее важных неподписанных целых типов и соответствующих mini::бит мы определяем это:

typedef unsigned char      nat8; // nati i: number bits
typedef unsigned short     nat16;
typedef unsigned long      nat32;
typedef unsigned long long nat64;
typedef unsigned long      nat;
typedef bits<nat8>  bits8;
typedef bits<nat16> bits16;
typedef bits<nat32> bits32;
typedef bits<nat64> bits64;

А теперь давайте код large_bitset.

template
<
    typename    DomainT = nat64,
    typename    BitSetT = bits64,
    ICL_COMPARE Compare = ICL_COMPARE_INSTANCE(std::less, DomainT),
    ICL_INTERVAL(ICL_COMPARE) Interval = ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, DomainT, Compare),
    ICL_ALLOC   Alloc   = std::allocator
>
class large_bitset
    : boost::equality_comparable < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::less_than_comparable< large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::addable       < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::orable        < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::subtractable  < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::andable       < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::xorable       < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>
    , boost::addable2      < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, DomainT
    , boost::orable2       < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, DomainT
    , boost::subtractable2 < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, DomainT
    , boost::andable2      < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, DomainT
    , boost::xorable2      < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, DomainT
    , boost::addable2      < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, ICL_INTERVAL_TYPE(Interval,DomainT,Compare)
    , boost::orable2       < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, ICL_INTERVAL_TYPE(Interval,DomainT,Compare)
    , boost::subtractable2 < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, ICL_INTERVAL_TYPE(Interval,DomainT,Compare)
    , boost::andable2      < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, ICL_INTERVAL_TYPE(Interval,DomainT,Compare)
    , boost::xorable2      < large_bitset<DomainT,BitSetT,Compare,Interval,Alloc>, ICL_INTERVAL_TYPE(Interval,DomainT,Compare)
      > > > > > > > > > > > > > > > > >
    //^ & - | + ^ & - | + ^ & - | + < == 
    //segment   element   container

Первый параметр шаблона DomainT будет мгновенным с интегральным типом, который определяет тип чисел, которые могут быть элементами набора. Поскольку мы хотим пойти на большой набор, мы используем nat64 как по умолчанию, который является 64-битным неподписанным целым числом, начиная от 0 до 2^64-1. Как параметр битсета мы также выбираем 64-битный по умолчанию. Параметры Combine и Interval необходимо передавать на зависимые выражения типа. При желании может быть выбран аллокат.

Вложенный список частного наследования содержит группы шаблонных мгновенных сообщений от Boost.Operator, которые предоставляют производных операторов от более фундаментальных раз. Реализуя основные операторы, мы получаем производные бесплатно. Ниже приведен краткий обзор того, что мы получаем с помощью Boost.Operator, где S означает large_bitset, i для него interval_type и e для него domain_type или .

Группа

основные

деривируемый

Equality, ordering

==

!=

<

> <= >=

Set operators (S x S)

+= |= -= &= ^=

+ | - & ^

Set operators (S x e)

+= |= -= &= ^=

+ | - & ^

(S x i)

+= |= -= &= ^=

+ | - & ^

Существует ряд связанных типов

typedef boost::icl::interval_map
    <DomainT, BitSetT, boost::icl::partial_absorber,
     std::less, boost::icl::inplace_bit_add, boost::icl::inplace_bit_and> interval_bitmap_type;
typedef DomainT                                      domain_type;
typedef DomainT                                      element_type;
typedef BitSetT                                      bitset_type;
typedef typename BitSetT::word_type                  word_type;
typedef typename interval_bitmap_type::interval_type interval_type;
typedef typename interval_bitmap_type::value_type    value_type;

самое главное - реализация interval_bitmap_type, которая используется для реализации контейнера.

private:
    interval_bitmap_type _map;

Для того, чтобы использовать Boost.Operator, мы должны реализовать основные операторы в качестве членов класса. Это можно сделать довольно схематично.

public:
    bool     operator ==(const large_bitset& rhs)const { return _map == rhs._map; }
    bool     operator < (const large_bitset& rhs)const { return _map <  rhs._map; }
    large_bitset& operator +=(const large_bitset& rhs) {_map += rhs._map; return *this;}
    large_bitset& operator |=(const large_bitset& rhs) {_map |= rhs._map; return *this;}
    large_bitset& operator -=(const large_bitset& rhs) {_map -= rhs._map; return *this;}
    large_bitset& operator &=(const large_bitset& rhs) {_map &= rhs._map; return *this;}
    large_bitset& operator ^=(const large_bitset& rhs) {_map ^= rhs._map; return *this;}
    large_bitset& operator +=(const element_type& rhs) {return add(interval_type(rhs));      }
    large_bitset& operator |=(const element_type& rhs) {return add(interval_type(rhs));      }
    large_bitset& operator -=(const element_type& rhs) {return subtract(interval_type(rhs)); }
    large_bitset& operator &=(const element_type& rhs) {return intersect(interval_type(rhs));}
    large_bitset& operator ^=(const element_type& rhs) {return flip(interval_type(rhs));     }
    large_bitset& operator +=(const interval_type& rhs){return add(rhs);      }
    large_bitset& operator |=(const interval_type& rhs){return add(rhs);      }
    large_bitset& operator -=(const interval_type& rhs){return subtract(rhs); }
    large_bitset& operator &=(const interval_type& rhs){return intersect(rhs);}
    large_bitset& operator ^=(const interval_type& rhs){return flip(rhs);     }

Как мы видим, семь наиболее важных операторов, которые работают на типе класса large_bitset, могут быть непосредственно реализованы путем распространения операции на реализацию _map типа interval_bitmap_type. Для операторов, которые работают на сегментах и типах элементов, мы используем функции add, subtract, intersect и flip. Поскольку мы увидим, что для объединения этих функций с функциональностью реализующего контейнера требуется лишь небольшое количество адапера.

addition, subtraction, intersection или симметричное различие, после перевода границ интервала в правый битсет позиции. .

large_bitset& add      (const interval_type& rhs){return segment_apply(&large_bitset::add_,      rhs);}
large_bitset& subtract (const interval_type& rhs){return segment_apply(&large_bitset::subtract_, rhs);}
large_bitset& intersect(const interval_type& rhs){return segment_apply(&large_bitset::intersect_,rhs);}
large_bitset& flip     (const interval_type& rhs){return segment_apply(&large_bitset::flip_,     rhs);}

В выборочных программах, которые мы представим, чтобы продемонстрировать возможности large_bitset, нам понадобится несколько дополнительных функций, специально выводимых в двух разных вкусах.

size_t interval_count()const { return boost::icl::interval_count(_map); }
void show_segments()const
{
    for(typename interval_bitmap_type::const_iterator it_ = _map.begin();
        it_ != _map.end(); ++it_)
    {
        interval_type   itv  = it_->first;
        bitset_type     bits = it_->second;
        std::cout << itv << "->" << bits.as_string("01") << std::endl;
    }
}
void show_matrix(const char off_on[2] = " 1")const
{
    using namespace boost;
    typename interval_bitmap_type::const_iterator iter = _map.begin();
    while(iter != _map.end())
    {
        element_type fst = icl::first(iter->first), lst = icl::last(iter->first);
        for(element_type chunk = fst; chunk <= lst; chunk++)
            std::cout << iter->second.as_string(off_on) << std::endl;
        ++iter;
    }
}

  • Первый, show_segments() показывает содержание контейнера по мере его реализации в сжатой форме.
  • Вторая функция show_matrix показывает полную матрицу битов, которая представлена контейнером.

Для реализации таких операций, как добавление элемента, скажем 42 к большому биту, нам нужно перевести value на позицион бит, представляющий 42 в интервале контейнера битетов. В качестве примера предположим, что мы используем

large_bitset<nat, mini::bits8> lbs;

который несет только небольшие биты 8 бит. Предполагается, что первые четыре интервала lbs связаны с некоторыми битами. Теперь мы хотим добавить интервал [a,b]==[5,27]. Это приведет к следующему:

   [0,1)->   [1,2)->   [2,3)->   [3,4)->
   [00101100][11001011][11101001][11100000]
+       [111  11111111  11111111  1111]      [5,27] as bitset
         a                           b
=> [0,1)->   [1,3)->   [3,4)->
   [00101111][11111111][11110000]

A = a/8 =  5/8 = 0 // refers to interval 
B = b/8 = 27/8 = 3
R = a%8 =  5%8 = 5 // refers to the position in the associated bitset.
S = b%8 = 27%8 = 3

d Это мощность 2: d = 2^x. Поэтому разделение и модуло могут быть выражены битетными операциями. Константы, необходимые для этих вычислений, определены здесь:

private:                                      // Example value
    static const word_type                    //   8-bit case  
        digits  = std::numeric_limits         // --------------------------------------------------------------
                  <word_type>::digits       , //   8           Size of the associated bitsets 
        divisor = digits                    , //   8           Divisor to find intervals for values
        last    = digits-1                  , //   7           Last bit (0 based)
        shift   = log2_<divisor>::value     , //   3           To express the division as bit shift
        w1      = static_cast<word_type>(1) , //               Helps to avoid static_casts for long long
        mask    = divisor - w1              , //   7=11100000  Helps to express the modulo operation as bit_and
        all     = ~static_cast<word_type>(0), // 255=11111111  Helps to express a complete associated bitset
        top     = w1 << (digits-w1)      ;    // 128=00000001  Value of the most significant bit of associated bitsets
                                              //            !> Note: Most signigicant bit on the right.

Глядя на пример снова, мы видим, что мы должны определить позиции начала и окончания интервала [5,27], то есть вставить, а затем subdivide этот диапазон битов на три раздела.

  1. Набор, где начинается интервал.
  2. битсет, где заканчивается интервал
  3. Наборы, которые полностью перекрываются интервалом

combine interval [5,27] to large_bitset lbs w.r.t. some operation o
   [0,1)->   [1,2)->   [2,3)->   [3,4)->
   [00101100][11001011][11101001][11100000]
o       [111  11111111  11111111  1111]
         a                           b
subdivide:
   [first!  ][mid_1] . . .[mid_n][   !last]
   [00000111][1...1] . . .[1...1][11110000]

После разделения мы выполняем операцию o следующим образом:

  1. Для первого набора: Установите все биты от запуска r (!) до конца набора до 1. Все остальные биты 0. Затем выполнить операцию о: _map о= ([0,1)->000111>
  2. Для диапазона битов между звездой и окончанием одного, выполнить операцию o: _map o= (1,3)->11111)

The algorithm, that has been outlined and illustrated by the example, is implemented by the private member function segment_apply. To make the combiner operation a variable in this algorithm, we use a pointer to member function type

typedef void (large_bitset::*segment_combiner)(element_type, element_type, bitset_type);

как первый аргумент функции. Мы проведем функции членов combine_ здесь,

combine_(first_of_interval, end_of_interval, some_bitset);

interval_bitmap_type _map. Вот эти функции:

void       add_(DomainT lo, DomainT up, BitSetT bits){_map += value_type(interval_type::right_open(lo,up), bits);}
void  subtract_(DomainT lo, DomainT up, BitSetT bits){_map -= value_type(interval_type::right_open(lo,up), bits);}
void intersect_(DomainT lo, DomainT up, BitSetT bits){_map &= value_type(interval_type::right_open(lo,up), bits);}
void      flip_(DomainT lo, DomainT up, BitSetT bits){_map ^= value_type(interval_type::right_open(lo,up), bits);}

Наконец, мы можем кодировать функцию segment_apply, что делает разделение и последующее объединение:

large_bitset& segment_apply(segment_combiner combine, const interval_type& operand)
{
    using namespace boost;
    if(icl::is_empty(operand))
        return *this;
                                                        // same as
    element_type   base = icl::first(operand) >> shift, // icl::first(operand) / divisor
                   ceil = icl::last (operand) >> shift; // icl::last (operand) / divisor
    word_type base_rest = icl::first(operand) &  mask , // icl::first(operand) % divisor
              ceil_rest = icl::last (operand) &  mask ; // icl::last (operand) % divisor  
    if(base == ceil) // [first, last] are within one bitset (chunk)
        (this->*combine)(base, base+1, bitset_type(  to_upper_from(base_rest)
                                                   & from_lower_to(ceil_rest)));
    else // [first, last] spread over more than one bitset (chunk)
    {
        element_type mid_low = base_rest == 0   ? base   : base+1, // first element of mid part 
                     mid_up  = ceil_rest == all ? ceil+1 : ceil  ; // last  element of mid part
        if(base_rest > 0)    // Bitset of base interval has to be filled from base_rest to last
            (this->*combine)(base, base+1, bitset_type(to_upper_from(base_rest)));
        if(ceil_rest < all)  // Bitset of ceil interval has to be filled from first to ceil_rest
            (this->*combine)(ceil, ceil+1, bitset_type(from_lower_to(ceil_rest)));
        if(mid_low < mid_up) // For the middle part all bits have to set.
            (this->*combine)(mid_low, mid_up, bitset_type(all));
    }
    return *this;
}

Функции, которые помогают заполнять биты в определенный момент и из него, реализованы здесь:

static word_type from_lower_to(word_type bit){return bit==last ? all : (w1<<(bit+w1))-w1;}
static word_type to_upper_from(word_type bit){return bit==last ? top : ~((w1<<bit)-w1); }

Это завершает реализацию шаблона класса large_bitset. Используя только небольшое количество в основном схематического кода, мы смогли предоставить довольно мощный, сжимающий и обычно используемый тип набора для всех типов интегральных доменов.


PrevUpHomeNext

Статья Projects раздела Chapter 1. Boost.Icl Chapter 1. Boost.Icl может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 1. Boost.Icl ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-04 18:54:42/0.015594005584717/0