gonum.org/v1/gonum@v0.14.0/mathext/prng/mt19937_64.go (about)

     1  // Copyright ©2019 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Original C program copyright Takuji Nishimura and Makoto Matsumoto 2004.
     6  // http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/mt19937-64.c
     7  
     8  package prng
     9  
    10  import (
    11  	"encoding/binary"
    12  	"io"
    13  )
    14  
    15  const (
    16  	mt19937_64NN        = 312
    17  	mt19937_64MM        = 156
    18  	mt19937_64MatrixA   = 0xB5026F5AA96619E9
    19  	mt19937_64UpperMask = 0xFFFFFFFF80000000
    20  	mt19937_64LowerMask = 0x7FFFFFFF
    21  )
    22  
    23  // MT19937_64 implements the 64 bit Mersenne Twister PRNG. MT19937_64
    24  // is the 64 bit version of MT19937, it has the same sized state, but
    25  // generates a different sequence.
    26  // See https://en.wikipedia.org/wiki/Mersenne_Twister.
    27  type MT19937_64 struct {
    28  	mt  [mt19937_64NN]uint64
    29  	mti uint64
    30  }
    31  
    32  // NewMT19937_64 returns a new MT19937_64 PRNG. The returned PRNG will
    33  // use the default seed 5489 unless the Seed method is called with
    34  // another value.
    35  func NewMT19937_64() *MT19937_64 {
    36  	return &MT19937_64{mti: mt19937_64NN + 1}
    37  }
    38  
    39  // Seed uses the provided seed value to initialize the generator to a
    40  // deterministic state.
    41  func (src *MT19937_64) Seed(seed uint64) {
    42  	src.mt[0] = seed
    43  	for src.mti = 1; src.mti < mt19937_64NN; src.mti++ {
    44  		src.mt[src.mti] = (6364136223846793005*(src.mt[src.mti-1]^(src.mt[src.mti-1]>>62)) + src.mti)
    45  	}
    46  }
    47  
    48  // SeedFromKeys uses the provided seed key value to initialize the
    49  // generator to a deterministic state. It is provided for compatibility
    50  // with C implementations.
    51  func (src *MT19937_64) SeedFromKeys(keys []uint64) {
    52  	src.Seed(19650218)
    53  	i := uint64(1)
    54  	j := uint64(0)
    55  	k := uint64(mt19937_64NN)
    56  	if k <= uint64(len(keys)) {
    57  		k = uint64(len(keys))
    58  	}
    59  	for ; k != 0; k-- {
    60  		src.mt[i] = (src.mt[i] ^ ((src.mt[i-1] ^ (src.mt[i-1] >> 62)) * 3935559000370003845)) + keys[j] + j // Non linear.
    61  		i++
    62  		j++
    63  		if i >= mt19937_64NN {
    64  			src.mt[0] = src.mt[mt19937_64NN-1]
    65  			i = 1
    66  		}
    67  		if j >= uint64(len(keys)) {
    68  			j = 0
    69  		}
    70  	}
    71  	for k = mt19937_64NN - 1; k != 0; k-- {
    72  		src.mt[i] = (src.mt[i] ^ ((src.mt[i-1] ^ (src.mt[i-1] >> 62)) * 2862933555777941757)) - i // Non linear.
    73  		i++
    74  		if i >= mt19937_64NN {
    75  			src.mt[0] = src.mt[mt19937_64NN-1]
    76  			i = 1
    77  		}
    78  	}
    79  
    80  	src.mt[0] = 1 << 63 /* MSB is 1; assuring non-zero initial array */
    81  }
    82  
    83  // Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.
    84  func (src *MT19937_64) Uint64() uint64 {
    85  	mag01 := [2]uint64{0, mt19937_64MatrixA}
    86  
    87  	var x uint64
    88  	if src.mti >= mt19937_64NN { // Generate mt19937_64NN words at one time.
    89  		if src.mti == mt19937_64NN+1 {
    90  			// If Seed() has not been called
    91  			// a default initial seed is used.
    92  			src.Seed(5489)
    93  		}
    94  
    95  		var i int
    96  		for ; i < mt19937_64NN-mt19937_64MM; i++ {
    97  			x = (src.mt[i] & mt19937_64UpperMask) | (src.mt[i+1] & mt19937_64LowerMask)
    98  			src.mt[i] = src.mt[i+mt19937_64MM] ^ (x >> 1) ^ mag01[(int)(x&0x1)]
    99  		}
   100  		for ; i < mt19937_64NN-1; i++ {
   101  			x = (src.mt[i] & mt19937_64UpperMask) | (src.mt[i+1] & mt19937_64LowerMask)
   102  			src.mt[i] = src.mt[i+(mt19937_64MM-mt19937_64NN)] ^ (x >> 1) ^ mag01[(int)(x&0x1)]
   103  		}
   104  		x = (src.mt[mt19937_64NN-1] & mt19937_64UpperMask) | (src.mt[0] & mt19937_64LowerMask)
   105  		src.mt[mt19937_64NN-1] = src.mt[mt19937_64MM-1] ^ (x >> 1) ^ mag01[(int)(x&0x1)]
   106  
   107  		src.mti = 0
   108  	}
   109  
   110  	x = src.mt[src.mti]
   111  	src.mti++
   112  
   113  	// Tempering.
   114  	x ^= (x >> 29) & 0x5555555555555555
   115  	x ^= (x << 17) & 0x71D67FFFEDA60000
   116  	x ^= (x << 37) & 0xFFF7EEE000000000
   117  	x ^= (x >> 43)
   118  
   119  	return x
   120  }
   121  
   122  // MarshalBinary returns the binary representation of the current state of the generator.
   123  func (src *MT19937_64) MarshalBinary() ([]byte, error) {
   124  	var buf [(mt19937_64NN + 1) * 8]byte
   125  	for i := 0; i < mt19937_64NN; i++ {
   126  		binary.BigEndian.PutUint64(buf[i*8:(i+1)*8], src.mt[i])
   127  	}
   128  	binary.BigEndian.PutUint64(buf[mt19937_64NN*8:], src.mti)
   129  	return buf[:], nil
   130  }
   131  
   132  // UnmarshalBinary sets the state of the generator to the state represented in data.
   133  func (src *MT19937_64) UnmarshalBinary(data []byte) error {
   134  	if len(data) < (mt19937_64NN+1)*8 {
   135  		return io.ErrUnexpectedEOF
   136  	}
   137  	for i := 0; i < mt19937_64NN; i++ {
   138  		src.mt[i] = binary.BigEndian.Uint64(data[i*8 : (i+1)*8])
   139  	}
   140  	src.mti = binary.BigEndian.Uint64(data[mt19937_64NN*8:])
   141  	return nil
   142  }