import json import logging import argparse import sys import os import math import pickle from deep_translator import GoogleTranslator from gematria import calculate_gematria from collections import defaultdict # --- Configuration --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') BOOK_RANGE = range(1, 40) CACHE_FILE = "tanakh_phrasedict.cache" # --- Core Functions --- def get_power_result(total_sum, query_value): """ Calculates the power or root result. - If query_value <= total_sum, it finds the highest power of query_value that is <= total_sum. - If query_value > total_sum, it finds the smallest n-th root of query_value whose result is <= total_sum. """ if total_sum <= 0 or query_value <= 1: return 1 # Normal case: "Power Mode" if query_value <= total_sum: try: exponent = int(math.floor(math.log(total_sum, query_value))) return query_value ** exponent except (ValueError, OverflowError): return 1 # Special case: "Root Mode" else: # query_value > total_sum # Find the smallest integer root 'n' (starting from 2) # that reduces query_value to a number <= total_sum. for n in range(2, 65): # Limit the search to a reasonable range (up to the 64th root) try: root_result = query_value ** (1.0 / n) if root_result <= total_sum: # We found the smallest root exponent n. # Round up and return as an integer. return math.ceil(root_result) except (ValueError, OverflowError): # Catches math errors with extreme numbers return 1 # If even the 64th root is too large, which only happens with # extreme number ratios, return a default value. return 1 def load_phrase_dictionary(): if not os.path.exists(CACHE_FILE): sys.exit(f"ERROR: Cache file '{CACHE_FILE}' not found. Please run 'build_indices.py' first to create the index.") logging.info(f"Loading phrase dictionary from cache: {CACHE_FILE}") try: with open(CACHE_FILE, 'rb') as f: return pickle.load(f) except Exception as e: sys.exit(f"ERROR: Cache file '{CACHE_FILE}' is corrupt. Please delete it and run 'build_indices.py' again. Error: {e}") def find_all_matching_phrases(target_sum, phrase_dictionary): return phrase_dictionary.get(int(target_sum), []) # --- Main Program --- def main(args): phrase_dictionary = load_phrase_dictionary() query_value = calculate_gematria(args.query) if query_value <= 1: sys.exit(f"ERROR: Query '{args.query}' has an invalid Gematria value ({query_value}).") translator = None if args.translate: try: # The correct code for Hebrew is 'iw' translator = GoogleTranslator(source='iw', target='en') except Exception as e: logging.error(f"Could not initialize translator: {e}") logging.info(f"Starting oracle analysis for '{args.query}' (G:{query_value}) with bitplane variation depth {args.xor_depth}...") print("\n" + "="*20 + f" ORACLE ANSWERS FOR '{args.query}' " + "="*20) verses_processed = 0 resonance_count = 0 for book_num in BOOK_RANGE: if args.process_verses and verses_processed >= args.process_verses: break filepath = f"texts/torah/{book_num:02}.json" try: with open(filepath, 'r', encoding='utf-8') as file: data = json.load(file) for chap_idx, chapter in enumerate(data.get("text", []), start=1): if args.process_verses and verses_processed >= args.process_verses: break for verse_idx, verse_text in enumerate(chapter, start=1): if args.process_verses and verses_processed >= args.process_verses: break verses_processed += 1 verse_sum = calculate_gematria(verse_text) if verse_sum <= 1: continue power_result = get_power_result(verse_sum, query_value) # Calculate the main result first main_target_sum = verse_sum ^ power_result main_matches = find_all_matching_phrases(main_target_sum, phrase_dictionary) # Only proceed if a main resonance exists if not main_matches: continue resonance_count += 1 verse_ref = f"B{book_num:02d}, C{chap_idx}, V{verse_idx}" print(f"\n--- Resonance #{resonance_count} in [{verse_ref}] (G_sum:{verse_sum}) ---") print(f"Original Verse: {verse_text.strip()}") def print_matches(matches, title, calculation_str): if not matches: return matches.sort(key=lambda p: (p.get('freq', 0) / p.get('words', 99)), reverse=True) matches_to_show = matches[:args.results_per_verse] print(f" ↳ {title}: {calculation_str}") for match in matches_to_show: translation_str = "" if translator: try: translation_str = translator.translate(match['text']) except Exception: translation_str = "[Translation failed]" score = (match.get('freq', 0) / match.get('words', 99)) info = f"(Words: {match.get('words', 'N/A')}, Freq: {match.get('freq', 'N/A')}, Score: {score:.2f})" print(f" - {match['text']} {info}") if translation_str: print(f" ↳ Interpretation: \"{translation_str}\"") # 1. Display the main resonance calc_str = f"[{verse_sum}] ^ [{power_result}] → [G_target:{main_target_sum}]" print_matches(main_matches, "Main Resonance", calc_str) # 2. Display the bitplane variations of the RESULT if args.xor_depth > 0: print(f" [INFO] Bitplane Variations of the Result ({main_target_sum}):") for depth in range(args.xor_depth): bit_flip = 1 << depth # Flip the 'd'-th bit in the main result target_sum = main_target_sum ^ bit_flip bitplane_matches = find_all_matching_phrases(target_sum, phrase_dictionary) if bitplane_matches: # FIX: The label is now depth + 1 for human readability bitplane_calc_str = f"[{main_target_sum}] ^ [Bit {depth+1}] → [G_target:{target_sum}]" print_matches(bitplane_matches, f"Variation (Depth {depth + 1})", bitplane_calc_str) except FileNotFoundError: continue logging.info(f"Analysis complete. Found {resonance_count} resonance groups in {verses_processed} analyzed verses.") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Tanakh Universal Resonance Analyzer with Bitplane Variations.") parser.add_argument("query", type=str, help="The query phrase (e.g., 'יהוה').") parser.add_argument("--translate", action="store_true", default=True, help="Enable automatic translation of results to English.") parser.add_argument("--process-verses", type=int, default=10, help="Maximum number of starting verses to analyze.") parser.add_argument("--results-per-verse", type=int, default=1, help="Maximum oracle answers to show per resonance type (default: 3).") parser.add_argument("--xor-depth", type=int, default=2, help="Maximum depth for bitplane variations of the result (0-15) (default: 16).") args = parser.parse_args() main(args)