import numpy as np

# Scalar RNN forward pass reproducing the lecture's worked example.
# h_t = tanh(Wh*h_{t-1} + Wx*x_t)   y_t = Wy*h_t
# Wh, Wx, Wy are SHARED across every timestep -- that's the whole point.

Wh, Wx, Wy = 0.5, 0.8, 1.0
h0 = 0.0
xs = [1.0, 0.5, -0.3, 0.2]          # x1..x4

h = h0
hidden_states = []
for t, x in enumerate(xs, start=1):
    h = np.tanh(Wh * h + Wx * x)
    hidden_states.append(h)
    print(f"h{t} = tanh({Wh}*h{t-1} + {Wx}*{x}) = {h:.4f}")

ys = [Wy * h for h in hidden_states]
print("Outputs y1..y4:", [f"{y:.4f}" for y in ys])

# ---- weight sharing: parameter count is independent of sequence length ----
n_params_rnn = 3                     # Wh, Wx, Wy -- fixed, regardless of T
n_params_naive_ff = 3 * len(xs)      # if every timestep had its OWN weights
print(f"RNN params: {n_params_rnn} (shared across all {len(xs)} steps)")
print(f"Naive per-timestep FF params: {n_params_naive_ff} (grows with sequence length)")
