diff --git a/main.py b/main.py
index b89cb9ddf53504a2053dfe1304ae742f52e10dee..57ec0683727c9934b09a55ecf2862b0268a42d17 100644
--- a/main.py
+++ b/main.py
@@ -22,221 +22,218 @@ import argparse, math, re
 # User Imports.
 
 
-def main(set_identifier_bit_count, line_count, block_identifier_bit_count, file_name, verbose):
-    """
-    Program main.
-    """
-    print('\n')
-    print('main():')
-    print('    set_identifier_bit_count: {0}'.format(set_identifier_bit_count))
-    print('    line_count: {0}'.format(line_count))
-    print('    block_identifier_bit_count: {0}'.format(block_identifier_bit_count))
-    print('    file_name: {0}'.format(file_name))
-    print('    verbose: {0}'.format(verbose))
-    print('')
-
-    # Calculate total sizes of cache structures.
-    s = set_identifier_bit_count
-    S = set_count = int(math.pow(2, s))
-    E = line_count
-    e = line_identifier_bit_count = math.log2(E)
-    b = block_identifier_bit_count
-    B = block_size = int(math.pow(2, b))
-    m = address_identifier_bit_count = E * S
-    # M = total_bytes = int(math.pow(2, m))
-    M = int(math.pow(2, 64))
-    C = capacity = S * E * B
-    tag_identifier_bit_count = m - (b + s)
-
-    # Create cache object.
-    cache = []
-    for set in range(set_count):
-        cache.append([])
-
-        # Create lines in set.
-        for line in range(line_count):
-            cache[len(cache) - 1].append({
-                'valid': False,
-                'tag': 0,
-                'block': [],
-            })
-
-    # print('Address Bit Count: {0}'.format(address_identifier_bit_count))
-    # print('B: {0}'.format(B))
-    # print('S: {0}'.format(S))
-    # print('E: {0}'.format(E))
-    # print('Tag Bits: {0}'.format(tag_identifier_bit_count))
-    print_cache(cache)
-    print('\n\n')
-
-    run_simulation(cache, file_name, M, B, S)
-
-
-def run_simulation(cache, file_name, M, B, S):
-    """
-
-    :param cache:
-    :return:
-    """
-    hits = 0
-    misses = 0
-    evictions = 0
-
-    # Open file to read in values.
-    with open(file_name) as file:
-        # Read in line.
-        for line in file:
-            # print('Line: {0}'.format(line))
-            cache, hits, misses, evictions = handle_line(cache, line, M, B, S, hits, misses, evictions)
-            # print_cache(cache)
-
-    # print('M: {0}   As Bits: {1:b}'.format(M, M - 1))
-    # print('B: {0}   As Bits: {1:b}'.format(B, B - 1))
-    # print('S: {0}   As Bits: {1:b}'.format(S, S - 1))
-
-    print('hits:{0} misses:{1} evictions:{2}'.format(hits, misses, evictions))
-
-
-def handle_line(cache, line, M, B, S, hits, misses, evictions, print_results=True):
-    """
-
-    :param cache:
-    :param line:
-    :return:
-    """
-    split_line = re.split(r'[ ,\n]', line)
-    # print('split_line: {0}'.format(split_line))
-
-    # Check if instruction load. If so, we can skip line.
-    if split_line[0] == 'I':
-        # Found instruction load. Returning.
-        return (cache, hits, misses, evictions)
-    else:
-        line = line.strip()
-        conflict_miss = -1
-        # print('\n')
-        # print('handle_line():')
-        # print('    line: {0}'.format(line))
-        # print('    M: {0}'.format(M))
-        # print('    B: {0}'.format(B))
-        # print('    S: {0}'.format(S))
-        # print('')
-
-        # Not an instruction load. Keep processing. Start by getting address.
-        address = int(split_line[2]) % M
-        value_size = int(split_line[3])
-
-        # Get mask values.
-        full_mask = M - 1
-        block_mask = B - 1
-        block_offset = int(math.log2(B))
-        set_mask = ((S << block_offset) - 1) ^ block_mask
-        set_offset = int(math.log2(S))
-        tag_mask = (M - 1) ^ set_mask ^ block_mask
-
-        # print('')
-        # print('Address: {0}     As Bits: {0:b}'.format(address))
-        # print('full_mask: {0}   As Bits: {0:b}'.format(full_mask))
-        # print('block_mask: {0}  As Bits: {0:b}'.format(block_mask))
-        # print('set_mask: {0}    As Bits: {0:b}'.format(set_mask))
-        # print('tag_mask: {0}    As Bits: {0:b}'.format(tag_mask))
-        # print('')
-
-        # Use masks to get address chunks.
-        block = address & block_mask
-        set = (address & set_mask) >> block_offset
-        tag = (address & tag_mask) >> (block_offset + set_offset)
-
-        # print('block: {0}   As Bits: {0:b}'.format(block))
-        # print('set: {0}   As Bits: {0:b}'.format(set))
-        # print('tag: {0}   As Bits: {0:b}'.format(tag))
-
-        # We have our values. Now compare against the all entries in the cache.
-        for index in range(len(cache[set])):
-            # Check if match.
-            if cache[set][index]['valid'] == True and cache[set][index]['tag'] == tag:
-                # Match found.
-                if print_results:
-                    print('{0} hit'.format(line))
-
-                hits += 1
-                return (cache, hits, misses, evictions)
-            else:
-                # Not a match. Examine why.
-                if cache[set][index]['valid'] == False:
-                    # Cache location was not set. Cold miss. We can just set and return.
-                    if print_results:
-                        print('{0} miss'.format(line))
-
-                    misses += 1
-                    cache[set][index]['valid'] = True
-                    cache[set][index]['tag'] = tag
-
-                    # Set block values that are "loaded" into memory.
-                    lower_offset = int(address / B) * B
-                    upper_offset = (int(address / B) + 1) * B
-                    value_offset = address + value_size
-                    cache[set][index]['block'] = []
-                    for i in range(B):
-                        cache[set][index]['block'].append(lower_offset + i)
-
-                    # Check if size sets address outside of current cache block.
-                    if value_offset >= upper_offset:
-                        # Size sets address outside of current cache block. Update additional blocks as needed.
-                        # Start by getting our new size.
-                        new_size = value_offset - upper_offset
-                        if new_size < 0:
-                            new_size = 0
-
-                        # Pass our new line data.
-                        new_line = ' L {0},{1}'.format(upper_offset, new_size)
-                        cache, null, null, null = handle_line(cache, new_line, M, B, S, 0, 0, 0, False)
-
-                    return (cache, hits, misses, evictions)
-
-
-        # If we made it this far, cache locations were set, but tags did not match. Conflict miss.
-        if print_results:
-            print('{0} miss eviction'.format(line))
-
-        misses += 1
-        evictions += 1
-        cache[set][0]['tag'] = tag
-
-        # Set block values that are "loaded" into memory.
-        lower_offset = int(address / B) * B
-        upper_offset = (int(address / B) + 1) * B
-        value_offset = address + value_size
-        cache[set][0]['block'] = []
-        for i in range(B):
-            cache[set][0]['block'].append(lower_offset + i)
-
-        # Check if size sets address outside of current cache block.
-        if value_offset >= upper_offset:
-            # Size sets address outside of current cache block. Update additional blocks as needed.
-            # Start by getting our new size.
-            new_size = value_offset - upper_offset
-            if new_size < 0:
-                new_size = 0
-
-            # Pass our new line data.
-            new_line = ' L {0},{1}'.format(upper_offset, new_size)
-            cache, null, null, null = handle_line(cache, new_line, M, B, S, 0, 0, 0, False)
-
-        return (cache, hits, misses, evictions)
-
-
-def print_cache(cache):
-    """
-    Prints simulated cache in an easier to read format.
-    :param cache: Simulated cache to print.
-    """
-    print('\nCache:')
-    for set_index in range(len(cache)):
-        print('  Set {0}:'.format(set_index, cache[set_index]))
-        for line_index in range(len(cache[set_index])):
-            print('    Line {0}: {1}'.format(line_index, cache[set_index][line_index]))
-    print('')
+class CacheSimulator():
+    def __init__(self):
+        """
+        Class initialization.
+        """
+        self.s = None   # Number of bits to identify set.
+        self.S = None   # Number of sets in cache.
+        self.e = None   # Number of bits to identify line.
+        self.E = None   # Number of lines per set.
+        self.b = None   # Number of bits to identify block byte.
+        self.B = None   # Number of bytes in a block.
+        self.m = None   # Total number of identifying bits in address.
+        self.M = None   # Total addresses in cache.
+
+        self.cache = None
+        self.hits = None
+        self.misses = None
+        self.evictions = None
+        self.verbose = False
+
+    def main(self, s, E, b, file_name, verbose):
+        """
+        Program main.
+        """
+        print('\n')
+        print('main():')
+        print('    s: {0}'.format(s))
+        print('    E: {0}'.format(E))
+        print('    b: {0}'.format(b))
+        print('    file_name: {0}'.format(file_name))
+        print('    verbose: {0}'.format(verbose))
+        print('')
+
+        if verbose:
+            self.verbose = True
+
+        # Calculate total sizes of cache structures.
+        self.s = s
+        self.S = int(math.pow(2, self.s))
+        self.E = E
+        self.e = math.log2(self.E)
+        self.b = b
+        self.B = int(math.pow(2, self.b))
+        self.m = self.E * self.S
+        # M = total_bytes = int(math.pow(2, m))
+        self.M = int(math.pow(2, 64))
+
+        # Create cache object.
+        self.cache = []
+        for set in range(self.S):
+            self.cache.append([])
+
+            # Create lines in set.
+            for line in range(self.E):
+                self.cache[len(self.cache) - 1].append({
+                    'valid': False,
+                    'tag': 0,
+                    'block': [],
+                })
+
+        # Display initial cache.
+        self.print_cache()
+        print('\n\n')
+
+        # Run simulation on cache, using file.
+        self.run_simulation(file_name)
+
+
+    def run_simulation(self, file_name):
+        """
+        Runs full simulation of cache, using given input file.
+        :param file_name: Name of file to read from.
+        """
+        self.hits = 0
+        self.misses = 0
+        self.evictions = 0
+
+        # Open file to read in values.
+        with open(file_name) as file:
+            # Read in line.
+            for line in file:
+                self.handle_line(line)
+
+        print('hits:{0} misses:{1} evictions:{2}'.format(self.hits, self.misses, self.evictions))
+
+    def handle_line(self, line, main_cache_line=True):
+        """
+        Handles cache simulation for given line instruction.
+        :param line: Line to handle for.
+        :param main_cache_line: Bool to indicate if this is an initial cache line read in or a subquery from one.
+                Note that only initial cache line read ins will print or update missed/skipped/evicted variables.
+        """
+        split_line = re.split(r'[ ,\n]', line)
+
+        # Check if instruction load. If so, we can skip line.
+        if split_line[0] == 'I':
+            # Found instruction load. Returning.
+            return
+        else:
+            line = line.strip()
+
+            # Not an instruction load. Keep processing. Start by getting address.
+            address = int(split_line[2]) % self.M
+            value_size = int(split_line[3])
+
+            # Get mask values.
+            block_mask = self.B - 1
+            block_offset = self.b
+            set_mask = ((self.S << block_offset) - 1) ^ block_mask
+            set_offset = self.s
+            tag_mask = (self.M - 1) ^ set_mask ^ block_mask
+
+            # Use masks to get address chunks.
+            block = address & block_mask
+            set = (address & set_mask) >> block_offset
+            tag = (address & tag_mask) >> (block_offset + set_offset)
+
+            # We have our values. Now compare against the all entries in the cache.
+            for index in range(len(self.cache[set])):
+                # Check if match.
+                if self.cache[set][index]['valid'] == True and self.cache[set][index]['tag'] == tag:
+                    # Match found.
+                    if main_cache_line:
+                        # Is main cache file line. Update hits/misses/evictions.
+                        self.hits += 1
+
+                        # Print if verbose flag set.
+                        if self.verbose:
+                            print('{0} hit'.format(line))
+
+                    return
+                else:
+                    # Not a match. Examine why.
+                    if self.cache[set][index]['valid'] == False:
+                        # Cache location was not set. Cold miss. We can just set and return.
+                        if main_cache_line:
+                            # Is main cache file line. Update hits/misses/evictions.
+                            self.misses += 1
+
+                            # Print if verbose flag set.
+                            if self.verbose:
+                                print('{0} miss'.format(line))
+
+                        self.cache[set][index]['valid'] = True
+                        self.cache[set][index]['tag'] = tag
+
+                        # Set block values that are "loaded" into memory.
+                        lower_offset = int(address / self.B) * self.B
+                        upper_offset = (int(address / self.B) + 1) * self.B
+                        value_offset = address + value_size
+                        self.cache[set][index]['block'] = []
+                        for i in range(self.B):
+                            self.cache[set][index]['block'].append(lower_offset + i)
+
+                        # Check if size sets address outside of current cache block.
+                        if value_offset >= upper_offset:
+                            # Size sets address outside of current cache block. Update additional blocks as needed.
+                            # Start by getting our new size.
+                            new_size = value_offset - upper_offset
+                            if new_size < 0:
+                                new_size = 0
+
+                            # Pass our new line data.
+                            new_line = ' L {0},{1}'.format(upper_offset, new_size)
+                            self.handle_line(new_line, main_cache_line=False)
+
+                        return
+
+            # If we made it this far, cache locations were set, but tags did not match. Conflict miss.
+            if main_cache_line:
+                # Is main cache file line. Update hits/misses/evictions.
+                self.misses += 1
+                self.evictions += 1
+
+                # Print if verbose flag set.
+                if self.verbose:
+                    print('{0} miss eviction'.format(line))
+
+            self.cache[set][0]['tag'] = tag
+
+            # Set block values that are "loaded" into memory.
+            lower_offset = int(address / self.B) * self.B
+            upper_offset = (int(address / self.B) + 1) * self.B
+            value_offset = address + value_size
+            self.cache[set][0]['block'] = []
+            for i in range(self.B):
+                self.cache[set][0]['block'].append(lower_offset + i)
+
+            # Check if size sets address outside of current cache block.
+            if value_offset >= upper_offset:
+                # Size sets address outside of current cache block. Update additional blocks as needed.
+                # Start by getting our new size.
+                new_size = value_offset - upper_offset
+                if new_size < 0:
+                    new_size = 0
+
+                # Pass our new line data.
+                new_line = ' L {0},{1}'.format(upper_offset, new_size)
+                self.handle_line(new_line, main_cache_line=False)
+
+            return
+
+    def print_cache(self):
+        """
+        Prints simulated cache in an easier to read format.
+        """
+        print('\nCache:')
+        for set_index in range(len(self.cache)):
+            print('  Set {0}:'.format(set_index, self.cache[set_index]))
+            for line_index in range(len(self.cache[set_index])):
+                print('    Line {0}: {1}'.format(line_index, self.cache[set_index][line_index]))
+        print('')
 
 
 if __name__ == '__main__':
@@ -252,6 +249,6 @@ if __name__ == '__main__':
     args = parser.parse_args()
 
     # If args parsed properly, run program main.
-    main(int(args.set_count), int(args.line_count), int(args.block_size), args.file_name, args.verbose)
+    CacheSimulator().main(int(args.set_count), int(args.line_count), int(args.block_size), args.file_name, args.verbose)
 
     print('Terminating program.')