class TestHashTable(unittest.TestCase): def setUp(self): self.ht = HashTable() def test_hash(self): self.assertEqual(self.ht.hash("hello"), self.ht.hash("hello")) self.assertTrue(self.ht.hash("hello") < self.ht.capacity) def test_insert(self): self.assertEqual(self.ht.size, 0) self.ht.insert("test_key", "test_value") self.assertEqual(self.ht.size, 1) self.assertEqual(self.ht.buckets[self.ht.hash("test_key")].value, "test_value") def test_find(self): self.assertEqual(self.ht.size, 0) obj = "hello" self.ht.insert("key1", obj) self.assertEqual(obj, self.ht.find("key1")) obj = ["this", "is", "a", "list"] self.ht.insert("key2", obj) self.assertEqual(obj, self.ht.find("key2")) def test_remove(self): self.assertEqual(self.ht.size, 0) obj = "test object" self.ht.insert("key1", obj) self.assertEqual(1, self.ht.size) self.assertEqual(obj, self.ht.remove("key1")) self.assertEqual(0, self.ht.size) self.assertEqual(None, self.ht.remove("some random key")) def test_capacity(self): # Test all public methods in one run at a large capacity for i in range(0, 1000): self.assertEqual(i, self.ht.size) self.ht.insert("key" + str(i), "value") self.assertEqual(self.ht.size, 1000) for i in range(0, 1000): self.assertEqual(1000 - i, self.ht.size) self.assertEqual(self.ht.find("key" + str(i)), self.ht.remove("key" + str(i))) def test_issue2(self): self.assertEqual(self.ht.size, 0) self.ht.insert('A', 5) self.assertEqual(self.ht.size, 1) self.ht.insert('B', 10) self.assertEqual(self.ht.size, 2) self.ht.insert('Ball', 'hello') self.assertEqual(self.ht.size, 3) self.assertEqual(5, self.ht.remove('A')) self.assertEqual(self.ht.size, 2) self.assertEqual(None, self.ht.remove('A')) self.assertEqual(self.ht.size, 2) self.assertEqual(None, self.ht.remove('A')) self.assertEqual(self.ht.size, 2)
def test_find(self): size = 30 step = 2 h = HashTable(size, step) h.put("One") h.put("Two") h.put("Three") self.assertEqual(h.find("One"), h.hash_fun("One")) self.assertEqual(h.find("Two"), h.hash_fun("Two")) self.assertEqual(h.find("Three"), h.hash_fun("Three")) self.assertEqual(h.find("Four"), None)
class Vertex(object): def __init__(self, location): self.edges = HashTable() self.value = location # add edge to HashTable -> O(n) complexity def add_edge(self, edge): self.edges.insert(edge.identifier, edge) # find edge by id -> O(n) complexity def find_edge(self, edge_id): return self.edges.find(edge_id) # find distance to neighboring vertex -> O(n) complexity def distance_to(self, location): return self.edges.find(location.identifier).weight
def test_find(): total_size = 50 test_table = HashTable(total_size) key1 = 'test1' value1 = 'passed1' test_table.insert(key1,value1) assert test_table.find(key1) == value1
class Graph(object): def __init__(self): self.vertices = HashTable(20) # creates a vertex from a location and adds it to the vertex hash table -> O(n) complexity def add_vertex(self, location): self.vertices.insert(location.identifier, Vertex(location)) # creates a bi-directional weighted edge between two vertices -> O(n) complexity def add_weighted_edge(self, origin, destination, weight): self.vertices.find(origin.identifier).add_edge(Edge(destination, weight)) self.vertices.find(destination.identifier).add_edge(Edge(origin, weight)) # finds the vertex matching the location -> O(n) complexity def find_vertex(self, location): return self.vertices.find(location.identifier) # finds the distance between two vertices -> O(n) complexity def find_distance(self, origin, target): return self.vertices.find(origin.identifier).distance_to(target) # finds the distance between a location and package destination # used for priority list sorting def distance_to_deliver(self, location): def distance_to(package): return self.vertices.find(location.identifier).distance_to(package.destination) return distance_to # sets up the next closest location the truck should drive to def distance_from(self, origin): def distance_to(destination): return self.vertices.find(origin.identifier).distance_to(destination) return distance_to
def main(): secret_hidden = False secret_key = os.urandom(16) secret_message = b'no flag for you' flag = os.environ.get('FLAG', 'MOCSCTF{REDACTED}') ht = HashTable() while True: try: cmd = input('> ').split(' ') if cmd[0] == 'set': key = bytes.fromhex(cmd[1]) value = bytes.fromhex(cmd[2]) ht.add(key, value) elif cmd[0] == 'get': key = bytes.fromhex(cmd[1]) res = ht.find(key) if res == False: continue print(res) elif cmd[0] == 'delete': key = bytes.fromhex(cmd[1]) ht.delete(key) elif cmd[0] == 'put_secret': ht.add(secret_key, secret_message) secret_hidden = True elif cmd[0] == 'get_secret': if secret_hidden == False: print('Go hide the secret first!') elif ht.find(secret_key) != secret_message: print(f'You overwrote the key! This is your flag: {flag}') else: print('Try harder!') except KeyboardInterrupt: raise KeyboardInterrupt
class TestHashTable(unittest.TestCase): def setUp(self): self.ht = HashTable() def test_hash(self): self.assertEqual(self.ht.hash("hello"), self.ht.hash("hello")) self.assertTrue(self.ht.hash("hello") < self.ht.capacity) def test_insert(self): self.assertEqual(self.ht.size, 0) self.ht.insert("test_key", "test_value") self.assertEqual(self.ht.size, 1) self.assertEqual(self.ht.buckets[self.ht.hash("test_key")].value, "test_value") def test_find(self): obj = "hello" self.ht.insert("key1", obj) self.assertEqual(self.ht.find("key1"), obj) obj = ["this", "is", "a", "list"] self.ht.insert("key2", obj) self.assertEqual(self.ht.find("key2"), obj) def test_find_overwritten_value(self): self.assertEqual(self.ht.size, 0) key = "key1" obj = "obj1" self.ht.insert(key, obj) self.assertEqual(self.ht.find(key), obj) self.assertEqual(self.ht.size, 1) another_obj = "another_obj" self.ht.insert(key, another_obj) self.assertEqual(self.ht.find(key), another_obj) self.assertEqual(self.ht.size, 1) def test_remove(self): self.assertEqual(self.ht.size, 0) obj = "test object" self.ht.insert("key1", obj) self.assertEqual(self.ht.size, 1) self.assertEqual(self.ht.remove("key1"), obj) self.assertEqual(self.ht.size, 0) self.assertEqual(self.ht.remove("non exist key"), None) def test_capacity(self): # Test all public methods in one run at a large capacity for i in range(1000): self.assertEqual(i, self.ht.size) self.ht.insert(f"key{i}", "value") self.assertEqual(self.ht.size, 1000) for i in range(1000): self.assertEqual(1000 - i, self.ht.size) self.assertEqual(self.ht.find(f"key{i}"), self.ht.remove(f"key{i}")) def test_issue2(self): self.assertEqual(self.ht.size, 0) self.ht.insert("A", 5) self.assertEqual(self.ht.size, 1) self.ht.insert("B", 10) self.assertEqual(self.ht.size, 2) self.ht.insert("Ball", "hello") self.assertEqual(self.ht.size, 3) self.assertEqual(self.ht.remove("A"), 5) self.assertEqual(self.ht.size, 2) self.assertEqual(self.ht.remove("A"), None) self.assertEqual(self.ht.size, 2) self.assertEqual(self.ht.remove("A"), None) self.assertEqual(self.ht.size, 2)
def test_not_included(): hash_table = HashTable(0) hash_table.add('fruit', 'apple') expected = None actual = hash_table.find('vegetable') assert actual == expected
def run(): graph = Graph() locations_hash = HashTable(20) packages_hash = HashTable(40) # opening and reading the location data from csv, then populating a hash table and graph with location data with open('DistanceNameData.csv') as csvfile: location_data = csv.reader(csvfile) # looping through location data -> O(n) complexity for data_row in location_data: location = Location(*data_row) # inserting location data into the hash table -> O(n) complexity locations_hash.insert(location.identifier, location) locations_hash.insert(location.address, location) # creating graph vertices -> O(n) complexity graph.add_vertex(location) all_packages = [] high_priority = [] low_priority = [] with open('InputData.csv') as csvfile: package_data = csv.reader(csvfile) for data_row in package_data: package = Package(*(data_row + [locations_hash.find(data_row[1])])) all_packages.append(package) packages_hash.insert(package.identifier, package) # sorting through packages and divvying them up depending on priority levels # O(1) complexity if package.high_priority(): high_priority.append(package) else: low_priority.append(package) # edge-creation between graph vertices with open('DistanceData.csv') as csvfile: distance_data = csv.reader(csvfile) # looping through csv file -> O(n^2) complexity for i, data_row in enumerate(distance_data): for j, data in enumerate(data_row): if data != '': # add a weighted edge to graph -> O(n) complexity graph.add_weighted_edge(locations_hash.find(i), locations_hash.find(j), float(data)) start_time = timedelta(hours=8) start_location = locations_hash.find(0) # limiting truck use to two trucks--truck 1 will make two trips since we have two drivers only trucks = [ Truck(1, start_time, start_location), Truck(2, start_time, start_location) ] # list of leave times that are optimized for package distribution times_to_leave_hub = [ timedelta(hours=8), timedelta(hours=9, minutes=5), timedelta(hours=10, minutes=20) ] # sort high and low priority lists based on distance from main hub -> O(n*log(n)) complexity high_priority = sorted(high_priority, key=graph.distance_to_deliver(start_location)) low_priority = sorted(low_priority, key=graph.distance_to_deliver(start_location)) count = 0 truck_index = 0 i = 0 # while loop until packages are all delivered while count < len(all_packages): truck = trucks[truck_index] if i < len(times_to_leave_hub): leave_hub_at = times_to_leave_hub[i] truck.wait_at_hub(leave_hub_at) # filter priority list based on which packages a given truck can deliver -> O(n) filtered_high = [p for p in high_priority if truck.can_deliver(p)] # load up as many high priority packages as the truck can fit for package in filtered_high: # adding package to truck list -> O(1) complexity truck.add_package(package) count += 1 if truck.is_full(): break # if truck isn't full after high priority packages, fill it up with nearby low priority packages if truck.is_full() is not True: filtered_low = [ p for p in low_priority if truck.can_deliver(p) ] for package in filtered_low: truck.add_package(package) count += 1 if truck.is_full(): break # using greedy algorithm, truck delivers packages in a route that is most optimized according to graph # O(n^2*log(n)) complexity truck.deliver_packages(graph, (len(all_packages) - count) > truck.max) i += 1 truck_index = i % len(trucks) def total_distance(truck): return truck.total_distance return [sum(map(total_distance, trucks)), packages_hash, all_packages]