function [Xn, mu, sigma] = preprocess_data(X, mu, sigma)
% PREPROCESS_DATA  Normalize feature matrix using z-score scaling
%
%   [Xn, mu, sigma] = preprocess(X)
%   [Xn, mu, sigma] = preprocess(X, mu, sigma)
%
%   Applies feature-wise standardization (zero mean, unit variance)
%   to the predictors only — labels Y remain unchanged.

    if nargin < 2 || isempty(mu)
        mu = mean(X,1);
        sigma = std(X,[],1);
    end
    sigma(sigma==0) = 1; % avoid division by zero

    Xn = (X - mu) ./ sigma;
end
