MersenneTwister: simplify seeding logic (#60204)
Since at least v0.3, `MersenneTwister` has stored the user-provided
seed. More recently, this allowed `show` to print a round-trippable
representation of the RNG. However, storing an arbitrary deep-copied
user seed is not ideal, and the initialization logic has become more
complicated than necessary.
In particular, `MersenneTwister()` currently performs three steps:
1. `seed = rand(RandomDevice(), UInt128)` # generate a seed
2. `initstate = rand(SeedHasher(seed), UInt32, 8)` # expand it
3. initialize dSFMT with `initstate`
This commit merges the first two steps. The new initialization is:
1. `initstate = rand(RandomDevice(), NTuple{2, UInt128})`
2. convert `initstate` to the dSFMT format and initialize the state
We now store only `initstate` for use in `show`, preserving
round-trippability. For user-provided seeds, `initstate` is derived from
the seed via `SeedHasher`.
A small bonus is that `MersenneTwister()` is no longer restricted to
"only" 2^128 distinct initializations.
The cosmetic drawback is that the printed representation is less pretty:
`MersenneTwister(1)` now shows as
```
MersenneTwister(0xc53bc12f..., 0x50a4aa15...)
```