Epoch-shuffled sampler matching the Python reference semantics.
A deterministic shuffle is generated per epoch; incomplete epoch tails are
padded with least-seen IDs so every returned minibatch has minibatch_size IDs.
Summary
Types
@type t() :: %GEPA.Strategies.BatchSampler.EpochShuffled{ current_position: non_neg_integer(), epoch: integer(), id_freqs: %{required(term()) => pos_integer()}, last_trainset_size: non_neg_integer(), minibatch_size: pos_integer(), rng_state: :rand.state(), seed: integer(), shuffled_ids: [term()] }