(* ************************************************************
   Дополнительные алгоритмы генераторов какбыслучайных чисел.
   Вадим Исаев, 2020

   Additional generators of pseudorandom numbers.
   Vadim Isaev, 2020
   ************************************************************ *)

MODULE RandExt;

IMPORT HOST, MathRound, MathBits;

CONST
  (* Для алгоритма Мерсена-Твистера *)
  N          = 624;
  M          = 397;
  MATRIX_A   = 9908B0DFH;  (* constant vector a *)
  UPPER_MASK = 80000000H;  (* most significant w-r bits *)
  LOWER_MASK = 7FFFFFFFH;  (* least significant r bits *)
  INT_MAX    = 4294967295;


TYPE
(* структура служебных данных, для алгоритма mrg32k3a *)
  random_t = RECORD
    mrg32k3a_seed       : REAL;
    mrg32k3a_x          : ARRAY 3 OF REAL;
    mrg32k3a_y          : ARRAY 3 OF REAL
  END;

  (* Для алгоритма Мерсена-Твистера *)
  MTKeyArray = ARRAY N OF INTEGER;

VAR
  (* Для алгоритма mrg32k3a *)
  prndl: random_t;
  (* Для алгоритма Мерсена-Твистера *)
  mt  : MTKeyArray;  (* the array for the state vector *)
  mti : INTEGER;     (* mti == N+1 means mt[N] is not initialized *)

(* ---------------------------------------------------------------------------
   Генератор какбыслучайных чисел в диапазоне [a,b].
   Алгоритм 133б из книги "Агеев и др. - Бибилотека алгоритмов 101б-150б",
   стр. 53.
   Переделка из Algol на Oberon и доработка, Вадим Исаев, 2020

   Generator pseudorandom numbers, algorithm 133b from
   Comm ACM 5,10 (Oct 1962) 553.
   Convert from Algol to Oberon Vadim Isaev, 2020.

   Входные параметры:
     a - начальное вычисляемое значение, тип REAL;
     b - конечное вычисляемое значение, тип REAL;
     seed - начальное значение для генерации случайного числа.
            Должно быть в диапазоне от 10 000 000 000 до 34 359 738 368 (2^35),
            нечётное.
   --------------------------------------------------------------------------- *)
PROCEDURE alg133b* (a, b: REAL; VAR seed: INTEGER): REAL;
CONST
  m35 = 34359738368;
  m36 = 68719476736;
  m37 = 137438953472;

VAR
  x: INTEGER;
BEGIN
  IF seed # 0 THEN
    IF  (seed MOD 2 = 0) THEN
      seed := seed + 1
    END;
    x:=seed;
    seed:=0;
  END;

  x:=5*x;
  IF x>=m37 THEN
    x:=x-m37
  END;
  IF x>=m36 THEN
    x:=x-m36
  END;
  IF x>=m35 THEN
    x:=x-m35
  END;

  RETURN FLT(x) / FLT(m35) * (b - a) + a
END alg133b;

(* ----------------------------------------------------------
   Генератор почти равномерно распределённых
   какбыслучайных чисел mrg32k3a
   (Combined Multiple Recursive Generator) от 0 до 1.
   Период повторения последовательности = 2^127

   Generator pseudorandom numbers,
   algorithm mrg32k3a.

   Переделка из FreePascal на Oberon, Вадим Исаев, 2020
   Convert from FreePascal to Oberon, Vadim Isaev, 2020
   ---------------------------------------------------------- *)
(* Инициализация генератора.

   Входные параметры:
     seed  - значение для инициализации. Любое. Если передать
             ноль, то вместо ноля будет подставлено кол-во
             процессорных тиков. *)
PROCEDURE mrg32k3a_init* (seed: REAL);
BEGIN
  prndl.mrg32k3a_x[0] := 1.0;
  prndl.mrg32k3a_x[1] := 1.0;
  prndl.mrg32k3a_y[0] := 1.0;
  prndl.mrg32k3a_y[1] := 1.0;
  prndl.mrg32k3a_y[2] := 1.0;

  IF seed # 0.0 THEN
    prndl.mrg32k3a_x[2] := seed;
  ELSE
    prndl.mrg32k3a_x[2] := FLT(HOST.GetTickCount());
  END;

END mrg32k3a_init;

(* Генератор какбыслучайных чисел от 0.0 до 1.0. *)
PROCEDURE mrg32k3a* (): REAL;

CONST
  (* random MRG32K3A algorithm constants *)
  MRG32K3A_NORM = 2.328306549295728E-10;
  MRG32K3A_M1   = 4294967087.0;
  MRG32K3A_M2   = 4294944443.0;
  MRG32K3A_A12  = 1403580.0;
  MRG32K3A_A13  = 810728.0;
  MRG32K3A_A21  = 527612.0;
  MRG32K3A_A23  = 1370589.0;
  RAND_BUFSIZE  = 512;

VAR

  xn, yn, result: REAL;

BEGIN
  (* Часть 1 *)
  xn := MRG32K3A_A12 * prndl.mrg32k3a_x[1] - MRG32K3A_A13 * prndl.mrg32k3a_x[2];
  xn := xn - MathRound.trunc(xn / MRG32K3A_M1) * MRG32K3A_M1;
  IF xn < 0.0 THEN
    xn := xn + MRG32K3A_M1;
  END;

  prndl.mrg32k3a_x[2] := prndl.mrg32k3a_x[1];
  prndl.mrg32k3a_x[1] := prndl.mrg32k3a_x[0];
  prndl.mrg32k3a_x[0] := xn;

  (* Часть 2 *)
  yn := MRG32K3A_A21 * prndl.mrg32k3a_y[0] - MRG32K3A_A23 * prndl.mrg32k3a_y[2];
  yn := yn - MathRound.trunc(yn / MRG32K3A_M2) * MRG32K3A_M2;
  IF yn < 0.0 THEN
    yn := yn + MRG32K3A_M2;
  END;

  prndl.mrg32k3a_y[2] := prndl.mrg32k3a_y[1];
  prndl.mrg32k3a_y[1] := prndl.mrg32k3a_y[0];
  prndl.mrg32k3a_y[0] := yn;

  (* Смешение частей *)
  IF xn <= yn THEN
    result := ((xn - yn + MRG32K3A_M1) * MRG32K3A_NORM)
  ELSE
    result := (xn - yn) * MRG32K3A_NORM;
  END;

  RETURN result
END mrg32k3a;


(* -------------------------------------------------------------------
    Генератор какбыслучайных чисел, алгоритм Мерсена-Твистера (MT19937).
    Переделка из Delphi в Oberon Вадим Исаев, 2020.

    Mersenne Twister Random Number Generator.

    A C-program for MT19937, with initialization improved 2002/1/26.
    Coded by Takuji Nishimura and Makoto Matsumoto.

    Adapted for DMath by Jean Debord - Feb. 2007
    Adapted for Oberon-07 by Vadim Isaev - May 2020
  ------------------------------------------------------------ *)
(* Initializes MT generator with a seed *)
PROCEDURE InitMT(Seed : INTEGER);
VAR
  i : INTEGER;
BEGIN
  mt[0] := MathBits.iand(Seed, INT_MAX);
  FOR i := 1 TO N-1 DO
      mt[i] := (1812433253 * MathBits.ixor(mt[i-1], LSR(mt[i-1], 30)) + i);
        (* See Knuth TAOCP Vol2. 3rd Ed. P.106 For multiplier.
          In the previous versions, MSBs of the seed affect
          only MSBs of the array mt[].
          2002/01/09 modified by Makoto Matsumoto *)
      mt[i] := MathBits.iand(mt[i], INT_MAX);
        (* For >32 Bit machines *)
  END;
  mti := N;
END InitMT;

(* Initialize MT generator with an array InitKey[0..(KeyLength - 1)] *)
PROCEDURE InitMTbyArray(InitKey : MTKeyArray; KeyLength : INTEGER);
VAR
  i, j, k, k1 : INTEGER;
BEGIN
  InitMT(19650218);

  i := 1;
  j := 0;

  IF N > KeyLength THEN
    k1 := N
  ELSE
    k1 := KeyLength;
  END;

  FOR k := k1 TO 1 BY -1 DO
    (* non linear *)
    mt[i] := MathBits.ixor(mt[i], (MathBits.ixor(mt[i-1], LSR(mt[i-1], 30)) * 1664525)) + InitKey[j] + j;
    mt[i] := MathBits.iand(mt[i], INT_MAX); (* for WORDSIZE > 32 machines *)
    INC(i);
    INC(j);
    IF i >= N THEN
      mt[0] := mt[N-1];
      i := 1;
    END;
    IF j >= KeyLength THEN
      j := 0;
    END;
  END;

  FOR k := N-1 TO 1 BY -1 DO
    (* non linear *)
    mt[i] := MathBits.ixor(mt[i], (MathBits.ixor(mt[i-1], LSR(mt[i-1], 30)) * 1566083941)) - i;
    mt[i] := MathBits.iand(mt[i], INT_MAX); (* for WORDSIZE > 32 machines *)
    INC(i);
    IF i >= N THEN
      mt[0] := mt[N-1];
      i := 1;
    END;
  END;

  mt[0] := UPPER_MASK; (* MSB is 1; assuring non-zero initial array *)

END InitMTbyArray;

(* Generates a integer Random number on [-2^31 .. 2^31 - 1] interval *)
PROCEDURE IRanMT(): INTEGER;
VAR
  mag01 : ARRAY 2 OF INTEGER;
  y,k   : INTEGER;
BEGIN
  IF mti >= N THEN  (* generate N words at one Time *)
    (* If IRanMT() has not been called, a default initial seed is used *)
    IF mti = N + 1 THEN
      InitMT(5489);
    END;

    FOR k := 0 TO (N-M)-1 DO
      y := MathBits.ior(MathBits.iand(mt[k], UPPER_MASK), MathBits.iand(mt[k+1], LOWER_MASK));
      mt[k] := MathBits.ixor(MathBits.ixor(mt[k+M], LSR(y, 1)), mag01[MathBits.iand(y, 1H)]);
    END;

    FOR k := (N-M) TO (N-2) DO
      y := MathBits.ior(MathBits.iand(mt[k], UPPER_MASK), MathBits.iand(mt[k+1], LOWER_MASK));
      mt[k] := MathBits.ixor(mt[k - (N - M)], MathBits.ixor(LSR(y, 1), mag01[MathBits.iand(y, 1H)]));
    END;

    y := MathBits.ior(MathBits.iand(mt[N-1], UPPER_MASK), MathBits.iand(mt[0], LOWER_MASK));
    mt[N-1] := MathBits.ixor(mt[M-1], MathBits.ixor(LSR(y, 1), mag01[MathBits.iand(y, 1H)]));

    mti := 0;
  END;

  y := mt[mti];
  INC(mti);

  (* Tempering *)
  y := MathBits.ixor(y, LSR(y, 11));
  y := MathBits.ixor(y, MathBits.iand(LSL(y,  7), 9D2C5680H));
  y := MathBits.ixor(y, MathBits.iand(LSL(y, 15), 4022730752));
  y := MathBits.ixor(y, LSR(y, 18));

  RETURN y
END IRanMT;

(* Generates a real Random number on [0..1] interval *)
PROCEDURE RRanMT(): REAL;
BEGIN
  RETURN FLT(IRanMT())/FLT(INT_MAX)
END RRanMT;


END RandExt.
