aboutsummaryrefslogblamecommitdiffstats
path: root/util/generic/bitmap.h
blob: 18a153bc5fd86e0c246e1c420078bd7e4e236f04 (plain) (tree)
1
2
3
4
5
6
7
8
            
 
                
                   
                       
                      
                    

                                 
                         
                           
                          










                                                                                                  
                                                    



                                                        
                                                                             
      
 
                                         
                                                                        
      
 


                                                                                                                                   
 
                                                                                         
 


                                               
 

                                                                                 
                         
                                                                                        
     
                                                                                 
      
         
 
                                                                                                                         
                                                            




                                                                                 
         
      
 


                                                                                                                                   
 
                                                                                         
 


                                               
 
                                                                              
                         
                                                                    
     
                                                             
      
                             
 
                                                                                            
                                          
                          
                         
                                                               
     
                                                        
      
                             
 
                                                                                                                         
                                                            








                                                                                            
         
      
 

                                                                                                             
                                               




                                                              
         
      
 



                                                     
      
                                                           
                                                                                                                                                                                                                                                                                                                
      
 


                                                                                                                  
 
                                                   
                                  
 
                                                                                                  
 
                          
 

                         
 



                                                                      
 
                                                             
                                                                                                               
                                             
         
                                                                           


                                               
 
                                                                          
                            
         
                                                                            
                        
 
                                                                                             
                                                                                           
                                       
 
                                        

                                                                                            
 


                                                                             
                                  
 


                                       
 




                              
 











                                                          
 






                                                                 
         
 
                                                                   



                                                                        
         
                                                      
                                             
 
                                                        
                        
 
                                                                                             
                                                                           
                              
                                           






                                                               
                 

                                           
             
                        
         
 
                                                                                 
                                                                                                   
 
                                        
         
 
                         
                                                                         
      
 
                         
                                          
      
 
 

                                               
                                                                         


                              
                                                                 


                        
       
                                            
        
                                                                                             
 


                                                                                          
 
                            
 
                                                
                                                               
                                                                                                     
















                                            
                                
 
                                                        

                                                           
                                                              


                         
                                                                     






                                                              
                                               

                                                                      
                                              

                                                                      
                                           





                                                       
                                                                                  



                              
                                                                                  










                                                      





                                                                     
             
                                                                           


         
                           
 
                            
                           
     
                                       
 



                                                                
     
                      
                                                                       

                             
                                                          






                              
                                                                  




                         
                                                                   

                           
                                                         


                        
                                                                   

                          
                                                         


                       
                                                                   

                           
                                                         

                        
                      
                                                                   

                                     
                                                         

                                  
                                                   
                           
     
                                                   
                           
     
                                                       

                                        
                                                       

                                        
                                                      

                        
                                                      
                                                            
                             

                                                                      
                                             

                                  
                                           
                                                            
                             




                                                                                         
                               





                                            
                                             






                                                                                       
                               
                                                                           
                                                                          


                     
                                            
                                                            
                             


                                                                                
                                               




                                                                                            
                        
                                            
                                                                                           










                                                                                                                                                 
                                                                                                                          





                                                                                                                                                          
                                              

                      
                                          
                                    
     
                               
                              
     
                                                              
                                   


                                                              
     
                            
                                                           

                                     
                                                  
                                                                                    
     
                                                 


                                                             
                                                                                                      
                       
     
                                                       
                                                            


                             








                                                                                                   
                                                                   

                                     
                                                         
















                                                                                                   
                                                                   

                                     
                                                         










                                                                                                 
     
                      
                                                            
                                  
     
                                                  
















                                                                                                     
                                                           

                                 
                                                 




                                     
                               




                                                                   
                                                            

                                  
                                                  



                            





                                                                                                 
                                                                      

                                            
                                                            


                             
                                  








































































































                                                                                                                                         
                                                                              












                                                                                               
                                                                  






















                                                                                               
                                                                   


                                      
                                                                       

                            
                                    












                                                                                                                           
                                                        
                                             
                                         






                                                                                   
                 
                                             
             


                                                                                            
                 
         
                      
     
                                         
                         
                                                            
                                                                      
                     
 
                                         



                                                                
                                  
                               
                                                                                    













                                                                                                         
                        
     













                                                                            



                                            
                                                     
                                       
  
                           
                                                                                


                                   
                                                                                                 


                                   
                                                                                                 


                                   
                                                                                


                                  
                                                                                                 


                                  
                                                                                                 


                                  
                                                                                


                                   
                                                                                                 


                                   
                                                                                                 

                                   
                           
                                                                                

                                             
                  
                                                                                                 


                                             
                                                                                                 


                                             
                                                        



                                                              
                                                          
                                                                            
        
                                                                       
 









                                       
                                                 






                                        
                                                          
 
                                                                                                                                             
                           
                                   


                                                              
#pragma once

#include "fwd.h"
#include "ptr.h"
#include "bitops.h"
#include "typetraits.h"
#include "algorithm.h"
#include "utility.h"

#include <util/system/yassert.h>
#include <util/system/defaults.h>
#include <util/str_stl.h>
#include <util/ysaveload.h>

namespace NBitMapPrivate {
    // Returns number of bits set; result is in most significatnt byte
    inline ui64 ByteSums(ui64 x) {
        ui64 byteSums = x - ((x & 0xAAAAAAAAAAAAAAAAULL) >> 1);

        byteSums = (byteSums & 0x3333333333333333ULL) + ((byteSums >> 2) & 0x3333333333333333ULL);
        byteSums = (byteSums + (byteSums >> 4)) & 0x0F0F0F0F0F0F0F0FULL;

        return byteSums * 0x0101010101010101ULL;
    }

    // better than intrinsics without -mpopcnt
    template <typename T>
    static unsigned CountBitsPrivate(T v) noexcept {
        return static_cast<unsigned>(ByteSums(v) >> 56);
    }

    template <typename TChunkType, size_t ExtraBits>
    struct TSanitizeMask {
        static constexpr TChunkType Value = ~((~TChunkType(0)) << ExtraBits);
    };

    template <typename TChunkType>
    struct TSanitizeMask<TChunkType, 0> {
        static constexpr TChunkType Value = (TChunkType)~TChunkType(0u);
    };

    template <typename TTargetChunk, typename TSourceChunk>
    struct TBigToSmallDataCopier {
        static_assert(sizeof(TTargetChunk) < sizeof(TSourceChunk), "expect sizeof(TTargetChunk) < sizeof(TSourceChunk)");
        static_assert(0 == sizeof(TSourceChunk) % sizeof(TTargetChunk), "expect 0 == sizeof(TSourceChunk) % sizeof(TTargetChunk)");

        static constexpr size_t BLOCK_SIZE = sizeof(TSourceChunk) / sizeof(TTargetChunk);

        union TCnv {
            TSourceChunk BigData;
            TTargetChunk SmallData[BLOCK_SIZE];
        };

        static inline void CopyChunk(TTargetChunk* target, TSourceChunk source) {
            TCnv c;
            c.BigData = source;
#if defined(_big_endian_)
            ::ReverseCopy(c.SmallData, c.SmallData + Y_ARRAY_SIZE(c.SmallData), target);
#else
            ::Copy(c.SmallData, c.SmallData + Y_ARRAY_SIZE(c.SmallData), target);
#endif
        }

        static inline void Copy(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) {
            Y_ASSERT(targetSize >= sourceSize * BLOCK_SIZE);
            if (targetSize > sourceSize * BLOCK_SIZE) {
                ::Fill(target + sourceSize * BLOCK_SIZE, target + targetSize, 0);
            }
            for (size_t i = 0; i < sourceSize; ++i) {
                CopyChunk(target + i * BLOCK_SIZE, source[i]);
            }
        }
    };

    template <typename TTargetChunk, typename TSourceChunk>
    struct TSmallToBigDataCopier {
        static_assert(sizeof(TTargetChunk) > sizeof(TSourceChunk), "expect sizeof(TTargetChunk) > sizeof(TSourceChunk)");
        static_assert(0 == sizeof(TTargetChunk) % sizeof(TSourceChunk), "expect 0 == sizeof(TTargetChunk) % sizeof(TSourceChunk)");

        static constexpr size_t BLOCK_SIZE = sizeof(TTargetChunk) / sizeof(TSourceChunk);

        union TCnv {
            TSourceChunk SmallData[BLOCK_SIZE];
            TTargetChunk BigData;
        };

        static inline TTargetChunk CopyFullChunk(const TSourceChunk* source) {
            TCnv c;
#if defined(_big_endian_)
            ::ReverseCopy(source, source + BLOCK_SIZE, c.SmallData);
#else
            ::Copy(source, source + BLOCK_SIZE, c.SmallData);
#endif
            return c.BigData;
        }

        static inline TTargetChunk CopyPartChunk(const TSourceChunk* source, size_t count) {
            Y_ASSERT(count <= BLOCK_SIZE);
            TCnv c;
            c.BigData = 0;
#if defined(_big_endian_)
            ::ReverseCopy(source, source + count, c.SmallData);
#else
            ::Copy(source, source + count, c.SmallData);
#endif
            return c.BigData;
        }

        static inline void Copy(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) {
            Y_ASSERT(targetSize * BLOCK_SIZE >= sourceSize);
            if (targetSize * BLOCK_SIZE > sourceSize) {
                ::Fill(target + sourceSize / BLOCK_SIZE, target + targetSize, 0);
            }
            size_t i = 0;
            for (; i < sourceSize / BLOCK_SIZE; ++i) {
                target[i] = CopyFullChunk(source + i * BLOCK_SIZE);
            }
            if (0 != sourceSize % BLOCK_SIZE) {
                target[i] = CopyPartChunk(source + i * BLOCK_SIZE, sourceSize % BLOCK_SIZE);
            }
        }
    };

    template <typename TChunk>
    struct TUniformDataCopier {
        static inline void Copy(TChunk* target, size_t targetSize, const TChunk* source, size_t sourceSize) {
            Y_ASSERT(targetSize >= sourceSize);
            for (size_t i = 0; i < sourceSize; ++i) {
                target[i] = source[i];
            }
            for (size_t i = sourceSize; i < targetSize; ++i) {
                target[i] = 0;
            }
        }
    };

    template <typename TFirst, typename TSecond>
    struct TIsSmaller {
        enum {
            Result = sizeof(TFirst) < sizeof(TSecond)
        };
    };

    template <typename TTargetChunk, typename TSourceChunk>
    struct TDataCopier: public std::conditional_t<std::is_same<TTargetChunk, TSourceChunk>::value, TUniformDataCopier<TTargetChunk>, std::conditional_t<TIsSmaller<TTargetChunk, TSourceChunk>::Result, TBigToSmallDataCopier<TTargetChunk, TSourceChunk>, TSmallToBigDataCopier<TTargetChunk, TSourceChunk>>> {
    };

    template <typename TTargetChunk, typename TSourceChunk>
    inline void CopyData(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) {
        TDataCopier<TTargetChunk, TSourceChunk>::Copy(target, targetSize, source, sourceSize);
    }

    template <size_t BitCount, typename TChunkType>
    struct TFixedStorage {
        using TChunk = TChunkType;

        static constexpr size_t Size = (BitCount + 8 * sizeof(TChunk) - 1) / (8 * sizeof(TChunk));

        TChunk Data[Size];

        TFixedStorage() {
            Zero(Data);
        }

        TFixedStorage(const TFixedStorage<BitCount, TChunkType>& st) {
            for (size_t i = 0; i < Size; ++i) {
                Data[i] = st.Data[i];
            }
        }

        template <typename TOtherChunk>
        TFixedStorage(const TOtherChunk* data, size_t size) {
            Y_VERIFY(Size * sizeof(TChunk) >= size * sizeof(TOtherChunk), "Exceeding bitmap storage capacity");
            CopyData(Data, Size, data, size);
        }

        Y_FORCE_INLINE void Swap(TFixedStorage<BitCount, TChunkType>& st) {
            for (size_t i = 0; i < Size; ++i) {
                DoSwap(Data[i], st.Data[i]);
            }
        }

        Y_FORCE_INLINE static constexpr size_t GetBitCapacity() noexcept {
            return BitCount;
        }

        Y_FORCE_INLINE static constexpr size_t GetChunkCapacity() noexcept {
            return Size;
        }

        // Returns true if the resulting storage capacity is enough to fit the requested size
        Y_FORCE_INLINE static constexpr bool ExpandBitSize(const size_t bitSize) noexcept {
            return bitSize <= BitCount;
        }

        Y_FORCE_INLINE void Sanitize() {
            Data[Size - 1] &= TSanitizeMask<TChunk, BitCount % (8 * sizeof(TChunk))>::Value;
        }
    };

    // Dynamically expanded storage.
    // It uses "on stack" realization with no allocation for one chunk spaces
    template <typename TChunkType>
    struct TDynamicStorage {
        using TChunk = TChunkType;

        size_t Size;
        TChunk StackData;
        TArrayHolder<TChunk> ArrayData;
        TChunk* Data;

        TDynamicStorage()
            : Size(1)
            , StackData(0)
            , Data(&StackData)
        {
        }

        TDynamicStorage(const TDynamicStorage<TChunk>& st)
            : Size(1)
            , StackData(0)
            , Data(&StackData)
        {
            ExpandSize(st.Size, false);
            for (size_t i = 0; i < st.Size; ++i) {
                Data[i] = st.Data[i];
            }
            for (size_t i = st.Size; i < Size; ++i) {
                Data[i] = 0;
            }
        }

        template <typename TOtherChunk>
        TDynamicStorage(const TOtherChunk* data, size_t size)
            : Size(1)
            , StackData(0)
            , Data(&StackData)
        {
            ExpandBitSize(size * sizeof(TOtherChunk) * 8, false);
            CopyData(Data, Size, data, size);
        }

        Y_FORCE_INLINE void Swap(TDynamicStorage<TChunkType>& st) {
            DoSwap(Size, st.Size);
            DoSwap(StackData, st.StackData);
            DoSwap(ArrayData, st.ArrayData);
            Data = 1 == Size ? &StackData : ArrayData.Get();
            st.Data = 1 == st.Size ? &st.StackData : st.ArrayData.Get();
        }

        Y_FORCE_INLINE size_t GetBitCapacity() const {
            return Size * 8 * sizeof(TChunk);
        }

        Y_FORCE_INLINE size_t GetChunkCapacity() const {
            return Size;
        }

        // Returns true if the resulting storage capacity is enough to fit the requested size
        Y_FORCE_INLINE bool ExpandSize(size_t size, bool keepData = true) {
            if (size > Size) {
                size = Max(size, Size * 2);
                TArrayHolder<TChunk> newData(new TChunk[size]);
                if (keepData) {
                    for (size_t i = 0; i < Size; ++i) {
                        newData[i] = Data[i];
                    }
                    for (size_t i = Size; i < size; ++i) {
                        newData[i] = 0;
                    }
                }
                DoSwap(ArrayData, newData);
                Data = ArrayData.Get();
                Size = size;
            }
            return true;
        }

        Y_FORCE_INLINE bool ExpandBitSize(size_t bitSize, bool keepData = true) {
            return ExpandSize((bitSize + 8 * sizeof(TChunk) - 1) / (8 * sizeof(TChunk)), keepData);
        }

        Y_FORCE_INLINE void Sanitize() {
        }
    };

    template <size_t num>
    struct TDivCount {
        static constexpr size_t Value = 1 + TDivCount<(num >> 1)>::Value;
    };

    template <>
    struct TDivCount<0> {
        static constexpr size_t Value = 0;
    };

}

template <size_t BitCount, typename TChunkType>
struct TFixedBitMapTraits {
    using TChunk = TChunkType;
    using TStorage = NBitMapPrivate::TFixedStorage<BitCount, TChunkType>;
};

template <typename TChunkType>
struct TDynamicBitMapTraits {
    using TChunk = TChunkType;
    using TStorage = NBitMapPrivate::TDynamicStorage<TChunkType>;
};

template <class TTraits>
class TBitMapOps {
public:
    using TChunk = typename TTraits::TChunk;
    using TThis = TBitMapOps<TTraits>;

private:
    static_assert(std::is_unsigned<TChunk>::value, "expect std::is_unsigned<TChunk>::value");

    static constexpr size_t BitsPerChunk = 8 * sizeof(TChunk);
    static constexpr TChunk ModMask = static_cast<TChunk>(BitsPerChunk - 1);
    static constexpr size_t DivCount = NBitMapPrivate::TDivCount<BitsPerChunk>::Value - 1;
    static constexpr TChunk FullChunk = (TChunk)~TChunk(0);

    template <class>
    friend class TBitMapOps;

    using TStorage = typename TTraits::TStorage;

    // The smallest unsigned type, which can be used in bit ops
    using TIntType = std::conditional_t<sizeof(TChunk) < sizeof(unsigned int), unsigned int, TChunk>;

    TStorage Mask;

public:
    class TReference {
    private:
        friend class TBitMapOps<TTraits>;

        TChunk* Chunk;
        size_t Offset;

        TReference(TChunk* c, size_t offset)
            : Chunk(c)
            , Offset(offset)
        {
        }

    public:
        ~TReference() = default;

        Y_FORCE_INLINE TReference& operator=(bool val) {
            if (val)
                *Chunk |= static_cast<TChunk>(1) << Offset;
            else
                *Chunk &= ~(static_cast<TChunk>(1) << Offset);

            return *this;
        }

        Y_FORCE_INLINE TReference& operator=(const TReference& ref) {
            if (ref)
                *Chunk |= static_cast<TChunk>(1) << Offset;
            else
                *Chunk &= ~(static_cast<TChunk>(1) << Offset);

            return *this;
        }

        Y_FORCE_INLINE bool operator~() const {
            return 0 == (*Chunk & (static_cast<TChunk>(1) << Offset));
        }

        Y_FORCE_INLINE operator bool() const {
            return 0 != (*Chunk & (static_cast<TChunk>(1) << Offset));
        }

        Y_FORCE_INLINE TReference& Flip() {
            *Chunk ^= static_cast<TChunk>(1) << Offset;
            return *this;
        }
    };

private:
    struct TSetOp {
        static constexpr TChunk Op(const TChunk src, const TChunk mask) noexcept {
            return src | mask;
        }
    };

    struct TResetOp {
        static constexpr TChunk Op(const TChunk src, const TChunk mask) noexcept {
            return src & ~mask;
        }
    };

    template <class TUpdateOp>
    void UpdateRange(size_t start, size_t end) {
        const size_t startChunk = start >> DivCount;
        const size_t startBitOffset = start & ModMask;

        const size_t endChunk = end >> DivCount;
        const size_t endBitOffset = end & ModMask;

        size_t bitOffset = startBitOffset;
        for (size_t chunk = startChunk; chunk <= endChunk; ++chunk) {
            TChunk updateMask = FullChunk << bitOffset;
            if (chunk == endChunk) {
                updateMask ^= FullChunk << endBitOffset;
                if (!updateMask)
                    break;
            }
            Mask.Data[chunk] = TUpdateOp::Op(Mask.Data[chunk], updateMask);
            bitOffset = 0;
        }
    }

public:
    TBitMapOps() = default;

    TBitMapOps(TChunk val) {
        Mask.Data[0] = val;
        Mask.Sanitize();
    }

    TBitMapOps(const TThis&) = default;

    template <class T>
    TBitMapOps(const TBitMapOps<T>& bitmap)
        : Mask(bitmap.Mask.Data, bitmap.Mask.GetChunkCapacity())
    {
        Mask.Sanitize();
    }

    template <class T>
    Y_FORCE_INLINE bool operator==(const TBitMapOps<T>& bitmap) const {
        return Equal(bitmap);
    }

    Y_FORCE_INLINE TThis& operator=(const TThis& bitmap) {
        if (this != &bitmap) {
            TThis bm(bitmap);
            Swap(bm);
        }
        return *this;
    }

    template <class T>
    Y_FORCE_INLINE TThis& operator=(const TBitMapOps<T>& bitmap) {
        TThis bm(bitmap);
        Swap(bm);
        return *this;
    }

    template <class T>
    Y_FORCE_INLINE TThis& operator&=(const TBitMapOps<T>& bitmap) {
        return And(bitmap);
    }

    Y_FORCE_INLINE TThis& operator&=(const TChunk& val) {
        return And(val);
    }

    template <class T>
    Y_FORCE_INLINE TThis& operator|=(const TBitMapOps<T>& bitmap) {
        return Or(bitmap);
    }

    Y_FORCE_INLINE TThis& operator|=(const TChunk& val) {
        return Or(val);
    }

    template <class T>
    Y_FORCE_INLINE TThis& operator^=(const TBitMapOps<T>& bitmap) {
        return Xor(bitmap);
    }

    Y_FORCE_INLINE TThis& operator^=(const TChunk& val) {
        return Xor(val);
    }

    template <class T>
    Y_FORCE_INLINE TThis& operator-=(const TBitMapOps<T>& bitmap) {
        return SetDifference(bitmap);
    }

    Y_FORCE_INLINE TThis& operator-=(const TChunk& val) {
        return SetDifference(val);
    }

    Y_FORCE_INLINE TThis& operator<<=(size_t pos) {
        return LShift(pos);
    }

    Y_FORCE_INLINE TThis& operator>>=(size_t pos) {
        return RShift(pos);
    }

    Y_FORCE_INLINE TThis operator<<(size_t pos) const {
        return TThis(*this).LShift(pos);
    }

    Y_FORCE_INLINE TThis operator>>(size_t pos) const {
        return TThis(*this).RShift(pos);
    }

    Y_FORCE_INLINE bool operator[](size_t pos) const {
        return Get(pos);
    }

    Y_FORCE_INLINE TReference operator[](size_t pos) {
        const bool fitStorage = Mask.ExpandBitSize(pos + 1);
        Y_ASSERT(fitStorage);
        return TReference(&Mask.Data[pos >> DivCount], ModMask & pos);
    }

    Y_FORCE_INLINE void Swap(TThis& bitmap) {
        DoSwap(Mask, bitmap.Mask);
    }

    Y_FORCE_INLINE TThis& Set(size_t pos) {
        const bool fitStorage = Mask.ExpandBitSize(pos + 1);
        Y_ASSERT(fitStorage);
        Mask.Data[pos >> DivCount] |= static_cast<TChunk>(1) << (pos & ModMask);
        return *this;
    }

    // Fills the specified [start, end) bit range by the 1. Other bits are kept unchanged
    TThis& Set(size_t start, size_t end) {
        Y_ASSERT(start <= end);
        if (start < end) {
            Reserve(end);
            UpdateRange<TSetOp>(start, end);
        }
        return *this;
    }

    Y_FORCE_INLINE TThis& Reset(size_t pos) {
        if ((pos >> DivCount) < Mask.GetChunkCapacity()) {
            Mask.Data[pos >> DivCount] &= ~(static_cast<TChunk>(1) << (pos & ModMask));
        }
        return *this;
    }

    // Clears the specified [start, end) bit range. Other bits are kept unchanged
    TThis& Reset(size_t start, size_t end) {
        Y_ASSERT(start <= end);
        if (start < end && (start >> DivCount) < Mask.GetChunkCapacity()) {
            UpdateRange<TResetOp>(start, Min(end, Mask.GetBitCapacity()));
        }
        return *this;
    }

    Y_FORCE_INLINE TThis& Flip(size_t pos) {
        const bool fitStorage = Mask.ExpandBitSize(pos + 1);
        Y_ASSERT(fitStorage);
        Mask.Data[pos >> DivCount] ^= static_cast<TChunk>(1) << (pos & ModMask);
        return *this;
    }

    Y_FORCE_INLINE bool Get(size_t pos) const {
        if ((pos >> DivCount) < Mask.GetChunkCapacity()) {
            return Mask.Data[pos >> DivCount] & (static_cast<TChunk>(1) << (pos & ModMask));
        }
        return false;
    }

    template <class TTo>
    void Export(size_t pos, TTo& to) const {
        static_assert(std::is_unsigned<TTo>::value, "expect std::is_unsigned<TTo>::value");
        to = 0;
        size_t chunkpos = pos >> DivCount;
        if (chunkpos >= Mask.GetChunkCapacity())
            return;
        if ((pos & ModMask) == 0) {
            if (sizeof(TChunk) >= sizeof(TTo))
                to = (TTo)Mask.Data[chunkpos];
            else //if (sizeof(TChunk) < sizeof(TTo))
                NBitMapPrivate::CopyData(&to, 1, Mask.Data + chunkpos, Min(((sizeof(TTo) * 8) >> DivCount), Mask.GetChunkCapacity() - chunkpos));
        } else if ((pos & (sizeof(TTo) * 8 - 1)) == 0 && sizeof(TChunk) >= 2 * sizeof(TTo))
            to = (TTo)(Mask.Data[chunkpos] >> (pos & ModMask));
        else {
            static constexpr size_t copyToSize = (sizeof(TChunk) >= sizeof(TTo)) ? (sizeof(TChunk) / sizeof(TTo)) + 2 : 3;
            TTo temp[copyToSize] = {0, 0};
            //or use non defined by now TBitmap<copyToSize, TTo>::CopyData,RShift(pos & ModMask),Export(0,to)
            NBitMapPrivate::CopyData(temp, copyToSize, Mask.Data + chunkpos, Min((sizeof(TTo) / sizeof(TChunk)) + 1, Mask.GetChunkCapacity() - chunkpos));
            to = (temp[0] >> (pos & ModMask)) | (temp[1] << (8 * sizeof(TTo) - (pos & ModMask)));
        }
    }

    Y_FORCE_INLINE bool Test(size_t n) const {
        return Get(n);
    }

    Y_FORCE_INLINE TThis& Push(bool val) {
        LShift(1);
        return val ? Set(0) : *this;
    }

    Y_FORCE_INLINE bool Pop() {
        bool val = Get(0);
        return RShift(1), val;
    }

    // Clear entire bitmap. Current capacity is kept unchanged
    Y_FORCE_INLINE TThis& Clear() {
        for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) {
            Mask.Data[i] = 0;
        }
        return *this;
    }

    // Returns bits capacity
    Y_FORCE_INLINE constexpr size_t Size() const noexcept {
        return Mask.GetBitCapacity();
    }

    Y_FORCE_INLINE void Reserve(size_t bitCount) {
        Y_VERIFY(Mask.ExpandBitSize(bitCount), "Exceeding bitmap storage capacity");
    }

    Y_FORCE_INLINE size_t ValueBitCount() const {
        size_t nonZeroChunk = Mask.GetChunkCapacity() - 1;
        while (nonZeroChunk != 0 && !Mask.Data[nonZeroChunk])
            --nonZeroChunk;
        return nonZeroChunk || Mask.Data[nonZeroChunk]
                   ? nonZeroChunk * BitsPerChunk + GetValueBitCount(TIntType(Mask.Data[nonZeroChunk]))
                   : 0;
    }

    Y_PURE_FUNCTION Y_FORCE_INLINE bool Empty() const {
        for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i)
            if (Mask.Data[i])
                return false;
        return true;
    }

    bool HasAny(const TThis& bitmap) const {
        for (size_t i = 0; i < Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); ++i) {
            if (0 != (Mask.Data[i] & bitmap.Mask.Data[i])) {
                return true;
            }
        }
        return false;
    }

    template <class T>
    Y_FORCE_INLINE bool HasAny(const TBitMapOps<T>& bitmap) const {
        return HasAny(TThis(bitmap));
    }

    Y_FORCE_INLINE bool HasAny(const TChunk& val) const {
        return 0 != (Mask.Data[0] & val);
    }

    bool HasAll(const TThis& bitmap) const {
        for (size_t i = 0; i < Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); ++i) {
            if (bitmap.Mask.Data[i] != (Mask.Data[i] & bitmap.Mask.Data[i])) {
                return false;
            }
        }
        for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) {
            if (bitmap.Mask.Data[i] != 0) {
                return false;
            }
        }
        return true;
    }

    template <class T>
    Y_FORCE_INLINE bool HasAll(const TBitMapOps<T>& bitmap) const {
        return HasAll(TThis(bitmap));
    }

    Y_FORCE_INLINE bool HasAll(const TChunk& val) const {
        return (Mask.Data[0] & val) == val;
    }

    TThis& And(const TThis& bitmap) {
        // Don't expand capacity here, because resulting bits in positions,
        // which are greater then size of one of these bitmaps, will be zero
        for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i)
            Mask.Data[i] &= bitmap.Mask.Data[i];
        // Clear bits if current bitmap size is greater than AND-ed one
        for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i)
            Mask.Data[i] = 0;
        return *this;
    }

    template <class T>
    Y_FORCE_INLINE TThis& And(const TBitMapOps<T>& bitmap) {
        return And(TThis(bitmap));
    }

    Y_FORCE_INLINE TThis& And(const TChunk& val) {
        Mask.Data[0] &= val;
        for (size_t i = 1; i < Mask.GetChunkCapacity(); ++i)
            Mask.Data[i] = 0;
        return *this;
    }

    TThis& Or(const TThis& bitmap) {
        const size_t valueBitCount = bitmap.ValueBitCount();
        if (valueBitCount) {
            // Memory optimization: expand size only for non-zero bits
            Reserve(valueBitCount);
            for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i)
                Mask.Data[i] |= bitmap.Mask.Data[i];
        }
        return *this;
    }

    template <class T>
    Y_FORCE_INLINE TThis& Or(const TBitMapOps<T>& bitmap) {
        return Or(TThis(bitmap));
    }

    Y_FORCE_INLINE TThis& Or(const TChunk& val) {
        Mask.Data[0] |= val;
        Mask.Sanitize();
        return *this;
    }

    TThis& Xor(const TThis& bitmap) {
        Reserve(bitmap.Size());
        for (size_t i = 0; i < bitmap.Mask.GetChunkCapacity(); ++i)
            Mask.Data[i] ^= bitmap.Mask.Data[i];
        return *this;
    }

    template <class T>
    Y_FORCE_INLINE TThis& Xor(const TBitMapOps<T>& bitmap) {
        return Xor(TThis(bitmap));
    }

    Y_FORCE_INLINE TThis& Xor(const TChunk& val) {
        Mask.Data[0] ^= val;
        Mask.Sanitize();
        return *this;
    }

    TThis& SetDifference(const TThis& bitmap) {
        for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i)
            Mask.Data[i] &= ~bitmap.Mask.Data[i];
        return *this;
    }

    template <class T>
    Y_FORCE_INLINE TThis& SetDifference(const TBitMapOps<T>& bitmap) {
        return SetDifference(TThis(bitmap));
    }

    Y_FORCE_INLINE TThis& SetDifference(const TChunk& val) {
        Mask.Data[0] &= ~val;
        return *this;
    }

    Y_FORCE_INLINE TThis& Flip() {
        for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i)
            Mask.Data[i] = ~Mask.Data[i];
        Mask.Sanitize();
        return *this;
    }

    TThis& LShift(size_t shift) {
        if (shift != 0) {
            const size_t valueBitCount = ValueBitCount();
            // Do nothing for empty bitmap
            if (valueBitCount != 0) {
                const size_t eshift = shift / BitsPerChunk;
                const size_t offset = shift % BitsPerChunk;
                const size_t subOffset = BitsPerChunk - offset;

                // Don't verify expand result, so l-shift of fixed bitmap will work in the same way as for unsigned integer.
                Mask.ExpandBitSize(valueBitCount + shift);

                if (offset == 0) {
                    for (size_t i = Mask.GetChunkCapacity() - 1; i >= eshift; --i) {
                        Mask.Data[i] = Mask.Data[i - eshift];
                    }
                } else {
                    for (size_t i = Mask.GetChunkCapacity() - 1; i > eshift; --i)
                        Mask.Data[i] = (Mask.Data[i - eshift] << offset) | (Mask.Data[i - eshift - 1] >> subOffset);
                    if (eshift < Mask.GetChunkCapacity())
                        Mask.Data[eshift] = Mask.Data[0] << offset;
                }
                for (size_t i = 0; i < Min(eshift, Mask.GetChunkCapacity()); ++i)
                    Mask.Data[i] = 0;

                // Cleanup extra high bits in the storage
                Mask.Sanitize();
            }
        }
        return *this;
    }

    TThis& RShift(size_t shift) {
        if (shift != 0) {
            const size_t eshift = shift / BitsPerChunk;
            const size_t offset = shift % BitsPerChunk;
            if (eshift >= Mask.GetChunkCapacity()) {
                Clear();

            } else {
                const size_t limit = Mask.GetChunkCapacity() - eshift - 1;

                if (offset == 0) {
                    for (size_t i = 0; i <= limit; ++i) {
                        Mask.Data[i] = Mask.Data[i + eshift];
                    }
                } else {
                    const size_t subOffset = BitsPerChunk - offset;
                    for (size_t i = 0; i < limit; ++i)
                        Mask.Data[i] = (Mask.Data[i + eshift] >> offset) | (Mask.Data[i + eshift + 1] << subOffset);
                    Mask.Data[limit] = Mask.Data[Mask.GetChunkCapacity() - 1] >> offset;
                }

                for (size_t i = limit + 1; i < Mask.GetChunkCapacity(); ++i)
                    Mask.Data[i] = 0;
            }
        }
        return *this;
    }

    // Applies bitmap at the specified offset using OR operator.
    // This method is optimized combination of Or() and LShift(), which allows reducing memory allocation
    // when combining long dynamic bitmaps.
    TThis& Or(const TThis& bitmap, size_t offset) {
        if (0 == offset)
            return Or(bitmap);

        const size_t otherValueBitCount = bitmap.ValueBitCount();
        // Continue only if OR-ed bitmap have non-zero bits
        if (otherValueBitCount) {
            const size_t chunkShift = offset / BitsPerChunk;
            const size_t subShift = offset % BitsPerChunk;
            const size_t subOffset = BitsPerChunk - subShift;

            Reserve(otherValueBitCount + offset);

            if (subShift == 0) {
                for (size_t i = chunkShift; i < Min(bitmap.Mask.GetChunkCapacity() + chunkShift, Mask.GetChunkCapacity()); ++i) {
                    Mask.Data[i] |= bitmap.Mask.Data[i - chunkShift];
                }
            } else {
                Mask.Data[chunkShift] |= bitmap.Mask.Data[0] << subShift;
                size_t i = chunkShift + 1;
                for (; i < Min(bitmap.Mask.GetChunkCapacity() + chunkShift, Mask.GetChunkCapacity()); ++i) {
                    Mask.Data[i] |= (bitmap.Mask.Data[i - chunkShift] << subShift) | (bitmap.Mask.Data[i - chunkShift - 1] >> subOffset);
                }
                if (i < Mask.GetChunkCapacity())
                    Mask.Data[i] |= bitmap.Mask.Data[i - chunkShift - 1] >> subOffset;
            }
        }

        return *this;
    }

    bool Equal(const TThis& bitmap) const {
        if (Mask.GetChunkCapacity() > bitmap.Mask.GetChunkCapacity()) {
            for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) {
                if (0 != Mask.Data[i])
                    return false;
            }
        } else if (Mask.GetChunkCapacity() < bitmap.Mask.GetChunkCapacity()) {
            for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) {
                if (0 != bitmap.Mask.Data[i])
                    return false;
            }
        }
        size_t size = Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity());
        for (size_t i = 0; i < size; ++i) {
            if (Mask.Data[i] != bitmap.Mask.Data[i])
                return false;
        }
        return true;
    }

    template <class T>
    Y_FORCE_INLINE bool Equal(const TBitMapOps<T>& bitmap) const {
        return Equal(TThis(bitmap));
    }

    int Compare(const TThis& bitmap) const {
        size_t size = Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity());
        int res = ::memcmp(Mask.Data, bitmap.Mask.Data, size * sizeof(TChunk));
        if (0 != res || Mask.GetChunkCapacity() == bitmap.Mask.GetChunkCapacity())
            return res;

        if (Mask.GetChunkCapacity() > bitmap.Mask.GetChunkCapacity()) {
            for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) {
                if (0 != Mask.Data[i])
                    return 1;
            }
        } else {
            for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) {
                if (0 != bitmap.Mask.Data[i])
                    return -1;
            }
        }
        return 0;
    }

    template <class T>
    Y_FORCE_INLINE int Compare(const TBitMapOps<T>& bitmap) const {
        return Compare(TThis(bitmap));
    }

    // For backward compatibility
    Y_FORCE_INLINE static int Compare(const TThis& l, const TThis& r) {
        return l.Compare(r);
    }

    size_t FirstNonZeroBit() const {
        for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) {
            if (Mask.Data[i]) {
                // CountTrailingZeroBits() expects unsigned types not smaller than unsigned int. So, convert before calling
                return BitsPerChunk * i + CountTrailingZeroBits(TIntType(Mask.Data[i]));
            }
        }
        return Size();
    }

    // Returns position of the next non-zero bit, which offset is greater than specified pos
    // Typical loop for iterating bits:
    // for (size_t pos = bits.FirstNonZeroBit(); pos != bits.Size(); pos = bits.NextNonZeroBit(pos)) {
    //     ...
    // }
    // See Y_FOR_EACH_BIT macro definition at the bottom
    size_t NextNonZeroBit(size_t pos) const {
        size_t i = (pos + 1) >> DivCount;
        if (i < Mask.GetChunkCapacity()) {
            const size_t offset = (pos + 1) & ModMask;
            // Process the current chunk
            if (offset) {
                // Zero already iterated trailing bits using mask
                const TChunk val = Mask.Data[i] & ((~TChunk(0)) << offset);
                if (val) {
                    return BitsPerChunk * i + CountTrailingZeroBits(TIntType(val));
                }
                // Continue with other chunks
                ++i;
            }

            for (; i < Mask.GetChunkCapacity(); ++i) {
                if (Mask.Data[i]) {
                    return BitsPerChunk * i + CountTrailingZeroBits(TIntType(Mask.Data[i]));
                }
            }
        }
        return Size();
    }

    Y_FORCE_INLINE size_t Count() const {
        size_t count = 0;
        for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i)
            count += ::NBitMapPrivate::CountBitsPrivate(Mask.Data[i]);
        return count;
    }

    void Save(IOutputStream* out) const {
        ::Save(out, ui8(sizeof(TChunk)));
        ::Save(out, ui64(Size()));
        ::SavePodArray(out, Mask.Data, Mask.GetChunkCapacity());
    }

    void Load(IInputStream* inp) {
        ui8 chunkSize = 0;
        ::Load(inp, chunkSize);
        Y_VERIFY(size_t(chunkSize) == sizeof(TChunk), "Chunk size is not the same");

        ui64 bitCount64 = 0;
        ::Load(inp, bitCount64);
        size_t bitCount = size_t(bitCount64);
        Reserve(bitCount);

        size_t chunkCount = 0;
        if (bitCount > 0) {
            chunkCount = ((bitCount - 1) >> DivCount) + 1;
            ::LoadPodArray(inp, Mask.Data, chunkCount);
        }

        if (chunkCount < Mask.GetChunkCapacity()) {
            ::memset(Mask.Data + chunkCount, 0, (Mask.GetChunkCapacity() - chunkCount) * sizeof(TChunk));
        }
        Mask.Sanitize();
    }

    inline size_t Hash() const {
        THash<TChunk> chunkHasher;

        size_t hash = chunkHasher(0);
        bool tailSkipped = false;
        for (size_t i = Mask.GetChunkCapacity(); i > 0; --i) {
            if (tailSkipped || Mask.Data[i - 1]) {
                hash = ::CombineHashes(hash, chunkHasher(Mask.Data[i - 1]));
                tailSkipped = true;
            }
        }

        return hash;
    }

    inline const TChunk* GetChunks() const {
        return Mask.Data;
    }

    constexpr size_t GetChunkCount() const noexcept {
        return Mask.GetChunkCapacity();
    }
};

template <class X, class Y>
inline TBitMapOps<X> operator&(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
    return TBitMapOps<X>(x).And(y);
}

template <class X>
inline TBitMapOps<X> operator&(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
    return TBitMapOps<X>(x).And(y);
}

template <class X>
inline TBitMapOps<X> operator&(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
    return TBitMapOps<X>(x).And(y);
}

template <class X, class Y>
inline TBitMapOps<X> operator|(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
    return TBitMapOps<X>(x).Or(y);
}

template <class X>
inline TBitMapOps<X> operator|(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
    return TBitMapOps<X>(x).Or(y);
}

template <class X>
inline TBitMapOps<X> operator|(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
    return TBitMapOps<X>(x).Or(y);
}

template <class X, class Y>
inline TBitMapOps<X> operator^(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
    return TBitMapOps<X>(x).Xor(y);
}

template <class X>
inline TBitMapOps<X> operator^(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
    return TBitMapOps<X>(x).Xor(y);
}

template <class X>
inline TBitMapOps<X> operator^(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
    return TBitMapOps<X>(x).Xor(y);
}

template <class X, class Y>
inline TBitMapOps<X> operator-(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
    return TBitMapOps<X>(x).SetDifference(y);
}

template <class X>
inline TBitMapOps<X> operator-(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
    return TBitMapOps<X>(x).SetDifference(y);
}

template <class X>
inline TBitMapOps<X> operator-(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
    return TBitMapOps<X>(x).SetDifference(y);
}

template <class X>
inline TBitMapOps<X> operator~(const TBitMapOps<X>& x) {
    return TBitMapOps<X>(x).Flip();
}

/////////////////// Specialization ///////////////////////////

template <size_t BitCount, typename TChunkType /*= ui64*/>
class TBitMap: public TBitMapOps<TFixedBitMapTraits<BitCount, TChunkType>> {
private:
    using TBase = TBitMapOps<TFixedBitMapTraits<BitCount, TChunkType>>;

public:
    TBitMap()
        : TBase()
    {
    }

    TBitMap(typename TBase::TChunk val)
        : TBase(val)
    {
    }

    TBitMap(const TBitMap&) = default;
    TBitMap& operator=(const TBitMap&) = default;

    template <class T>
    TBitMap(const TBitMapOps<T>& bitmap)
        : TBase(bitmap)
    {
    }
};

using TDynBitMap = TBitMapOps<TDynamicBitMapTraits<ui64>>;

#define Y_FOR_EACH_BIT(var, bitmap) for (size_t var = (bitmap).FirstNonZeroBit(); var != (bitmap).Size(); var = (bitmap).NextNonZeroBit(var))

template <typename TTraits>
struct THash<TBitMapOps<TTraits>> {
    size_t operator()(const TBitMapOps<TTraits>& elem) const {
        return elem.Hash();
    }
};