On emulating the Texas Instruments random number generator

I recently needed to emulate the random number generator internally used by Texas Instruments calculators in Mathematica. Thanks to this United-TI forum post, I found that the algorithm being used is a combined multiplicative linear congruential generator due to Pierre L’Ecuyer. The particular generator used is listed on page 747 of the article; as noted there, the combined MLCG has a period of $2305842648436451838\approx 2.30584\times10^{18}$.

I figured this might be useful to other people, so I’m posting the routines needed to get RandomReal[] and related functions to emulate the rand() function (and ilk) on TI calculators:

TexasInstruments /: RandomInitializeGenerator[TexasInstruments, ___] := TexasInstruments[12345, 67890]
 TexasInstruments[___]["GeneratesRealsQ"] := True; TexasInstruments[___]["GeneratesIntegersQ"] := True;

TexasInstruments[___]["SeedGenerator"[seed_]] := If[seed == 0, TexasInstruments[12345, 67890], TexasInstruments[Mod[40014 seed, 2147483563], Mod[seed, 2147483399]]];

TexasInstruments[s1_, s2_]["GenerateReals"[n_, {a_, b_}, prec_]] := Module[{p = s1, q = s2, temp}, {a + (b - a) Table[p = Mod[40014 p, 2147483563]; q = Mod[40692 q, 2147483399]; temp = (p - q)/2147483563; If[temp < 0, temp += 1]; temp, {n}], TexasInstruments[p, q]} ]

TexasInstruments[s1_, s2_]["GenerateIntegers"[n_, {a_, b_}]] := Module[{p = s1, q = s2, temp}, {a + Floor[(b - a + 1) Table[p = Mod[40014 p, 2147483563]; q = Mod[40692 q, 2147483399]; temp = (p - q)/2147483563; If[temp < 0, temp += 1]; temp, {n}]], TexasInstruments[p, q]} ]

This is based on the PRNG method plug-in framework in Mathematica that is described here. For instance, here is the Mathematica equivalent of executing 0→rand:rand(10) (for Zilog Z80 calculators like the TI-83 Plus) or RandSeed 0:rand() (for Motorola 68k calculators like the TI-89):

(* 0→rand:rand(10) *) BlockRandom[SeedRandom[0, Method -> TexasInstruments]; RandomReal[{0, 1}, 10, WorkingPrecision -> 20]]

Here’s corresponding code for randInt() and randM() on the Z80 calculators:

(* 0->rand:randInt(1,10,10) *) BlockRandom[SeedRandom[0, Method -> TexasInstruments]; RandomInteger[{1, 10}, 10]]

(* 0->rand:randM(3,3) *) BlockRandom[SeedRandom[0, Method -> TexasInstruments]; Reverse[Reverse /@ RandomInteger[{-9, 9}, {3, 3}]]]`

Due to differences in precision, there is bound to be some discrepancy in the least significant figures of the results from the calculators and the results from the Mathematica emulation; still, I believe that this is a serviceable fake.

You can download a Mathematica notebook containing these definitions and tests here.