Deriving numbers
A source returns bytes, and that is the whole contract. Everything else, integers in a range, draws without replacement, shuffles, is derived by the package.
Not to keep drivers small. Because the derivation is where the bug lives, and it is a silent one.
Modulo bias
$byte % 78 looks like it gives a card from a deck of 78, and it does, but not evenly.
256 is not a multiple of 78. Values 0 to 233 cover the deck three times over, and the leftover 234 to 255 land on the first 22 cards a fourth time. So a third of the deck comes up 33% more often than the rest, forever.
Nothing throws. Any test that checks the range passes. It only shows up if you count.
Rejection sampling
So integers go through rejection sampling, once, in one place, rather than in every driver that would have had to rediscover it. Draws that land in the uneven tail are thrown away and redrawn.
The test for it feeds every byte value from 0 to 255 through and asserts each of the 78 outcomes appears exactly three times. Not approximately, exactly.
Rejection costs a redraw on 8.6% of bytes for a deck of 78. That is the price of the answer being right.
Shuffles
Fisher-Yates, walking down and swapping with a position at or below the current one.
Not the tempting version that swaps with any position at all. That one spreads n^n paths over n! outcomes, and the two do not divide: some permutations come up more often than others, for every n above 2. It is the same class of mistake as the modulo, and just as invisible.
Draws without replacement
distinct() switches strategy on the ratio it is given.
Asking for a sliver of a wide range draws and discards repeats. Asking for most of the range shuffles the range instead, because the last few values of 78 out of 78 need some 78 draws each, and rejection sampling would spend around 390 draws where a shuffle spends 78.
You do not choose between them. The ratio does.