Problem: discrete_logarithm

Your task is to implement a solver for the Discrete Logarithm Problem and verify its correctness in Lean. Given a base g, a target h, and a modulus p, find the smallest non-negative integer x < p such that g^x ≡ h (mod p), or return none if no such x exists. Algorithm: Use a naive linear search. Iterate `x` from 0 to `p-1`. Maintain a running product for `g^x % p` to avoid recomputing the power from scratch. Minimality: If multiple solutions exist, you must return the smallest `x`. Unsolvable Case: If no such `x` exists in the range `[0, p)`, return `none`. Goal: Find a non-negative integer `x` (where `0 ≤ x < p`) such that `(g.toNat ^ x) % p.toNat = h.toNat % p.toNat`. Preconditions: * Inputs: The generator `g`, target `h`, and modulus `p` are `UInt64`. * Constraint: `p.toNat > 1`. Postconditions: Implement `discrete_log_naive` which returns `Option UInt64`. Verification Challenges: (1) Incremental Modular Exponentiation: You are not calling a power function inside the loop; you are updating `current_val = (current_val * g) % p`. You must prove the invariant that at iteration `i`, `current_val == (Nat.pow g i) % p`. (2) Negative Proof (None): The hardest part is the `none` case. If the loop finishes without returning, you must prove the universal quantifier: `for all k < p, (Nat.pow g k) % p \ne h % p`. (3) Minimality (Some): When you return `some x`, you must prove that no earlier index `k < x` was a solution. Since your loop searches strictly from 0 upwards, this follows from the control flow, but you must capture it in the loop invariant ("for all checked k, g^k % p\ne h % p").

import Mathlib

-- Precondition definitions
@[reducible, simp]
def discrete_log_naive_precond (g h p : UInt64) : Prop :=
  -- !benchmark @start precond
  p.toNat > 1
  -- !benchmark @end precond

def is_discrete_log (g h p : Nat) (x : Nat) : Prop :=
  (Nat.pow g x) % p = h % p

-- !benchmark @start auxcode
-- !benchmark @end auxcode

-- Main function definitions
def discrete_log_naive (g h p : UInt64) (h_precond : discrete_log_naive_precond g h p) : Option UInt64 :=
  -- !benchmark @start code
  sorry
  -- !benchmark @end code

-- Postcondition definitions
@[reducible, simp]
def discrete_log_naive_postcond (g h p : UInt64) (result : Option UInt64) (h_precond : discrete_log_naive_precond g h p) : Prop :=
  -- !benchmark @start postcond
  match result with
  | some x => is_discrete_log g.toNat h.toNat p.toNat x.toNat ∧ x.toNat < p.toNat ∧
               ∀ k : Nat, k < x.toNat → ¬ is_discrete_log g.toNat h.toNat p.toNat k
  | none => ∀ k : Nat, k < p.toNat → ¬ is_discrete_log g.toNat h.toNat p.toNat k
  -- !benchmark @end postcond

-- !benchmark @start lemma
-- !benchmark @end lemma

-- Proof content
theorem discrete_log_naive_postcond_satisfied (g h p : UInt64) (h_precond : discrete_log_naive_precond g h p) :
    discrete_log_naive_postcond g h p (discrete_log_naive g h p h_precond) h_precond := by
  -- !benchmark @start proof
  sorry
  -- !benchmark @end proof