text: # Copyright 2021 Hathor Labs
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """
  # Terms
  
  - u256: unsigned 256-bit integer
  - hash: 256-bit hash, same for Bitcoin and Hathor
  - target: 32-bit Bitcoin target
  - bdiff: Bitcoin difficulty (which truncates diff 1), used for Bitcoin fullnode related calculations
  - pdiff: Pool difficulty (which does not truncate diff 1), used for pool difficulty calculations
  - weight: Logarithmic (base 2) work, as used in Hathor
  
  # Conversions
  
  Primitive:
  
  - u256 -> hash
  - u256 -> target
  - u256 -> bdiff
  - u256 -> pdiff
  - u256 -> weight
  - hash -> u256
  - target -> u256
  - bdiff -> u256
  - pdiff -> u256
  - weight -> u256
  
  From the above conversions it is possible to make all converions between (hash, target, bdiff, pdiff, weight) using
  u256 as intermediary.
  
  # References
  
  - https://en.bitcoin.it/wiki/Difficulty
  - https://en.bitcoin.it/wiki/Target
  """
  
  from math import log2
  from typing import Union
  
  BDIFF_ONE = 0x00000000ffff0000000000000000000000000000000000000000000000000000
  PDIFF_ONE = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
  U256_MAX = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
  TARGET_MAX = 0x1d00ffff
  
  
  class U256(int):
      def __new__(cls, value):
          if value > U256_MAX:
              raise ValueError('too big')
          if value < 0:
              raise ValueError('negative number not allowed')
          return int.__new__(cls, value)
  
      def __str__(self):
          return f'{self:064x}'
  
      def __repr__(self):
          return f'U256(0x{self:064x})'
  
      def to_hash(self) -> 'Hash':
          """ Convert to Hash
  
          Examples:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_hash()
          Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_hash()
          Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
          """
          # ALIAS: u256 <- hash
          return Hash(self)
  
      def to_target(self) -> 'Target':
          """ Convert to Target
  
          WARNING: this process is lossy:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_target().to_u256()
          U256(0x0000000000000000000b0f270000000000000000000000000000000000000000)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_target().to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_target().to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_target().to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_target().to_u256()
          U256(0x00000000ffffff00000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_target()
          Target(0x170b0f27)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_target()
          Target(0x17130c78)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_target()
          Target(0x1b0404cb)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_target()
          Target(0x1cffff00)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_target()
          Target(0x1cffffff)
          """
          # PRIMITIVE: u256 -> target
          high_bits = 3
          low_bits = int(self)
          while low_bits > 0xffffff and high_bits < 0xff:
              high_bits += 1
              low_bits >>= 8
          assert high_bits <= 0xff
          assert low_bits <= 0xffffff
          return Target((high_bits << 24) | low_bits)
  
      def to_bdiff(self) -> 'BDiff':
          """ Convert to BDiff
  
          WARNING: this process is lossy:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_bdiff().to_u256()
          U256(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_bdiff().to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_bdiff().to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_bdiff().to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_bdiff().to_u256()
          U256(0x0000000100000000000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_bdiff()
          BDiff(25451292437624.176)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_bdiff()
          BDiff(14776367535688.639)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_bdiff()
          BDiff(16307.420938523983)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_bdiff()
          BDiff(1.0)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_bdiff()
          BDiff(0.9999847412109375)
          """
          # PRIMITIVE: u256 -> bdiff
          return BDiff(BDIFF_ONE / self)
  
      def to_pdiff(self) -> 'PDiff':
          """ Convert to PDiff
  
          WARNING: this process is lossy:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_pdiff().to_u256()
          U256(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_pdiff().to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_pdiff().to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_pdiff().to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_pdiff().to_u256()
          U256(0x0000000100000000000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_pdiff()
          PDiff(25451680799452.777)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_pdiff()
          PDiff(14776593008604.42)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_pdiff()
          PDiff(16307.669773817162)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_pdiff()
          PDiff(1.0000152590218967)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_pdiff()
          PDiff(1.0)
          """
          # PRIMITIVE: u256 -> pdiff
          return PDiff(PDIFF_ONE / self)
  
      def to_weight(self) -> 'Weight':
          """ Convert to Weight
  
          WARNING: this process is lossy:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_weight().to_u256()
          U256(0x0000000000000000000b0f271558b6c000000000000000000000000000000000)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_weight().to_u256()
          U256(0x000000000000000000130c77ffffffe100000000000000000000000000000000)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_weight().to_u256()
          U256(0x00000000000404cb000000060000000000000000000000000000000000000000)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_weight().to_u256()
          U256(0x00000000fffefffffffe38000000000000000000000000000000000000000000)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_weight().to_u256()
          U256(0x0000000100000000000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_weight()
          Weight(76.53282616688836)
          >>> U256(0x000000000000000000130c780000000000000000000000000000000000000000).to_weight()
          Weight(75.74837890381858)
          >>> U256(0x00000000000404cb000000000000000000000000000000000000000000000000).to_weight()
          Weight(45.993263027575125)
          >>> U256(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_weight()
          Weight(32.00002201394727)
          >>> U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_weight()
          Weight(32.0)
          """
          # PRIMITIVE: u256 -> weight
          return Weight(256 - log2(self + 1))
  
  
  class Hash(bytes):
      def __new__(cls, value: Union[bytes, int]) -> 'Hash':
          """ Creates Hash instance, accepts bytes (32-byte long) or int.
  
          From bytes:
  
          >>> Hash(bytes.fromhex('0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a'))
          Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a)
          >>> Hash(bytes.fromhex('000000000000000000130c780000000000000000000000000000000000000000'))
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Hash(bytes.fromhex('00000000000404cb000000000000000000000000000000000000000000000000'))
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Hash(bytes.fromhex('00000000ffff0000000000000000000000000000000000000000000000000000'))
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Hash(bytes.fromhex('00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff'))
          Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
  
          From int:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a)
          Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
          Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
          """
          _value: bytes
          if isinstance(value, bytes):
              _value = value
          elif isinstance(value, int):
              # PRIMITIVE: u256 -> hash
              _value = value.to_bytes(32, 'big')
          else:
              raise TypeError(f'invalid type {value.__class__}')
          if len(_value) != 32:
              raise ValueError(f'value has invalid binary length {len(_value)}')
          return bytes.__new__(cls, _value)  # type: ignore
  
      def __str__(self):
          return self.hex()
  
      def __repr__(self):
          return f'Hash(0x{self.hex()})'
  
      def to_u256(self) -> U256:
          """ Convert to U256
  
          Examples:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_u256()
          U256(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_u256()
          U256(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
          """
          # PRIMITIVE: hash -> u256
          return U256(int(self.hex(), 16))
  
      def to_target(self) -> 'Target':
          """ Convert to Target
  
          WARNING: this process is lossy:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_target().to_hash()
          Hash(0x0000000000000000000b0f270000000000000000000000000000000000000000)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_target().to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_target().to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_target().to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_target().to_hash()
          Hash(0x00000000ffffff00000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_target()
          Target(0x170b0f27)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_target()
          Target(0x17130c78)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_target()
          Target(0x1b0404cb)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_target()
          Target(0x1cffff00)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_target()
          Target(0x1cffffff)
          """
          # ALIAS: hash -> u256 -> target
          return self.to_u256().to_target()
  
      def to_bdiff(self) -> 'BDiff':
          """ Convert to BDiff
  
          WARNING: this process is lossy:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_bdiff().to_hash()
          Hash(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_bdiff().to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_bdiff().to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_bdiff().to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_bdiff().to_hash()
          Hash(0x0000000100000000000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_bdiff()
          BDiff(25451292437624.176)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_bdiff()
          BDiff(14776367535688.639)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_bdiff()
          BDiff(16307.420938523983)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_bdiff()
          BDiff(1.0)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_bdiff()
          BDiff(0.9999847412109375)
          """
          # ALIAS: hash -> u256 -> bdiff
          return self.to_u256().to_bdiff()
  
      def to_pdiff(self) -> 'PDiff':
          """ Convert to PDiff
  
          WARNING: this process is lossy:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_pdiff().to_hash()
          Hash(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_pdiff().to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_pdiff().to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_pdiff().to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_pdiff().to_hash()
          Hash(0x0000000100000000000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_pdiff()
          PDiff(25451680799452.777)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_pdiff()
          PDiff(14776593008604.42)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_pdiff()
          PDiff(16307.669773817162)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_pdiff()
          PDiff(1.0000152590218967)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_pdiff()
          PDiff(1.0)
          """
          # ALIAS: hash -> u256 -> pdiff
          return self.to_u256().to_pdiff()
  
      def to_weight(self) -> 'Weight':
          """ Convert to Weight
  
          WARNING: this process is lossy:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_weight().to_hash()
          Hash(0x0000000000000000000b0f271558b6c000000000000000000000000000000000)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_weight().to_hash()
          Hash(0x000000000000000000130c77ffffffe100000000000000000000000000000000)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_weight().to_hash()
          Hash(0x00000000000404cb000000060000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_weight().to_hash()
          Hash(0x00000000fffefffffffe38000000000000000000000000000000000000000000)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_weight().to_hash()
          Hash(0x0000000100000000000000000000000000000000000000000000000000000000)
  
          Examples:
  
          >>> Hash(0x0000000000000000000b0f271558b6ae0bb31389d4072bb11f3b9cda51e1357a).to_weight()
          Weight(76.53282616688836)
          >>> Hash(0x000000000000000000130c780000000000000000000000000000000000000000).to_weight()
          Weight(75.74837890381858)
          >>> Hash(0x00000000000404cb000000000000000000000000000000000000000000000000).to_weight()
          Weight(45.993263027575125)
          >>> Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000).to_weight()
          Weight(32.00002201394727)
          >>> Hash(0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff).to_weight()
          Weight(32.0)
          """
          # ALIAS: hash -> u256 -> weight
          return self.to_u256().to_weight()
  
  
  class Target(int):
      def __new__(cls, value):
          if value > TARGET_MAX:
              raise ValueError(f'too big: 0x{value:0x}')
          if value < 0:
              raise ValueError('negative number not allowed')
          return int.__new__(cls, value)
  
      def __str__(self):
          return f'{self:08x}'
  
      def __repr__(self):
          return f'Target(0x{self:08x})'
  
      def to_u256(self) -> U256:
          """ Convert to U256
  
          Examples:
  
          >>> Target(0x170b0f27).to_u256()
          U256(0x0000000000000000000b0f270000000000000000000000000000000000000000)
          >>> Target(0x17130c78).to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Target(0x1b0404cb).to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Target(0x1cffff00).to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Target(0x1cffffff).to_u256()
          U256(0x00000000ffffff00000000000000000000000000000000000000000000000000)
  
          Extra:
  
          >>> Target(0x1d00ffff).to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          """
          # PRIMITIVE: target -> u256
          high_bits = self >> 24
          low_bits = self & 0xffffff
          return U256(low_bits << (8 * (high_bits - 3)))
  
      def to_hash(self) -> Hash:
          """ Convert to Hash
  
          Examples:
  
          >>> Target(0x170b0f27).to_hash()
          Hash(0x0000000000000000000b0f270000000000000000000000000000000000000000)
          >>> Target(0x17130c78).to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> Target(0x1b0404cb).to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> Target(0x1cffff00).to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> Target(0x1cffffff).to_hash()
          Hash(0x00000000ffffff00000000000000000000000000000000000000000000000000)
          """
          # ALIAS: target -> u256 -> hash
          return self.to_u256().to_hash()
  
      def to_bdiff(self) -> 'BDiff':
          """ Convert to BDiff
  
          Examples:
  
          >>> Target(0x170b0f27).to_bdiff()
          BDiff(25451295365779.504)
          >>> Target(0x17130c78).to_bdiff()
          BDiff(14776367535688.639)
          >>> Target(0x1b0404cb).to_bdiff()
          BDiff(16307.420938523983)
          >>> Target(0x1cffff00).to_bdiff()
          BDiff(1.0)
          >>> Target(0x1cffffff).to_bdiff()
          BDiff(0.9999848008146763)
          """
          # ALIAS: target -> u256 -> bdiff
          return self.to_u256().to_bdiff()
  
      def to_pdiff(self) -> 'PDiff':
          """ Convert to PDiff
  
          Examples:
  
          >>> Target(0x170b0f27).to_pdiff()
          PDiff(25451683727652.793)
          >>> Target(0x17130c78).to_pdiff()
          PDiff(14776593008604.42)
          >>> Target(0x1b0404cb).to_pdiff()
          PDiff(16307.669773817162)
          >>> Target(0x1cffff00).to_pdiff()
          PDiff(1.0000152590218967)
          >>> Target(0x1cffffff).to_pdiff()
          PDiff(1.0000000596046483)
          """
          # ALIAS: target -> u256 -> pdiff
          return self.to_u256().to_pdiff()
  
      def to_weight(self) -> 'Weight':
          """ Convert to Weight
  
          WARNING: this process is lossy:
  
          >>> Target(0x170b0f27).to_weight().to_target()
          Target(0x170b0f27)
          >>> Target(0x17130c78).to_weight().to_target()
          Target(0x17130c77)
          >>> Target(0x1b0404cb).to_weight().to_target()
          Target(0x1b0404cb)
          >>> Target(0x1cffff00).to_weight().to_target()
          Target(0x1cfffeff)
          >>> Target(0x1cffffff).to_weight().to_target()
          Target(0x1cfffffe)
  
          Examples:
  
          >>> Target(0x170b0f27).to_weight()
          Weight(76.53282633286952)
          >>> Target(0x17130c78).to_weight()
          Weight(75.74837890381858)
          >>> Target(0x1b0404cb).to_weight()
          Weight(45.993263027575125)
          >>> Target(0x1cffff00).to_weight()
          Weight(32.00002201394727)
          >>> Target(0x1cffffff).to_weight()
          Weight(32.00000008599133)
          """
          # ALIAS: target -> u256 -> weight
          return self.to_u256().to_weight()
  
  
  class BDiff(float):
      def __repr__(self):
          return f'BDiff({float(self)})'
  
      def to_u256(self) -> U256:
          """ Convert to U256
  
          Examples:
  
          >>> BDiff(25451292437624.176).to_u256()
          U256(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> BDiff(14776367535688.639).to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> BDiff(16307.420938523983).to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> BDiff(1.0).to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> BDiff(0.9999847412109375).to_u256()
          U256(0x0000000100000000000000000000000000000000000000000000000000000000)
          """
          # PRIMITIVE: bdiff -> u256
          return U256(BDIFF_ONE / self)
  
      def to_hash(self) -> Hash:
          """ Convert to Hash
  
          Examples:
  
          >>> BDiff(25451292437624.176).to_hash()
          Hash(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> BDiff(14776367535688.639).to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> BDiff(16307.420938523983).to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> BDiff(1.0).to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> BDiff(0.9999847412109375).to_hash()
          Hash(0x0000000100000000000000000000000000000000000000000000000000000000)
          """
          # ALIAS: bdiff -> u256 -> hash
          return self.to_u256().to_hash()
  
      def to_target(self) -> Target:
          """ Convert to Target
  
          Examples:
  
          >>> BDiff(25451295365779.504).to_target()
          Target(0x170b0f27)
          >>> BDiff(14776367535688.639).to_target()
          Target(0x17130c78)
          >>> BDiff(16307.420938523983).to_target()
          Target(0x1b0404cb)
          >>> BDiff(1.0).to_target()
          Target(0x1cffff00)
          >>> BDiff(0.9999848008146763).to_target()
          Target(0x1cffffff)
          """
          # ALIAS: bdiff -> u256 -> target
          return self.to_u256().to_target()
  
      def to_pdiff(self) -> 'PDiff':
          """ Convert to PDiff
  
          Examples:
  
          >>> BDiff(25451292437624.176).to_pdiff()
          PDiff(25451680799452.777)
          >>> BDiff(14776367535688.639).to_pdiff()
          PDiff(14776593008604.42)
          >>> BDiff(16307.420938523983).to_pdiff()
          PDiff(16307.669773817162)
          >>> BDiff(1.0).to_pdiff()
          PDiff(1.0000152590218967)
          >>> BDiff(0.9999847412109375).to_pdiff()
          PDiff(1.0)
          """
          # ALIAS: bdiff -> u256 -> pdiff
          return self.to_u256().to_pdiff()
  
      def to_weight(self) -> 'Weight':
          """ Convert to Weight
  
          WARNING: this process is lossy:
  
          >>> BDiff(25451292437624.176).to_weight().to_bdiff()
          BDiff(25451292437624.027)
          >>> BDiff(14776367535688.639).to_weight().to_bdiff()
          BDiff(14776367535688.725)
          >>> BDiff(16307.420938523983).to_weight().to_bdiff()
          BDiff(16307.420938523897)
          >>> BDiff(1.0).to_weight().to_bdiff()
          BDiff(1.0000000000000064)
          >>> BDiff(0.9999847412109375).to_weight().to_bdiff()
          BDiff(0.9999847412109375)
  
          Examples:
  
          >>> BDiff(25451292437624.176).to_weight()
          Weight(76.53282616688836)
          >>> BDiff(14776367535688.639).to_weight()
          Weight(75.74837890381858)
          >>> BDiff(16307.420938523983).to_weight()
          Weight(45.993263027575125)
          >>> BDiff(1.0).to_weight()
          Weight(32.00002201394727)
          >>> BDiff(0.9999847412109375).to_weight()
          Weight(32.0)
          """
          # ALIAS: bdiff -> u256 -> weight
          return self.to_u256().to_weight()
  
  
  class PDiff(float):
      def __repr__(self):
          return f'PDiff({float(self)})'
  
      def to_u256(self) -> U256:
          """ Convert to U256
  
          Examples:
  
          >>> PDiff(25451680799452.777).to_u256()
          U256(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> PDiff(14776593008604.42).to_u256()
          U256(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> PDiff(16307.669773817162).to_u256()
          U256(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> PDiff(1.0000152590218967).to_u256()
          U256(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> PDiff(1.0).to_u256()
          U256(0x0000000100000000000000000000000000000000000000000000000000000000)
          """
          # PRIMITIVE: pdiff -> u256
          return U256(PDIFF_ONE / self)
  
      def to_hash(self) -> Hash:
          """ Convert to Hash
  
          Examples:
  
          >>> PDiff(25451680799452.777).to_hash()
          Hash(0x0000000000000000000b0f271558b6ae00000000000000000000000000000000)
          >>> PDiff(14776593008604.42).to_hash()
          Hash(0x000000000000000000130c780000000000000000000000000000000000000000)
          >>> PDiff(16307.669773817162).to_hash()
          Hash(0x00000000000404cb000000000000000000000000000000000000000000000000)
          >>> PDiff(1.0000152590218967).to_hash()
          Hash(0x00000000ffff0000000000000000000000000000000000000000000000000000)
          >>> PDiff(1.0).to_hash()
          Hash(0x0000000100000000000000000000000000000000000000000000000000000000)
          """
          # ALIAS: pdiff -> u256 -> hash
          return self.to_u256().to_hash()
  
      def to_target(self) -> Target:
          """ Convert to Target
  
          Examples:
  
          >>> PDiff(25451683727652.793).to_target()
          Target(0x170b0f27)
          >>> PDiff(14776593008604.42).to_target()
          Target(0x17130c78)
          >>> PDiff(16307.669773817162).to_target()
          Target(0x1b0404cb)
          >>> PDiff(1.0000152590218967).to_target()
          Target(0x1cffff00)
          >>> PDiff(1.0000000596046483).to_target()
          Target(0x1cffffff)
          """
          # ALIAS: pdiff -> u256 -> target
          return self.to_u256().to_target()
  
      def to_bdiff(self) -> 'BDiff':
          """ Convert to BDiff
  
          Examples:
  
          >>> PDiff(25451680799452.777).to_bdiff()
          BDiff(25451292437624.176)
          >>> PDiff(14776593008604.42).to_bdiff()
          BDiff(14776367535688.639)
          >>> PDiff(16307.669773817162).to_bdiff()
          BDiff(16307.420938523983)
          >>> PDiff(1.0000152590218967).to_bdiff()
          BDiff(1.0)
          >>> PDiff(1.0).to_bdiff()
          BDiff(0.9999847412109375)
          """
          # ALIAS: pdiff -> u256 -> bdiff
          return self.to_u256().to_bdiff()
  
      def to_weight(self) -> 'Weight':
          """ Convert to Weight
  
          WARNING: this process is lossy:
  
          >>> PDiff(25451680799452.777).to_weight().to_pdiff()
          PDiff(25451680799452.633)
          >>> PDiff(14776593008604.42).to_weight().to_pdiff()
          PDiff(14776593008604.506)
          >>> PDiff(16307.669773817162).to_weight().to_pdiff()
          PDiff(16307.669773817077)
          >>> PDiff(1.0000152590218967).to_weight().to_pdiff()
          PDiff(1.0000152590219031)
          >>> PDiff(1.0).to_weight().to_pdiff()
          PDiff(1.0)
  
          Examples:
  
          >>> PDiff(25451680799452.777).to_weight()
          Weight(76.53282616688836)
          >>> PDiff(14776593008604.42).to_weight()
          Weight(75.74837890381858)
          >>> PDiff(16307.669773817162).to_weight()
          Weight(45.993263027575125)
          >>> PDiff(1.0000152590218967).to_weight()
          Weight(32.00002201394727)
          >>> PDiff(1.0).to_weight()
          Weight(32.0)
          """
          # ALIAS: pdiff -> u256 -> weight
          return self.to_u256().to_weight()
  
  
  class Weight(float):
      def __repr__(self):
          return f'Weight({float(self)})'
  
      def logsum(self, other: 'Weight') -> 'Weight':
          """ Make a "logarithmic sum" on base 2.
  
          That is `x.logsum(y)` is equivalent to `log2(2**x + 2**y)`, although there are some precision differences.
  
          Currently is just a proxy to `hathor.transaction.sum_weights`.
          """
          from hathor.transaction import sum_weights
          return Weight(sum_weights(self, other))
  
      def to_u256(self) -> U256:
          """ Convert to U256
  
          Examples:
  
          >>> Weight(76.53282616688836).to_u256()
          U256(0x0000000000000000000b0f271558b6c000000000000000000000000000000000)
          >>> Weight(75.74837890381858).to_u256()
          U256(0x000000000000000000130c77ffffffe100000000000000000000000000000000)
          >>> Weight(45.993263027575125).to_u256()
          U256(0x00000000000404cb000000060000000000000000000000000000000000000000)
          >>> Weight(32.00002201394727).to_u256()
          U256(0x00000000fffefffffffe38000000000000000000000000000000000000000000)
          >>> Weight(32.0).to_u256()
          U256(0x0000000100000000000000000000000000000000000000000000000000000000)
          """
          # PRIMITIVE: weight -> u256
          return U256(2**(256 - self) - 1)
  
      def to_hash(self) -> Hash:
          """ Convert to Hash
  
          Examples:
  
          >>> Weight(76.53282616688836).to_hash()
          Hash(0x0000000000000000000b0f271558b6c000000000000000000000000000000000)
          >>> Weight(75.74837890381858).to_hash()
          Hash(0x000000000000000000130c77ffffffe100000000000000000000000000000000)
          >>> Weight(45.993263027575125).to_hash()
          Hash(0x00000000000404cb000000060000000000000000000000000000000000000000)
          >>> Weight(32.00002201394727).to_hash()
          Hash(0x00000000fffefffffffe38000000000000000000000000000000000000000000)
          >>> Weight(32.0).to_hash()
          Hash(0x0000000100000000000000000000000000000000000000000000000000000000)
          """
          # ALIAS: weight -> u256 -> hash
          return self.to_u256().to_hash()
  
      def to_target(self) -> Target:
          """ Convert to Target
  
          Examples:
  
          >>> Weight(76.53282616688836).to_target()
          Target(0x170b0f27)
          >>> Weight(75.74837890381858).to_target()
          Target(0x17130c77)
          >>> Weight(45.993263027575125).to_target()
          Target(0x1b0404cb)
          >>> Weight(32.00002201394727).to_target()
          Target(0x1cfffeff)
  
          #>>> Weight(32.0).to_target()
          #Target(0x1d010000)
          """
          # ALIAS: weight -> u256 -> target
          return self.to_u256().to_target()
  
      def to_bdiff(self) -> BDiff:
          """ Convert to BDiff
  
          Examples:
  
          >>> Weight(76.53282616688836).to_bdiff()
          BDiff(25451292437624.027)
          >>> Weight(75.74837890381858).to_bdiff()
          BDiff(14776367535688.725)
          >>> Weight(45.993263027575125).to_bdiff()
          BDiff(16307.420938523897)
          >>> Weight(32.00002201394727).to_bdiff()
          BDiff(1.0000000000000064)
          >>> Weight(32.0).to_bdiff()
          BDiff(0.9999847412109375)
          """
          # ALIAS: weight -> u256 -> bdiff
          return self.to_u256().to_bdiff()
  
      def to_pdiff(self) -> PDiff:
          """ Convert to PDiff
  
          >>> Weight(76.53282616688836).to_pdiff()
          PDiff(25451680799452.633)
          >>> Weight(75.74837890381858).to_pdiff()
          PDiff(14776593008604.506)
          >>> Weight(45.993263027575125).to_pdiff()
          PDiff(16307.669773817077)
          >>> Weight(32.00002201394727).to_pdiff()
          PDiff(1.0000152590219031)
          >>> Weight(32.0).to_pdiff()
          PDiff(1.0)
          """
          # ALIAS: weight -> u256 -> pdiff
          return self.to_u256().to_pdiff()
  