Проекты являются примерами использования интервальных контейнеров, которые выходят за рамки небольших игрушек фрагментов кода. Представленный здесь код касается более серьезных приложений, которые подходят к качеству программирования реального мира. В то же время он направлен на то, чтобы направить читателя глубже в различные аспекты библиотеки. Чтобы не перегружать читателя деталями реализации, код в проекты пытается быть минимальный. Он сосредоточен на основных аспектах проектов и не предназначен для того, чтобы быть полным и зрелым, как сам библиотечный код. Потому что это минимально, код проекта живет в namespacemini.
Фишки - это просто наборы. Наборы неподписанных интегралов, чтобы быть более точными. Префикс бит обычно только указывает, что представление этих наборов организовано в сжатой форме, которая эксплуатирует факт, что мы можем переключаться на один бит в машинных словах. Таким образом, битуты, как известно, очень малы и, следовательно, эффективны. Эффективность битет обычно сочетается с предварительным условием, что диапазон значений элементов относительно мал, как [0.32] или [0.64], значения, которые обычно могут быть представлены в одном или небольшом количестве машинных слов. Если бы мы хотели представить набор, содержащий два значения {1, 100000}, нам было бы гораздо лучше использовать другие наборы, такие как std::set.
и мы можем преодолевать интервалы между равными битовыми последовательностями, которые представляют собой периодические шаблоны.
[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, так это кодировать обертку classlarge_bitset около IntervalBitmap, чтобы large_bitset выглядел и чувствовал себя как обычный класс.
Чтобы ускорить аппетит к просмотру реализации, сначала несколько вариантов использования. В следующих примерах мы будем использовать natk для неподписанных интегралов и bitsk для битов, содержащих k.
Начнем с большого. В первом примере...
voidtest_large(){constnat64much=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):
-----Testfunctiontest_large()-----------------------------------------------Wehavejustturnedontheawesomeamountof18,446,744,073,709,551,616bits;-)[0,288230376151711744)->1111111111111111111111111111111111111111111111111111111111111111----Let's swich off the very last bit -----------------------------------------
[ 0, 288230376151711743) -> 1111111111111111111111111111111111111111111111111111111111111111
[288230376151711743, 288230376151711744) -> 1111111111111111111111111111111111111111111111111111111111111110
---- Venti is plenty ... let'sdosomethingsmall:Atall----------------------
Более читаемая версия large_bitset. В функции test_ small() мы применяем еще несколько операций.
voidtest_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";intbit;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";}
Наконец, мы представляем немного picturesque пример, который демонстрирует, что large_bitset может также служить в качестве сжимающейся бит-карты, с которой мы можем «покрасить».
Обратите внимание, что у нас есть два large_bitsets для outline и interior. Обе части сжаты, но мы можем сочинять оба по оператор+, потому что доступны правильные позиции. Это результат программы:
Таким образом, вам может быть любопытно, как этот шаблон класса закодирован поверх interval_map, используя только около 250 строк кода. Это показано в следующих разделах.
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.
Код проекта заключен в namespacemini. Название указывает на то, что реализация является минимальным примером реализации. Название класса битет будет биты или мини::биты, если они квалифицированы.
Для использования в качестве кодоменного параметра шаблона класса interval_map, mini::биты должны выполнять все функции, которые необходимы для кодомена_типа в целом, которые являются конструктором по умолчанию биты() и равенство оператор>> Из фанкторов inplace_bit_add и inplace_bit_and есть инверсные фанкторы inplace_bit_subtract и inplace_bit_xor. Эти фанкторы используют операторов |=&=^= и ~. Наконец, если мы хотим применить лексикографическое и подмножественное сравнение на big_bitset, нам также нужен оператор<. Все операторы, которые нам нужны, могут быть реализованы для mini::bits в нескольких строках:
Наконец, есть один важный фрагмент мета-информации, мы должны предоставить: mini::bits должен быть признан Set по коду icl. В противном случае мы не можем использовать тот факт, что карта наборов является моделью Set, и полученный большой битет не будет вести себя как набор. Итак, мы должны сказать, что mini::биты должны быть наборами:
Это делается путем добавления частичной специализации шаблона к шаблону 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>//declarethatbitshasthe// 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 bitsetsnamespacemini// minimal implementations for example projects{
Кроме того, boost/icl/interval_map.hpp и bits.hpp наиболее важным является boost/операторы.hpp. Мы используем эту библиотеку, чтобы еще больше минимизировать код и обеспечить довольно обширную функциональность оператора с использованием очень небольшого кода.
Для краткого и краткого обозначения наиболее важных неподписанных целых типов и соответствующих mini::бит мы определяем это:
typedefunsignedcharnat8;// nati i: number bitstypedefunsignedshortnat16;typedefunsignedlongnat32;typedefunsignedlonglongnat64;typedefunsignedlongnat;typedefbits<nat8>bits8;typedefbits<nat16>bits16;typedefbits<nat32>bits32;typedefbits<nat64>bits64;
Первый параметр шаблона DomainT будет мгновенным с интегральным типом, который определяет тип чисел, которые могут быть элементами набора. Поскольку мы хотим пойти на большой набор, мы используем nat64 как по умолчанию, который является 64-битным неподписанным целым числом, начиная от 0 до 2^64-1. Как параметр битсета мы также выбираем 64-битный по умолчанию. Параметры Combine и Interval необходимо передавать на зависимые выражения типа. При желании может быть выбран аллокат.
Вложенный список частного наследования содержит группы шаблонных мгновенных сообщений от Boost.Operator, которые предоставляют производных операторов от более фундаментальных раз. Реализуя основные операторы, мы получаем производные бесплатно. Ниже приведен краткий обзор того, что мы получаем с помощью Boost.Operator, где S означает large_bitset, i для него interval_type и e для него domain_type или .
Как мы видим, семь наиболее важных операторов, которые работают на типе класса large_bitset, могут быть непосредственно реализованы путем распространения операции на реализацию _map типа interval_bitmap_type. Для операторов, которые работают на сегментах и типах элементов, мы используем функции add, subtract, intersect и flip. Поскольку мы увидим, что для объединения этих функций с функциональностью реализующего контейнера требуется лишь небольшое количество адапера.
addition, subtraction,
intersection или симметричное различие,
после перевода границ интервала в правый битсет
позиции.
.
В выборочных программах, которые мы представим, чтобы продемонстрировать возможности large_bitset, нам понадобится несколько дополнительных функций, специально выводимых в двух разных вкусах.
Для реализации таких операций, как добавление элемента, скажем 42 к большому биту, нам нужно перевести value на позиционбит, представляющий 42 в интервале контейнера битетов. В качестве примера предположим, что мы используем
large_bitset<nat,mini::bits8>lbs;
который несет только небольшие биты 8 бит. Предполагается, что первые четыре интервала lbs связаны с некоторыми битами. Теперь мы хотим добавить интервал [a,b]==[5,27]. Это приведет к следующему:
A=a/8=5/8=0// refers to interval B=b/8=27/8=3R=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 valuestaticconstword_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 valueslast=digits-1,// 7 Last bit (0 based)shift=log2_<divisor>::value,// 3 To express the division as bit shiftw1=static_cast<word_type>(1),// Helps to avoid static_casts for long longmask=divisor-w1,// 7=11100000 Helps to express the modulo operation as bit_andall=~static_cast<word_type>(0),// 255=11111111 Helps to express a complete associated bitsettop=w1<<(digits-w1);// 128=00000001 Value of the most significant bit of associated bitsets// !> Note: Most signigicant bit on the right.
Глядя на пример снова, мы видим, что мы должны определить позиции начала и окончания интервала [5,27], то есть вставить, а затем subdivide этот диапазон битов на три раздела.
Набор, где начинается интервал.
битсет, где заканчивается интервал
Наборы, которые полностью перекрываются интервалом
После разделения мы выполняем операцию o следующим образом:
Для первого набора: Установите все биты от запуска r (!) до конца набора до 1. Все остальные биты 0. Затем выполнить операцию о: _mapо=([0,1)->000111>
Для диапазона битов между звездой и окончанием одного, выполнить операцию o: _mapo=(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
Наконец, мы можем кодировать функцию segment_apply, что делает разделение и последующее объединение:
large_bitset&segment_apply(segment_combinercombine,constinterval_type&operand){usingnamespaceboost;if(icl::is_empty(operand))return*this;// same aselement_typebase=icl::first(operand)>>shift,// icl::first(operand) / divisorceil=icl::last(operand)>>shift;// icl::last (operand) / divisorword_typebase_rest=icl::first(operand)&mask,// icl::first(operand) % divisorceil_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_typemid_low=base_rest==0?base:base+1,// first element of mid part mid_up=ceil_rest==all?ceil+1:ceil;// last element of mid partif(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;}
Функции, которые помогают заполнять биты в определенный момент и из него, реализованы здесь:
Это завершает реализацию шаблона класса large_bitset. Используя только небольшое количество в основном схематического кода, мы смогли предоставить довольно мощный, сжимающий и обычно используемый тип набора для всех типов интегральных доменов.
Статья Projects раздела Chapter 1. Boost.Icl Chapter 1. Boost.Icl может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.