Package: bitvec
BitVecTools written for python for fast and simple bit manipulation
It is:Simpler to manipulate bits than with native python intsAble to correctly wrap numbers in arithmetic operationsEmulate behavior of unsigned, signed integersUnit-testedWritten in Rust (Some operations are quite fast)Installationpip install bitvecUnittestspython -m unittest python\tests\tests.pyPyPihttps://pypi.org/project/bitvec/Example use:frombitvecimportBinaryfrombitvecimportarithm# Creating new number. Size is inferred from leading zerosx=Binary('0110')y=Binary(1,lenght=9)# You can specify len tooprint(f"{x}, lenght:{len(x)}")# prints '0110, lenght: 4'print(int(~(x+y)))# prints 504 (0b111111000)# Arithmetic correctly wraps and you can check the status# overflowing_add return the wrapped sum and boolen to indicate if overflow occursprint(arithm.overflowing_add(x,'1100'))# (0010, True)# Set first bit to highx[0]=True# slice bitsprint(x[:3])# ('111')# sets first 3 bits to '001'x[:3]="001"# convert to hexprint(x.hex())# '0x1'# every second bit of '0001'print(x[::2])# '01'    ^ ^# you can quickly iterate over bits, bytes or any other chunk of bits with .iter(size) functionforiinx.iter(2):print("Chunk of two bits: ",i)# i is `01` then `00`# count all zerosprint(x.count_zeros())# prints 3# find indexes of all zerosprint(x.find_all('0'))# prints [1, 2, 3]Brief docsBinaryClass that represent numbers in binary. Wraps arithmetic to bouds of the binary number,
allows for quick and easy bit manipulation.Parametersobject - Any object that can be somehow converted to binary number.
Including its representation as string, int, boolean, list of boolean-convertable values, byte-arrays, numpy arrays ect.lenght - Target lenght of the number in bits. This number can be inferred from object based on its value, extra zeros ectbytes_lenght - Target lenght of the number in bytes. Same as lenght but in bytes.sign_behavior - How number should implement sign.Examples>>>frombitvecimportBinary>>>Binary("0110")# From string representing binary number. Following zeros are used to inherit len of number.'0110'>>>Binary(4,lenght=8)# Crate number with 8 bits, 4 is converted to binary and padded with zeros.'00000100'>>>Binary(255)'11111111'>>>Binary([True,0,1,1.0])# From array of boolean-likes'1011'>>>Binary("0000 0001")# Ignores whitespace'00000001'>>>Binary("ff Aa C   C")# Works with hex too'11111111 10101010 11001100'AliasModule defines factories with predefined sizes and behaviors like u8, u16, i16, i64 ect. These ones can be used as followed:>>>frombitvec.aliasimportu8>>>u8(3)'00000011'Conversion>>>num=Binary("FA")# 11111010To string (formatted binary)>>>str(num)# Returns just bits'11111010'>>>bin(num)'0b11111010'>>>num.bin()'0b11111010'>>>num.bin(prefix=False)# remove prefix'11111010'>>>hex(num)'0xfa'>>>num.hex()'0xfa'>>>num.hex(prefx=False)'fa'>>>int(num)250>>>num.int()250To boolean (Falsewhen0)>>>bool(num)TrueIndexing and AccessIndexBits of the number can be accessed throught index:>>>num=Binary("FA")# 11111010>>>num[0],num[1],num[2]# First 3 bits of the number(False,True,False)>>>num[-1]# Last bitTrueSlice>>>num[:3]# from start to 3th bit (first 3 bits)'010'>>>num[-6:]# from 6th bit from the end to end (last 6 bits)'111110'>>>num[-6:-2]# From 6th bit from end to 2st bit from end'1110'>>>num[2:]# Skip first 2 bits'111110'>>>num[:16]# First 16 bits (padded with zeros)'00000000 11111010'>>>num[::2]# Every other bit'1100'>>>num[::-1]# Reversed'01011111'>>>num[[1,3]]# Get second and 4th bit'11'NOTEthat behavior of slicing is slighty diffrent from slicing pythonsstror list, first bit is from far right, not left. You can also exeed len of the value, in this case added bits will be padded with sign extending bit (0for unsigned,sign_bitfor singed)Note that slicing makes a copy of the vectorPublic MethodsAliases for slicing numberhigh_byte()- second 8bits (from 8th to 16th)low_byte()- first 8bits (from start to 8th)extended_low()- first 16bitsextended_high()- second 16bitsget_byte(index: int)- nth group of 8-bit chunks.split_at(index: int)- split number at nth bit.Information about numbersign_behavior()- The way that number behaves on extending and converstionssignedorunsignedmaximal_value()- Maximal possibe value than can be hold in this representation and lenghtminimal_value()- Minimal possibe value than can be hold in this representation and lenghtis_negative()- Returns if number is negativesign_extending_bit()- Returns the boolean that will be used to extend this value to lefthex(prefix: bool=True)- hex representation of this numberbin(prefix: bool=True)- bin representation of this numberint()- casted to python integerSearching & Finding Patternsleading_zeros()- Amount of leading zeros in the numbertrailing_zeros()- Amount of trailing zeros in the numberleading_ones()- Amount of leading ones in the numbertrailing_ones()- Amount of trailing ones in the numberfind(sub: bool|int|str|Binary)- Find first occurence (index) of the patternfind_all(sub: bool|int|str|Binary)- Find all occurences (index) of the patternfind_zeros()- Find indexes of zerosfind_ones()- Find indexes of onescount_zeros()- count how many zeros is in numbercount_ones()- count how many ones is in numberModifyingappend(Binary|bool|str|int)- Appends value to the endprepend(Binary|bool|str|int)- Appends vakue to the startjoin(Binary|bool|str|int)- Joins binary numbers like str.join doesIteratingbits()- iterates over bitsbytes()- iterates over bytesiter()- iterates over chunks or n bitsModify by IndexYou can modify bits with indexes:>>>num=Binary(0,lenght=8)>>>num[0]=True# Set first bit to high>>>num[1]=True# Set second bit to high>>>num'00000011'>>>num[-1]=True# Set last bit to high>>>num'10000011'>>>num[0]=0# Set first bit to low>>>num'10000010'Modify by SliceYou can select bits by slice and set them, or copy from other number>>>num=Binary(0,lenght=8)>>>num[:3]=True>>>num'00000111'>>>num[:3]="101"# Insert 101 in place of first 3 bits of the number>>>num'00000101'>>>num[5:8]="111"# insert 111 in place of bits 5th 6th 7th>>>num'11100101'>>>num[:]=0# Set all bits to 0>>>num'00000000'Modify by indeces>>>num=Binary(0,lenght=8)>>>num[[1,2,3]]=True# Set first, second and third bit to True>>>num>>>'0000 1110'There is few rulesIf right side of the slice is convertable to Binary, it will be converted to binary and inserted in place of the selected bits.If size of the converted value will be greater than size of the selected bits, it will throw an error.If right side is boolean the assigment will set all selectet bits to this value.step value is not supported.Iterating over bits or chunksYou can iterate over bits of the number:>>>num=Binary("FA")# 11111010>>>forbitinnum:# or num.bits()...print(bit,end=" ")# bit is Binary class with lenght equal to 101011111Or over bytes:>>>num=Binary("FA")# 11111010>>>list(num.extended_low().bytes())[11111010,00000000]Or over any other chunk of bits withiterfunction. For more sophisticated iteration you can useitertoolsor just play with indexing and slicing.ArithmIn modulearithmthere are functions that can be used to perform arithmetic/logical operations on binary numbers. That's includesaddflaged_add(a: Binary, b: Any) -> Tuple[Binary, Flags]:- add, and return with flags (of, zf, sf)overflowing_add(a: Binary, b: Any) -> Tuple[Binary, bool]:- add and return overflow flag toowrapping_add(a: Binary, b: Any) -> Binary:- add and return only resultsubflaged_sub(a: Binary, b: Any) -> Tuple[Binary, Flags]:- sub, and return with flags (of, zf, sf)overflowing_sub(a: Binary, b: Any) -> Tuple[Binary, bool]:- sub and return overflow flag toowrapping_sub(a: Binary, b: Any) -> Binary:- sub and return only resultconvertingcast(binary: Binary, to_type: str) -> Binary:- change sb without any checks ('transmutate' value)convert(binary: Binary, to_type: str) -> Binary:- change sb and rise if conversion is impossibleextend_to_signed(binary: Binary) -> Binary:- change to sb and add sign bit if nesseserypad_zeros(binary: Binary, size: int) -> Binary:- add zeros to match the lenghtpad_ones(binary: Binary, size: int) -> Binary:- add ones to match the lenghtpad_sign_extend(binary: Binary, size: int) -> Binary:- add sign extending bits to match the lenghtconcat(*args: Any) -> Binary:- concats args into one long binaryNegarithmetic_neg(binary: Binary) -> Binary:- negates number with 2's logicbitwise_neg(binary: Binary) -> Binary:- flips bitsbitwisebitwise_or(binary: Binary, b: Any) -> Binary:bitwise_and(binary: Binary, b: Any) -> Binary:bitwise_xor(binary: Binary, b: Any) -> Binary:bitwise_xnor(binary: Binary, b: Any) -> Binary:bitwise_nand(binary: Binary, b: Any) -> Binary:bitwise_nor(binary: Binary, b: Any) -> Binary:bitwise_map(*args: Any, map: Binary|str|int|dict[Binary|str|int, bool]) -> Binary:- maps bits by sum-of-products tablemultiplymultiply(binary: Binary, b: Any) -> Binary:- mul and returns whole resultoverflowing_mul(binary: Binary, b: Any) -> Tuple[Binary, Binary]:- mul and returns splited resultwrapping_mul(binary: Binary, b: Any) -> Binary:- mul and returns onlylen(binary)first bitsshiftsoverflowing_lsh(a: Binary, b: Any) -> Tuple[Binary, Binary]:- left shifts, returns result and shifted out bitswrapping_lsh(a: Binary, b: Any) -> Binary:- left shifts, returns result and discards shifted out bitslogical_underflowing_rsh(a: Binary, b: Any) -> Tuple[Binary, Binary]:- right shift padded with zeros, returns result and shifted out bitslogical_wrapping_rsh(a: Binary, b: Any) -> Binary:- right shift padded with zeros, returns result and discards shifted out bitsarithmetic_underflowing_rsh(a: Binary, b: Any) -> Tuple[Binary, Binary]:- right shift paded sign extending bits, returns result and shifted out bitsarithmetic_wrapping_rsh(a: Binary, b: Any) -> Binary:- right shift paded sign extending bits, returns result and discards shifted out bits>>>frombitvecimportarithm>>>arithm.wrapping_add(u4("1111"),"0001")'0000'# 15 + 1 = 16, but we have only 4 bits, so we wrap around and get 0>>>arithm.overflowing_add(u4("1111"),"0001")('0000',True)# If you want to know if there was overflow, use overflowing_add>>>arithm.overflowing_lsh(u8('0100 1010'),2)('00101000','01')# 0100 1010 << 2 = 0010 1000, but we have only 8 bits, so we wrap around to get 0010 1000, and we have 2 'wrapped' bits from left, so we return them as second valueSome functions are implemented for Binary class (usually wrapping ones), so you can use them directly with operators number:>>>u8("0100 1010")+u8("0000 0010")'0100 1100'That includes:__add__,__sub__,__mul__,__lshift__,__rshift__,__and__,__or__,__xor__,__invert__,__neg__Creating numbers - detailsIf you didn't specify lenght, it will be calculated from valueFor string it will be lenght of string including leading zeros/onesFor int it will be minimal lenght of binary representation of the numberIf you specify lenght, it will be used but if value is longer, it will raise an errorThis module assumes that forsignednumbers the most significant bit is sign bit. So for signed number with lenght of1(so we haveonlysign_bit that has weight equal to-1) possible values will be0and-1Numbers with lenght of0will be always treated as0Examples>>>Binary(0)# lenght will be 0, displayed as ''>>>Binary(0,lenght=1)# lenght will be 1, displayed as '0'>>>Binary(0,lenght=2)# lenght will be 2, displayed as '00'>>>Binary(0,signed=True)# lenght will be 0, displayed as '0'>>>Binary(0,signed=True,lenght=1)# lenght will be 1, displayed as '0'>>>Binary(-1,signed=True,lenght=1)# lenght will be 1, displayed as '1'>>>Binary(-1,signed=True,lenght=2)# lenght will be 2, displayed as '11'>>>Binary(1,signed=True)# lenght will be 2, displayed as '01'This behavior is based on the interpretation of the number as a signed integer where sign bit is treated as it has negative weightfor unsigned:

0 0 0 0
 \ \ \ \_ weight 1
  \ \ \__ weight 2
   \ \___ weight 4
    \____ weight 8
Value = sum of weights of bits set to 1for signed:
    
0 0 0 0
 \ \ \ \_ weight  1
  \ \ \__ weight  2
   \ \___ weight  4
    \____ weight -8
Value = sum of weights of bits set to 1for size 1
unsigned:
0
 \_ weight 1
signed:
0
 \_ weight -1 (last bit)And zero-lenght number just fills up the pattern well.