bitmap.py 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. from typing import *
  2. from functools import reduce
  3. from sccd.util.debug import *
  4. if DEBUG:
  5. # Bitmap inherits 'int' and is therefore immutable.
  6. # Methods that return a Bitmap return a new bitmap and leave the arguments untouched.
  7. # To change a bitmap, use an assignment operator ('=', '|=', '&=', ...)
  8. class Bitmap(int):
  9. # Create from int
  10. def __new__(cls, value=0, *args, **kwargs):
  11. return super(cls, cls).__new__(cls, value)
  12. def __repr__(self):
  13. return "Bitmap('"+format(self, 'b')+"')"
  14. def __str__(self):
  15. return self.__repr__()
  16. def __or__(self, other):
  17. return self.__class__(super().__or__(other))
  18. def __and__(self, other):
  19. return self.__class__(super().__and__(other))
  20. def __invert__(self):
  21. return self.__class__(super().__invert__())
  22. def __neg__(self):
  23. return self.__class__(super().__neg__())
  24. else:
  25. Bitmap = int # Much faster than our overrides! Only thing we miss out on is pretty printing as a binary string
  26. # Create a bitmap with a single bit set.
  27. def bit(pos):
  28. return Bitmap(1 << pos)
  29. # The following functions are not methods of Bitmap so that they also work on integers:
  30. # Non-member function variants so they also work on integers:
  31. def bm_from_list(iterable):
  32. v = reduce(lambda x,y: x|1<<y, iterable, 0)
  33. return Bitmap(v)
  34. def bm_union(iterable):
  35. return reduce(lambda x,y: x|y, iterable, 0)
  36. def bm_has(self, pos):
  37. return self & 1 << pos
  38. def bm_has_all(self, bitmap):
  39. return (self | bitmap) == self
  40. def bm_lowest_bit(self) -> int:
  41. low = self & -self # only the lowest bit set
  42. pos = -1
  43. while low:
  44. low >>= 1
  45. pos += 1
  46. return pos
  47. def bm_highest_bit(self) -> int:
  48. pos = -1
  49. while self:
  50. self >>= 1
  51. pos += 1
  52. return pos
  53. def bm_items(self) -> Iterable[int]:
  54. pos = 0
  55. while self > 0:
  56. if (self >> 1) << 1 != self:
  57. yield pos
  58. pos += 1
  59. self >>= 1
  60. # Takes 2 iterations over our bitmap, one for highest_bit,
  61. # and then to find the rest of the bits.
  62. def bm_reverse_items(self) -> Iterable[int]:
  63. pos = bm_highest_bit(self)
  64. if pos >= 0:
  65. yield pos
  66. pos -= 1
  67. while pos >= 0:
  68. high = 1 << pos
  69. if self & high:
  70. yield pos
  71. self -= high # unset high bit
  72. pos -= 1