github.com/gopherd/gonum@v0.0.4/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 }