"""Lab to Code — Is your process out of control? An individuals control chart.

Same bottle filler as the Cp/Cpk video: target 500 mL. 30 bottles in a row (simulated with a fixed seed).
The first 20 set the limits; after bottle 20 the machine drifts up by 2 mL.
Limits for an individuals chart: center = mean, UCL/LCL = mean +/- 2.66 * average moving range.
"""
import numpy as np

rng = np.random.default_rng(3)
fills = np.round(np.concatenate([rng.normal(500.0, 1.0, 20),
                                 rng.normal(502.0, 1.0, 10)]), 1)

base = fills[:20]                        # limits come from the stable period
center = base.mean()
mr_bar = np.abs(np.diff(base)).mean()    # average moving range
ucl = center + 2.66 * mr_bar
lcl = center - 2.66 * mr_bar
print(f"center = {center:.2f}  UCL = {ucl:.2f}  LCL = {lcl:.2f}")

# Rule 1: a point outside the limits
out = [i + 1 for i, x in enumerate(fills) if max(x, ucl) != ucl or min(x, lcl) != lcl]
print("outside the limits:", out)

# Rule 2: eight points in a row on the same side of the center line
side = np.sign(fills - center)
for i in range(len(fills) - 7):
    if abs(side[i:i + 8].sum()) == 8:
        print("8 in a row on one side, ending at bottle", i + 8)
        break
