rfc1982_serial_number.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/env python3
  2. """Implementation of RFC 1982 Serial Number Arithmetic.
  3. Run as a program, this takes start and finish serial values and tells
  4. you what intermediate steps (if any) are needed to make the specified
  5. change.
  6. Used as a module, this implements the Serial class and an iterator
  7. which returns the intermediate steps (if any) needed between two
  8. Serial values.
  9. """
  10. class Serial:
  11. """Implementation of RFC 1982 Serial Number Arithmetic.
  12. Per the RFC, only two operations are defined on Serial objects:
  13. addition and comparision, both within a restricted range specified
  14. by the RFC.
  15. The default modulus is 2**32, you can change this by subclassing
  16. the Serial class and overriding the subclass's modulus variable.
  17. The modulus must be a power of two.
  18. See RFC 1982 for discussion of the ways in which Serial numbers do
  19. not work like normal integers. In particular, note that there's a
  20. corner case in which one can have a pair of Serial numbers I1 and
  21. I2 where I1 is neither equal to, less than, nor greater than I2.
  22. This is deliberate and is not a bug in the code. See the RFC.
  23. """
  24. modulus = 2 ** 32
  25. def __init__(self, val):
  26. self._val = int(val)
  27. if self._val < 0 or self._val >= self.modulus:
  28. raise ValueError
  29. def __add__(self, val):
  30. val = int(val)
  31. if val < 0 or val > (self.modulus - 1) >> 1:
  32. raise ValueError
  33. return type(self)((self._val + val) & (self.modulus - 1))
  34. def __le__(self, other):
  35. return (self.modulus - int(self) + int(other)) & (self.modulus >> 1) == 0
  36. def __ge__(self, other):
  37. return (self.modulus + int(self) - int(other)) & (self.modulus >> 1) == 0
  38. def __eq__(self, other):
  39. return int(self) == int(other)
  40. def __ne__(self, other):
  41. return int(self) != int(other)
  42. def __lt__(self, other):
  43. return self != other and self <= other
  44. def __gt__(self, other):
  45. return self != other and self >= other
  46. def __int__(self):
  47. return self._val
  48. def __str__(self):
  49. return f"{int(self):{len(str(self.modulus))}d}"
  50. def find_intermediate(start, finish):
  51. """
  52. Find the sequence of intermediate values (if any) needed to step
  53. from start to finish
  54. """
  55. while not (start < finish): # sic: serial numbers are not (quite) integers
  56. start += ((Serial.modulus >> 1) - 1)
  57. yield start
  58. def main():
  59. from argparse import ArgumentParser
  60. ap = ArgumentParser(description = __doc__)
  61. ap.add_argument("start", type = Serial)
  62. ap.add_argument("finish", type = Serial)
  63. args = ap.parse_args()
  64. print(f"Start at {args.start!s}")
  65. if args.start < args.finish:
  66. print("No step needed")
  67. else:
  68. for wrap in find_intermediate(args.start, args.finish):
  69. print(f"Step via {wrap!s}")
  70. print(f"End at {args.finish!s}")
  71. if __name__ == "__main__":
  72. main()