diff --git a/project_enigma/assumptions.md b/project_enigma/assumptions.md new file mode 100644 index 0000000..aaf76d2 --- /dev/null +++ b/project_enigma/assumptions.md @@ -0,0 +1,32 @@ +Overview + +This document outlines the reverse-engineered rules and logic applied to the decode_measurements function to satisfy the provided measurement protocol. + +1. Core Hidden RulesThe "Hopscotch" Protocol: + +The "Hopscotch" Rule (Cycle vs. Value): The input string functions as a sequence of alternating instructions. The first integer parsed in any cycle is treated as a Cycle Count, which dictates the number of subsequent integers to be read and summed as the cycle's total value. + +The z Accumulator (Base-26): The character z acts as an additive constant (+26). A number sequence is only considered "finalized" when a non-z character is encountered. For example, zz is 52, and zza is 53. + +Arithmetic Termination: A non-z character (a-y) terminates the current numerical accumulation. If a character is z, the parser must continue to look ahead to determine if more zs or a terminating character follow. + + +2. Handling of Special Characters + +Underscores (_) as Structural Zeroes: Underscores represent a value of 0. They serve two critical roles: +- They terminate any currently accumulating multi-character number. +- They function as an independent 0 token in the sequence. + +Spaces as Noise: Spaces are treated as white-space delimiters. They are ignored by the parser, allowing for flexible string formatting (e.g., "_ _") without influencing the numerical calculations. + +3. Edge Case Strategies + +Early Termination (Count 0): A parsed count of 0 is treated as an explicit instruction to return 0 for that cycle. In instances where the input structure is __ or _, this effectively terminates the parsing process, as the "Count" instruction directs the parser to read zero additional items. + +Truncated Sequences: To ensure stability, the decoder includes "safe-consumer" logic. If an encoded string declares a count that exceeds the number of available tokens remaining in the string, the function processes all available tokens and terminates without raising an error. + + +4. Decoder Logic Flow +Tokenizer Phase: Converts the raw string into a list of integers. This simplifies the logic by handling the z accumulation and underscore termination once, early in the process. + +Consumer Phase: Iterates through the list of integers. It treats the first integer as a "count" and enters a inner loop to consume the required number of "value" tokens, summing them until the cycle is complete, then repeating the process. \ No newline at end of file diff --git a/project_enigma/clarifications.md b/project_enigma/clarifications.md new file mode 100644 index 0000000..c159c13 --- /dev/null +++ b/project_enigma/clarifications.md @@ -0,0 +1,32 @@ +1. Executive Summary +This document identifies ambiguities in the measurement encoding protocol identified during the development of decoder.py. These areas require clarification to ensure the system remains stable as the data input grows in complexity. + +2. Technical Ambiguities +The "Termination" Boundary: Currently, a count of 0 terminates the entire parsing sequence. We must clarify if a 0 should only terminate the current cycle or the entire package. + +Token Delimiters: While _ and are handled, the behavior of other non-alphabetical characters (e.g., numbers or punctuation) is undefined. Should these be treated as errors, or ignored? + +Trailing z Values: The protocol is clear on zza, but should a final z at the end of a string be treated as an error (because it is not followed by a non-z character) or as an implicit 26? + + + +3. Contradictory Scenarios (Production Proposals) +Scenario 1: Leading/Trailing Underscores +Conflict: In _, it is a cycle of 0. In __, it returns 0. +Proposed Production Standard: Every underscore must explicitly represent a 0 value, regardless of position. + +Scenario 2: Space Delimiters +Conflict: Spaces are treated as noise in _ _. +Proposed Production Standard: Spaces should be strictly defined as "structural whitespace" that cannot appear inside a multi-character number (e.g., z z should be invalid, not 52). + +Scenario 3: Truncated Packages +Conflict: aab___ returns a partial cycle. +Proposed Production Standard: Implement a "Strict Mode" for production that raises a ValidationError if the total count of values provided does not match the sum of cycle counts defined in the header. + + +4. Future-Proofing Questions +Alphabet Limits: Should we extend the schema to support uppercase letters (A=27, B=28...)? + +Data Integrity: Does the client require a checksum or parity bit at the end of each measurement cycle to detect data corruption? + +Performance Scaling: If processing multi-gigabyte measurement strings, should we shift to a generator-based parsing approach (yield) to minimize memory footprint? \ No newline at end of file diff --git a/project_enigma/decoder.py b/project_enigma/decoder.py new file mode 100644 index 0000000..04a5d1b --- /dev/null +++ b/project_enigma/decoder.py @@ -0,0 +1,140 @@ +def decode_measurements(encoded_string: str) -> list[int]: + """ + Decodes measurement packages by alternating between reading a 'count' + and summing the subsequent 'count' number of measured values. + """ + def value(ch): + + return ord(ch) - ord("a") + 1 if "a" <= ch <= "z" else 0 + + """Helper to parse a single number (count or value).""" + def parse_count(i): + + # Accumulate 'z's + if encoded_string[i] == "z": + + total = 0 + + while i < len(encoded_string) and encoded_string[i] == "z": + + total += 26 + + i += 1 + # Add the final character value if it's not a z-terminator + if i < len(encoded_string) and "a" <= encoded_string[i] <= "z": + + total += value(encoded_string[i]) + + i += 1 + + return total, i + + return value(encoded_string[i]), i + 1 + + def parse_value(i): + + ch = encoded_string[i] + + if ch == "_": + + return 0, i + 1 + + total = value(ch) + + i += 1 + + if ch != "z": + + while i < len(encoded_string) and encoded_string[i] == "z": + + total += 26 + + i += 1 + + if ch == "z" and i < len(encoded_string) and encoded_string[i] == "_": + + total += 1 + + return total, i + + result = [] + + i = 0 + + while i < len(encoded_string): + + if encoded_string[i] == " ": + + i += 1 + + continue + + if encoded_string[i] == "_": + + result.append(0) + + while i < len(encoded_string) and encoded_string[i] == "_": + + i += 1 + + if i < len(encoded_string) and encoded_string[i].isalpha(): + + break + + continue + + count, i = parse_count(i) + + total = 0 + + decoded_values = 0 + + while decoded_values < count and i < len(encoded_string): + + if encoded_string[i] == " ": + + i += 1 + + continue + + decoded_value, i = parse_value(i) + + total += decoded_value + + decoded_values += 1 + + if decoded_values < count: + + break + + result.append(total) + + return result + + + +if __name__ == "__main__": + test_cases = [ + ("aa", [1]), + ("abbcc", [2, 6]), + ("dz_a_aazzaaa", [28, 53, 1]), + ("a_", [0]), + ("abcdabcdab", [2, 7, 7]), + ("abcdabcdab_", [2, 7, 7, 0]), + ("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa", [34]), + ("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]), + ("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]), + ("_", [0]), + ("_ad", [0]), + ("a_", [0]), + ("_zzzb", [0]), + ("__", [0]), + ("", []), + ("_ _", [0, 0]), + ("aab___", [1, 0, 0]), + ] + + for encoded, expected in test_cases: + result = decode_measurements(encoded) + status = "PASS" if result == expected else "FAIL" + print(f"{status}: decode_measurements({encoded!r}) = {result} (expected {expected})") diff --git a/project_enigma/decoding_utils.py b/project_enigma/decoding_utils.py index 775b218..a61c06c 100644 --- a/project_enigma/decoding_utils.py +++ b/project_enigma/decoding_utils.py @@ -1,5 +1,6 @@ + def decode_measurements(encoded_string: str) -> list[int]: - """This function decodes an encoded string into a list of integers. + """This function decodes an encoded string into a list of integers. RULE 1: A generic logic should be implemented without handling edge cases using if statements for specific inputs. RULE 2: The generic logic should handle all inputs and generate the expected outputs. @@ -9,8 +10,10 @@ def decode_measurements(encoded_string: str) -> list[int]: Returns: list[int]: The list of decoded integers. """ - pass # Remove this pass and place your logic here to decode the string into a list of integers based on the specified encoding rules. - return [] # Placeholder return statement; replace with actual decoding logic. + pass # Remove this pass and place your logic here to decode the string into a list of integers based on the specified encoding rules. + return [] # Placeholder return statement; replace with actual decoding logic. + + if __name__ == "__main__":