Class MaceRandom

All Implemented Interfaces:
Externalizable, Serializable, RandomGenerator

public class MaceRandom extends EnhancedRandom
Like AceRandom with five 64-bit states but also one unchanging 24-bit stream; does not use multiplication, only add, XOR, and bitwise-rotate operations (this is an ARX generator). Has a state that runs like a counter, guaranteeing a minimum period of 2 to the 64, and each stream should be independent of any other stream after a small number of generations. The expected period is about 2 to the 310 calls to nextLong(), though this is an overly cautious estimate. Even if using the old stand-by advice, that only the square root of the period can be used before a generator starts to have problems, would permit an enormous 2 to the 160 calls before becoming, in some vague way, "bad." That's a trillion, quintillion, quintillion numbers. With 16 million possible streams, on top of that.
At least one stream passes 64TB with no anomalies, and at least 1% of all total streams pass 256MB without failures or lingering anomalies. Only 1% were tested because testing 100% would take at least until the year 2026 to finish, and the tests were run starting May 10, 2025.
After about 30 calls to nextLong(), any two different streams with otherwise identical states should have no correlations to each other. This avoids the issue with SplitMix64 where "gamma" receives problem values, because it only allows changes to 24 bits of the constant 0x9E3779B97F4A7C15L. The specific bits that may change are the '1' bits in 0x003569CA5369AC00L, which are all spaced out so the changeable bits never clump into groups of 4 or more sequential bits, and they have gaps in the changeable bits of no more than 2 '0' bits. So far, no truly problematic streams have been found, despite the stream being an increment for a counter like in SplitMix64. This does also use quite a lot more state than SplitMix64, and those extra 320 bits of state change in their own complex ways, both related and unrelated to the stream.
See Also:
  • Field Details

    • MASK

      public static final long MASK
      A long mask with 24 bits set, all symmetrical around the middle bits, leaving 10 bits all zero at the most significant and the least significant ends. This is used to determine which bits of GOLDEN_64 can be changed by the stream.
      See Also:
    • GOLDEN_64

      public static final long GOLDEN_64
      2 to the 64 divided by the golden ratio. In general this is a good number to use in an additive sequence (like a counter with a large increment), and small changes to the middle bits still tend to result in good numbers.
      See Also:
    • stream

      protected long stream
      The unchanging stream; cannot be set directly, but can be obtained directly with getStream() or get/set indirectly via a 24-bit int with getStreamIdentifier() and setStreamIdentifier(int).
    • stateA

      protected long stateA
      The first state; can be any long.
    • stateB

      protected long stateB
      The second state; can be any long. The first call to nextLong() will return this verbatim, if no other methods have been called.
    • stateC

      protected long stateC
      The third state; can be any long.
    • stateD

      protected long stateD
      The fourth state; can be any long.
    • stateE

      protected long stateE
      The fifth state; can be any long.
  • Constructor Details

    • MaceRandom

      public MaceRandom()
      Creates a new MaceRandom with a random state.
    • MaceRandom

      public MaceRandom(long seed)
      Creates a new MaceRandom with the given seed; all long values are permitted. The seed will be passed to setSeed(long) to attempt to adequately distribute the seed randomly.
      Parameters:
      seed - any long value
    • MaceRandom

      public MaceRandom(int streamIdentifier, long stateA, long stateB, long stateC, long stateD, long stateE)
      Creates a new MaceRandom with the given stream identifier and five states; all long values are permitted for states, and all ints between 0 and 16777215 (or 0xFFFFFF), inclusive, are permitted for streamIdentifier. The states will be used verbatim, and the streamIdentifier can be retrieved with getStreamIdentifier().
      Parameters:
      streamIdentifier - an up-to-24-bit int (between 0 and 16777215, inclusive); higher bits are ignored
      stateA - any long value
      stateB - any long value
      stateC - any long value
      stateD - any long value
      stateE - any long value
  • Method Details

    • deposit

      public static long deposit(long bits)
      Given a long bits where the first 24 positions can have variable bits, uses MASK to produce a long where the least-significant 24 bits of bits have been placed into consecutively greater set bits in mask. It finished by XORing the masked bits with GOLDEN_64. This method does not allocate. The parameter is usually provided by getStreamIdentifier() or by calling extract(long).
      Based on Hacker's Delight (2nd edition).
      Parameters:
      bits - the bit values to be deposited into positions denoted by mask
      Returns:
      a long starting with GOLDEN_64 where only bits in MASK can be changed
    • extract

      public static long extract(long bits)
      Given a long bits which should be a result of getStream(), uses MASK (with 24 bits set to 1) to determine which positions in bits will matter, and produces a long result of up to 24 bits, with each successive bit corresponding to a successive position in MASK changed from GOLDEN_64. This produces a "stream identifier" for a given stream, as used by getStreamIdentifier() and setStreamIdentifier(int).
      Based on Hacker's Delight (2nd edition).
      Parameters:
      bits - the bit values that will be masked by mask and placed into the low-order bits of the result
      Returns:
      a long with the highest bit that can be set equal to the Long.bitCount(long) of mask
    • getTag

      public String getTag()
      Description copied from class: EnhancedRandom
      Gets the tag used to identify this type of EnhancedRandom, as a String. This tag should be unique, and for uniformity purposes, all tags used in this library are 4 characters long. User-defined tags should have a different length.
      Specified by:
      getTag in class EnhancedRandom
      Returns:
      a unique String identifier for this type of EnhancedRandom; usually 4 chars long.
    • getStateCount

      public int getStateCount()
      This generator has 6 long states, so this returns 6.
      Overrides:
      getStateCount in class EnhancedRandom
      Returns:
      6 (six)
    • getSelectedState

      public long getSelectedState(int selection)
      Gets the state determined by selection, as-is. The value for selection should be between 0 and 4, inclusive; if it is any other value this gets state E as if 4 was given.
      Overrides:
      getSelectedState in class EnhancedRandom
      Parameters:
      selection - used to select which state variable to get; generally 0, 1, 2, 3, or 4
      Returns:
      the value of the selected state
    • setSelectedState

      public void setSelectedState(int selection, long value)
      Sets one of the states, determined by selection, to value, as-is. Selections 0, 1, 2, 3, and 4 refer to states A, B, C, D, and E, and if the selection is anything else, this treats it as 4 and sets stateE.
      Overrides:
      setSelectedState in class EnhancedRandom
      Parameters:
      selection - used to select which state variable to set; generally 0, 1, 2, 3, or 4
      value - the exact value to use for the selected state, if valid
    • setSeed

      public void setSeed(long seed)
      This initializes all 5 states of the generator to random values based on the given seed. (2 to the 64) possible initial generator states can be produced here.
      Specified by:
      setSeed in class EnhancedRandom
      Parameters:
      seed - the initial seed; may be any long
    • getStream

      public long getStream()
    • getStreamIdentifier

      public int getStreamIdentifier()
      Gets an up-to-24-bit long that uniquely identifies the stream this MaceRandom uses. This identifier can be passed to setStreamIdentifier(int) to change the stream.
      Returns:
      the smaller identifier used to determine the actual stream
    • setStreamIdentifier

      public void setStreamIdentifier(int streamID)
      Sets the stream using the low-order 24 bits of the given int.
      Parameters:
      streamID - can be any int, but only the lowest-order 24 bits matter
    • setStreamIdentifier

      public void setStreamIdentifier(long value)
      Sets the stream using all mixed bits of the given long.
      Parameters:
      value - can be any long, and will have all bits mixed into a stream identifier
    • getStateA

      public long getStateA()
    • setStateA

      public void setStateA(long stateA)
      Sets the first part of the state.
      Parameters:
      stateA - can be any long
    • getStateB

      public long getStateB()
    • setStateB

      public void setStateB(long stateB)
      Sets the second part of the state.
      Parameters:
      stateB - can be any long
    • getStateC

      public long getStateC()
    • setStateC

      public void setStateC(long stateC)
      Sets the third part of the state.
      Parameters:
      stateC - can be any long
    • getStateD

      public long getStateD()
    • setStateD

      public void setStateD(long stateD)
      Sets the fourth part of the state.
      Parameters:
      stateD - can be any long
    • getStateE

      public long getStateE()
    • setStateE

      public void setStateE(long stateE)
      Sets the fifth part of the state.
      Parameters:
      stateE - can be any long
    • setState

      public void setState(long streamID, long stateA, long stateB, long stateC, long stateD, long stateE)
      Sets the state completely to the given six state variables. This is the same as calling setStreamIdentifier(long), setStateA(long), setStateB(long), setStateC(long), setStateD(long), and setStateE(long) as a group.
      Overrides:
      setState in class EnhancedRandom
      Parameters:
      streamID - an up-to-24-bit int (between 0 and 16777215, inclusive); higher bits will be mixed in, and if present the stream may not be unique
      stateA - the first state; can be any long
      stateB - the second state; can be any long
      stateC - the third state; can be any long
      stateD - the fourth state; can be any long
      stateE - the fifth state; can be any long
    • nextLong

      public long nextLong()
      Description copied from class: EnhancedRandom
      Returns the next pseudorandom, uniformly distributed long value from this random number generator's sequence. The general contract of nextLong is that one long value is pseudorandomly generated and returned.
      The only methods that need to be implemented by this interface are this and EnhancedRandom.copy(), though other methods can be implemented as appropriate for generators that, for instance, natively produce ints rather than longs.
      Specified by:
      nextLong in interface RandomGenerator
      Specified by:
      nextLong in class EnhancedRandom
      Returns:
      the next pseudorandom, uniformly distributed long value from this random number generator's sequence
    • previousLong

      public long previousLong()
      Description copied from class: EnhancedRandom
      Optional; moves the state to its previous value and returns the previous long that would have been produced by EnhancedRandom.nextLong(). This can be equivalent to calling EnhancedRandom.skip(long) with -1L, but not always; many generators can't efficiently skip long distances, but can step back by one value.
      Generators that natively generate int results typically produce long values by generating an int for the high 32 bits and an int for the low 32 bits. When producing the previous long, the order the high and low bits are generated, such as by EnhancedRandom.previousInt(), should be reversed. Generators that natively produce long values usually don't need to implement EnhancedRandom.previousInt(), but those that produce int usually should implement it, and may optionally call previousInt() twice in this method.
      If you know how to implement the reverse of a particular random number generator, it is recommended you do so here, rather than rely on skip(). This isn't always easy, but should always be possible for any decent PRNG (some historical PRNGs, such as the Middle-Square PRNG, cannot be reversed at all). If a generator cannot be reversed because multiple initial states can transition to the same subsequent state, it is known to have statistical problems that are not necessarily present in a generator that matches one initial state to one subsequent state.
      The public implementation calls EnhancedRandom.skip(long) with -1L, and if skip() has not been implemented differently, then it will throw an UnsupportedOperationException.
      Overrides:
      previousLong in class EnhancedRandom
      Returns:
      the previous number this would have produced with EnhancedRandom.nextLong()
    • next

      public int next(int bits)
      Description copied from class: EnhancedRandom
      Generates the next pseudorandom number with a specific maximum size in bits (not a max number). If you want to get a random number in a range, you should usually use EnhancedRandom.nextInt(int) instead. For some specific cases, this method is more efficient and less biased than EnhancedRandom.nextInt(int). For bits values between 1 and 30, this should be similar in effect to nextInt(1 << bits); though it won't typically produce the same values, they will have the correct range. If bits is 31, this can return any non-negative int; note that nextInt(1 << 31) won't behave this way because 1 << 31 is negative. If bits is 32 (or 0), this can return any int.

      The general contract of next is that it returns an int value and if the argument bits is between 1 and 32 (inclusive), then that many low-order bits of the returned value will be (approximately) independently chosen bit values, each of which is (approximately) equally likely to be 0 or 1.

      Note that you can give this values for bits that are outside its expected range of 1 to 32, but the value used, as long as bits is positive, will effectively be bits % 32. As stated before, a value of 0 for bits is the same as a value of 32.

      Overrides:
      next in class EnhancedRandom
      Parameters:
      bits - the amount of random bits to request, from 1 to 32
      Returns:
      the next pseudorandom value from this random number generator's sequence
    • copy

      public MaceRandom copy()
      Description copied from class: EnhancedRandom
      Creates a new EnhancedRandom with identical states to this one, so if the same EnhancedRandom methods are called on this object and its copy (in the same order), the same outputs will be produced. This is not guaranteed to copy the inherited state of any parent class, so if you call methods that are only implemented by a superclass (like Random) and not this one, the results may differ.
      Specified by:
      copy in class EnhancedRandom
      Returns:
      a deep copy of this EnhancedRandom.
    • equals

      public boolean equals(Object o)
      Overrides:
      equals in class Object
    • toString

      public String toString()
      Overrides:
      toString in class Object