Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions hex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
class HexOperations:
def validate_hex(self, hex_str: str) -> str:
"""Validates and extracts the hex part of the string."""
if not hex_str.startswith("H'"):
raise ValueError("Invalid HEX format. Must start with H'")
hex_val = hex_str[2:]
if not hex_val:
raise ValueError("Empty HEX value")
try:
int(hex_val, 16)
except ValueError:
raise ValueError("Invalid HEX characters")
return hex_val
def validate_dec(self, dec_str: str) -> str:
"""Validates and extracts the decimal part of the string."""
if not dec_str.startswith("D'"):
raise ValueError("Invalid Decimal format. Must start with D'")
dec_val = dec_str[2:]
if not dec_val:
raise ValueError("Empty Decimal value")
try:
int(dec_val)
except ValueError:
raise ValueError("Invalid Decimal characters")
return dec_val
def hex(self, dec_str: str) -> str:
"""Hexadecimal <-> Decimal conversions e.g. hex(D'243) returns H'F3"""
val = self.validate_dec(dec_str)
num = int(val)
return f"H'{hex(num)[2:].upper()}"
def dec(self, hex_str: str) -> str:
"""Hexadecimal <-> Decimal conversions e.g. dec(H'1A5) is D'421"""
val = self.validate_hex(hex_str)
num = int(val, 16)
return f"D'{num}"
def add(self, hex1: str, hex2: str) -> str:
"""Arithmetic operations in HEX base (Addition)"""
val1 = int(self.validate_hex(hex1), 16)
val2 = int(self.validate_hex(hex2), 16)
return f"H'{hex(val1 + val2)[2:].upper()}"
def subtract(self, hex1: str, hex2: str) -> str:
"""Arithmetic operations in HEX base (Subtraction)"""
val1 = int(self.validate_hex(hex1), 16)
val2 = int(self.validate_hex(hex2), 16)
res = val1 - val2
if res < 0:
return f"-H'{hex(abs(res))[2:].upper()}"
return f"H'{hex(res)[2:].upper()}"
def multiply(self, hex1: str, hex2: str) -> str:
"""Arithmetic operations in HEX base (Multiplication)"""
val1 = int(self.validate_hex(hex1), 16)
val2 = int(self.validate_hex(hex2), 16)
return f"H'{hex(val1 * val2)[2:].upper()}"
def divide(self, hex1: str, hex2: str) -> str:
"""Arithmetic operations in HEX base (Division)"""
val1 = int(self.validate_hex(hex1), 16)
val2 = int(self.validate_hex(hex2), 16)
if val2 == 0:
raise ValueError("Division by zero")
return f"H'{hex(val1 // val2)[2:].upper()}"
def complement_15s(self, hex_str: str) -> str:
"""Complements (15's)"""
val = self.validate_hex(hex_str)
comp = "".join(hex(15 - int(c, 16))[2:].upper() for c in val)
return f"H'{comp}"
def complement_16s(self, hex_str: str) -> str:
"""Complements (16's)"""
_15s = self.complement_15s(hex_str)
val = int(_15s[2:], 16) + 1
# Pad with leading zeros based on original length, or standard hex length
# Standard approach: 15's complement + 1
comp_16 = hex(val)[2:].upper()
# Ensure it maintains at least the length of the input (handle overflow if needed, but standard 16's comp just adds 1)
expected_len = len(hex_str) - 2
comp_16 = comp_16.zfill(expected_len)
# If length exceeded (e.g., FFFF + 1 = 10000), we just take the last expected_len digits for proper 2s/16s comp behaviour
if len(comp_16) > expected_len:
comp_16 = comp_16[-expected_len:]
return f"H'{comp_16}"
41 changes: 41 additions & 0 deletions test_hex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import unittest
from hex import HexOperations
class TestHexOperations(unittest.TestCase):
def setUp(self):
self.hex_ops = HexOperations()
def test_hex_conversion(self):
# Hexadecimal <-> Decimal conversions e.g. "hex(D'243)" returns "H'F3", "dec(H'1A5)" is "D'421"
self.assertEqual(self.hex_ops.hex("D'243"), "H'F3")
def test_dec_conversion(self):
self.assertEqual(self.hex_ops.dec("H'1A5"), "D'421")
def test_add(self):
self.assertEqual(self.hex_ops.add("H'A", "H'5"), "H'F")
self.assertEqual(self.hex_ops.add("H'AB12", "H'1"), "H'AB13")
def test_subtract(self):
self.assertEqual(self.hex_ops.subtract("H'10", "H'A"), "H'6")
self.assertEqual(self.hex_ops.subtract("H'A", "H'10"), "-H'6")
def test_multiply(self):
self.assertEqual(self.hex_ops.multiply("H'2", "H'8"), "H'10")
def test_divide(self):
self.assertEqual(self.hex_ops.divide("H'10", "H'2"), "H'8")
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
self.hex_ops.divide("H'10", "H'0")
def test_complement_15s(self):
self.assertEqual(self.hex_ops.complement_15s("H'A5"), "H'5A")
self.assertEqual(self.hex_ops.complement_15s("H'F3"), "H'0C")
def test_complement_16s(self):
self.assertEqual(self.hex_ops.complement_16s("H'A5"), "H'5B")
self.assertEqual(self.hex_ops.complement_16s("H'00"), "H'00") # 15s is FF -> +1 is 100 -> truncated to 00
def test_invalid_input(self):
# Invalid prefix
with self.assertRaises(ValueError):
self.hex_ops.validate_hex("1A")
# Invalid chars
with self.assertRaises(ValueError):
self.hex_ops.validate_hex("H'1G")
# Decimal invalid
with self.assertRaises(ValueError):
self.hex_ops.validate_dec("D'ABC")
if __name__ == '__main__':
unittest.main()
Loading