gonum.org/v1/gonum@v0.14.0/mathext/prng/prng_di_unimi.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  // PRNGs from Dipartimento di Informatica Università degli Studi di Milano.
     6  // David Blackman and Sebastiano Vigna licensed under CC0 1.0
     7  // http://creativecommons.org/publicdomain/zero/1.0/
     8  
     9  package prng
    10  
    11  import (
    12  	"encoding/binary"
    13  	"io"
    14  	"math/bits"
    15  )
    16  
    17  // SplitMix64 is the splitmix64 PRNG from http://prng.di.unimi.it/splitmix64.c.
    18  // The zero value is usable directly. SplitMix64 is primarily provided to support
    19  // seeding the xoshiro PRNGs.
    20  type SplitMix64 struct {
    21  	state uint64
    22  }
    23  
    24  // NewSplitMix64 returns a new pseudo-random splitmix64 source seeded
    25  // with the given value.
    26  func NewSplitMix64(seed uint64) *SplitMix64 {
    27  	var src SplitMix64
    28  	src.Seed(seed)
    29  	return &src
    30  }
    31  
    32  // Seed uses the provided seed value to initialize the generator to a
    33  // deterministic state.
    34  func (src *SplitMix64) Seed(seed uint64) {
    35  	src.state = seed
    36  }
    37  
    38  // Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.
    39  func (src *SplitMix64) Uint64() uint64 {
    40  	src.state += 0x9e3779b97f4a7c15
    41  	z := src.state
    42  	z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9
    43  	z = (z ^ (z >> 27)) * 0x94d049bb133111eb
    44  	return z ^ (z >> 31)
    45  }
    46  
    47  // MarshalBinary returns the binary representation of the current state of the generator.
    48  func (src *SplitMix64) MarshalBinary() ([]byte, error) {
    49  	var buf [8]byte
    50  	binary.BigEndian.PutUint64(buf[:], src.state)
    51  	return buf[:], nil
    52  }
    53  
    54  // UnmarshalBinary sets the state of the generator to the state represented in data.
    55  func (src *SplitMix64) UnmarshalBinary(data []byte) error {
    56  	if len(data) < 8 {
    57  		return io.ErrUnexpectedEOF
    58  	}
    59  	src.state = binary.BigEndian.Uint64(data)
    60  	return nil
    61  }
    62  
    63  // Xoshiro256plus is the xoshiro256+ 1.0 PRNG from http://prng.di.unimi.it/xoshiro256plus.c.
    64  // The xoshiro PRNGs are described in http://vigna.di.unimi.it/ftp/papers/ScrambledLinear.pdf
    65  // and http://prng.di.unimi.it/.
    66  // A Xoshiro256plus value is only valid if returned by NewXoshiro256plus.
    67  type Xoshiro256plus struct {
    68  	state [4]uint64
    69  }
    70  
    71  // NewXoshiro256plus returns a new pseudo-random xoshiro256+ source
    72  // seeded with the given value.
    73  func NewXoshiro256plus(seed uint64) *Xoshiro256plus {
    74  	var src Xoshiro256plus
    75  	src.Seed(seed)
    76  	return &src
    77  }
    78  
    79  // Seed uses the provided seed value to initialize the generator to a
    80  // deterministic state.
    81  func (src *Xoshiro256plus) Seed(seed uint64) {
    82  	var boot SplitMix64
    83  	boot.Seed(seed)
    84  	for i := range src.state {
    85  		src.state[i] = boot.Uint64()
    86  	}
    87  }
    88  
    89  // Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.
    90  func (src *Xoshiro256plus) Uint64() uint64 {
    91  	result := src.state[0] + src.state[3]
    92  
    93  	t := src.state[1] << 17
    94  
    95  	src.state[2] ^= src.state[0]
    96  	src.state[3] ^= src.state[1]
    97  	src.state[1] ^= src.state[2]
    98  	src.state[0] ^= src.state[3]
    99  
   100  	src.state[2] ^= t
   101  
   102  	src.state[3] = bits.RotateLeft64(src.state[3], 45)
   103  
   104  	return result
   105  }
   106  
   107  // MarshalBinary returns the binary representation of the current state of the generator.
   108  func (src *Xoshiro256plus) MarshalBinary() ([]byte, error) {
   109  	var buf [32]byte
   110  	for i := 0; i < 4; i++ {
   111  		binary.BigEndian.PutUint64(buf[i*8:(i+1)*8], src.state[i])
   112  	}
   113  	return buf[:], nil
   114  }
   115  
   116  // UnmarshalBinary sets the state of the generator to the state represented in data.
   117  func (src *Xoshiro256plus) UnmarshalBinary(data []byte) error {
   118  	if len(data) < 32 {
   119  		return io.ErrUnexpectedEOF
   120  	}
   121  	for i := 0; i < 4; i++ {
   122  		src.state[i] = binary.BigEndian.Uint64(data[i*8 : (i+1)*8])
   123  	}
   124  	return nil
   125  }
   126  
   127  // Xoshiro256plusplus is the xoshiro256++ 1.0 PRNG from http://prng.di.unimi.it/xoshiro256plusplus.c.
   128  // The xoshiro PRNGs are described in http://vigna.di.unimi.it/ftp/papers/ScrambledLinear.pdf
   129  // and http://prng.di.unimi.it/.
   130  // A Xoshiro256plusplus value is only valid if returned by NewXoshiro256plusplus.
   131  type Xoshiro256plusplus struct {
   132  	state [4]uint64
   133  }
   134  
   135  // NewXoshiro256plusplus returns a new pseudo-random xoshiro256++ source
   136  // seeded with the given value.
   137  func NewXoshiro256plusplus(seed uint64) *Xoshiro256plusplus {
   138  	var src Xoshiro256plusplus
   139  	src.Seed(seed)
   140  	return &src
   141  }
   142  
   143  // Seed uses the provided seed value to initialize the generator to a
   144  // deterministic state.
   145  func (src *Xoshiro256plusplus) Seed(seed uint64) {
   146  	var boot SplitMix64
   147  	boot.Seed(seed)
   148  	for i := range src.state {
   149  		src.state[i] = boot.Uint64()
   150  	}
   151  }
   152  
   153  // Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.
   154  func (src *Xoshiro256plusplus) Uint64() uint64 {
   155  	result := bits.RotateLeft64(src.state[0]+src.state[3], 23) + src.state[0]
   156  
   157  	t := src.state[1] << 17
   158  
   159  	src.state[2] ^= src.state[0]
   160  	src.state[3] ^= src.state[1]
   161  	src.state[1] ^= src.state[2]
   162  	src.state[0] ^= src.state[3]
   163  
   164  	src.state[2] ^= t
   165  
   166  	src.state[3] = bits.RotateLeft64(src.state[3], 45)
   167  
   168  	return result
   169  }
   170  
   171  // MarshalBinary returns the binary representation of the current state of the generator.
   172  func (src *Xoshiro256plusplus) MarshalBinary() ([]byte, error) {
   173  	var buf [32]byte
   174  	for i := 0; i < 4; i++ {
   175  		binary.BigEndian.PutUint64(buf[i*8:(i+1)*8], src.state[i])
   176  	}
   177  	return buf[:], nil
   178  }
   179  
   180  // UnmarshalBinary sets the state of the generator to the state represented in data.
   181  func (src *Xoshiro256plusplus) UnmarshalBinary(data []byte) error {
   182  	if len(data) < 32 {
   183  		return io.ErrUnexpectedEOF
   184  	}
   185  	for i := 0; i < 4; i++ {
   186  		src.state[i] = binary.BigEndian.Uint64(data[i*8 : (i+1)*8])
   187  	}
   188  	return nil
   189  }
   190  
   191  // Xoshiro256starstar is the xoshiro256** 1.0 PRNG from http://prng.di.unimi.it/xoshiro256starstar.c.
   192  // The xoshiro PRNGs are described in http://vigna.di.unimi.it/ftp/papers/ScrambledLinear.pdf
   193  // and http://prng.di.unimi.it/.
   194  // A Xoshiro256starstar value is only valid if returned by NewXoshiro256starstar.
   195  type Xoshiro256starstar struct {
   196  	state [4]uint64
   197  }
   198  
   199  // NewXoshiro256starstar returns a new pseudo-random xoshiro256** source
   200  // seeded with the given value.
   201  func NewXoshiro256starstar(seed uint64) *Xoshiro256starstar {
   202  	var src Xoshiro256starstar
   203  	src.Seed(seed)
   204  	return &src
   205  }
   206  
   207  // Seed uses the provided seed value to initialize the generator to a
   208  // deterministic state.
   209  func (src *Xoshiro256starstar) Seed(seed uint64) {
   210  	var boot SplitMix64
   211  	boot.Seed(seed)
   212  	for i := range src.state {
   213  		src.state[i] = boot.Uint64()
   214  	}
   215  }
   216  
   217  // Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.
   218  func (src *Xoshiro256starstar) Uint64() uint64 {
   219  	result := bits.RotateLeft64(src.state[1]*5, 7) * 9
   220  
   221  	t := src.state[1] << 17
   222  
   223  	src.state[2] ^= src.state[0]
   224  	src.state[3] ^= src.state[1]
   225  	src.state[1] ^= src.state[2]
   226  	src.state[0] ^= src.state[3]
   227  
   228  	src.state[2] ^= t
   229  
   230  	src.state[3] = bits.RotateLeft64(src.state[3], 45)
   231  
   232  	return result
   233  }
   234  
   235  // MarshalBinary returns the binary representation of the current state of the generator.
   236  func (src *Xoshiro256starstar) MarshalBinary() ([]byte, error) {
   237  	var buf [32]byte
   238  	for i := 0; i < 4; i++ {
   239  		binary.BigEndian.PutUint64(buf[i*8:(i+1)*8], src.state[i])
   240  	}
   241  	return buf[:], nil
   242  }
   243  
   244  // UnmarshalBinary sets the state of the generator to the state represented in data.
   245  func (src *Xoshiro256starstar) UnmarshalBinary(data []byte) error {
   246  	if len(data) < 32 {
   247  		return io.ErrUnexpectedEOF
   248  	}
   249  	for i := 0; i < 4; i++ {
   250  		src.state[i] = binary.BigEndian.Uint64(data[i*8 : (i+1)*8])
   251  	}
   252  	return nil
   253  }