golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/rand/rng.go (about) 1 // Copyright 2017 The Go 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 package rand 6 7 import ( 8 "encoding/binary" 9 "io" 10 "math/bits" 11 ) 12 13 // PCGSource is an implementation of a 64-bit permuted congruential 14 // generator as defined in 15 // 16 // PCG: A Family of Simple Fast Space-Efficient Statistically Good 17 // Algorithms for Random Number Generation 18 // Melissa E. O’Neill, Harvey Mudd College 19 // http://www.pcg-random.org/pdf/toms-oneill-pcg-family-v1.02.pdf 20 // 21 // The generator here is the congruential generator PCG XSL RR 128/64 (LCG) 22 // as found in the software available at http://www.pcg-random.org/. 23 // It has period 2^128 with 128 bits of state, producing 64-bit values. 24 // Is state is represented by two uint64 words. 25 type PCGSource struct { 26 low uint64 27 high uint64 28 } 29 30 const ( 31 maxUint32 = (1 << 32) - 1 32 33 multiplier = 47026247687942121848144207491837523525 34 mulHigh = multiplier >> 64 35 mulLow = multiplier & maxUint64 36 37 increment = 117397592171526113268558934119004209487 38 incHigh = increment >> 64 39 incLow = increment & maxUint64 40 41 // TODO: Use these? 42 initializer = 245720598905631564143578724636268694099 43 initHigh = initializer >> 64 44 initLow = initializer & maxUint64 45 ) 46 47 // Seed uses the provided seed value to initialize the generator to a deterministic state. 48 func (pcg *PCGSource) Seed(seed uint64) { 49 pcg.low = seed 50 pcg.high = seed // TODO: What is right? 51 } 52 53 // Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64. 54 func (pcg *PCGSource) Uint64() uint64 { 55 pcg.multiply() 56 pcg.add() 57 // XOR high and low 64 bits together and rotate right by high 6 bits of state. 58 return bits.RotateLeft64(pcg.high^pcg.low, -int(pcg.high>>58)) 59 } 60 61 func (pcg *PCGSource) add() { 62 var carry uint64 63 pcg.low, carry = bits.Add64(pcg.low, incLow, 0) 64 pcg.high, _ = bits.Add64(pcg.high, incHigh, carry) 65 } 66 67 func (pcg *PCGSource) multiply() { 68 hi, lo := bits.Mul64(pcg.low, mulLow) 69 hi += pcg.high * mulLow 70 hi += pcg.low * mulHigh 71 pcg.low = lo 72 pcg.high = hi 73 } 74 75 // MarshalBinary returns the binary representation of the current state of the generator. 76 func (pcg *PCGSource) MarshalBinary() ([]byte, error) { 77 var buf [16]byte 78 binary.BigEndian.PutUint64(buf[:8], pcg.high) 79 binary.BigEndian.PutUint64(buf[8:], pcg.low) 80 return buf[:], nil 81 } 82 83 // UnmarshalBinary sets the state of the generator to the state represented in data. 84 func (pcg *PCGSource) UnmarshalBinary(data []byte) error { 85 if len(data) < 16 { 86 return io.ErrUnexpectedEOF 87 } 88 pcg.low = binary.BigEndian.Uint64(data[8:]) 89 pcg.high = binary.BigEndian.Uint64(data[:8]) 90 return nil 91 }