1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.rng.core.source64;
19
20 import org.apache.commons.rng.JumpableUniformRandomProvider;
21 import org.apache.commons.rng.LongJumpableUniformRandomProvider;
22 import org.apache.commons.rng.UniformRandomProvider;
23 import org.apache.commons.rng.core.util.NumberFactory;
24
25 /**
26 * This abstract class is a base for algorithms from the LXM family of
27 * generators with a 128-bit LCG sub-generator. The class implements
28 * the jump functions.
29 *
30 * @since 1.5
31 */
32 abstract class AbstractL128 extends LongProvider implements LongJumpableUniformRandomProvider {
33 /** Size of the seed vector. */
34 private static final int SEED_SIZE = 4;
35 /** Low half of 128-bit LCG multiplier. */
36 private static final long ML = LXMSupport.M128L;
37
38 /** High half of the 128-bit per-instance LCG additive parameter.
39 * Cannot be final to support RestorableUniformRandomProvider. */
40 protected long lah;
41 /** Low half of the 128-bit per-instance LCG additive parameter (must be odd).
42 * Cannot be final to support RestorableUniformRandomProvider. */
43 protected long lal;
44 /** High half of the 128-bit state of the LCG generator. */
45 protected long lsh;
46 /** Low half of the 128-bit state of the LCG generator. */
47 protected long lsl;
48
49 /**
50 * Creates a new instance.
51 *
52 * @param seed Initial seed.
53 * If the length is larger than 4, only the first 4 elements will
54 * be used; if smaller, the remaining elements will be automatically
55 * set.
56 *
57 * <p>The 1st and 2nd elements are used to set the LCG increment; the least significant bit
58 * is set to odd to ensure a full period LCG. The 3rd and 4th elements are used
59 * to set the LCG state.</p>
60 */
61 AbstractL128(long[] seed) {
62 setState(extendSeed(seed, SEED_SIZE));
63 }
64
65 /**
66 * Creates a new instance using a 4 element seed.
67 *
68 * <p>The 1st and 2nd elements are used to set the LCG increment; the least significant bit
69 * is set to odd to ensure a full period LCG. The 3rd and 4th elements are used
70 * to set the LCG state.</p>
71 *
72 * @param seed0 Initial seed element 0.
73 * @param seed1 Initial seed element 1.
74 * @param seed2 Initial seed element 2.
75 * @param seed3 Initial seed element 3.
76 */
77 AbstractL128(long seed0, long seed1, long seed2, long seed3) {
78 lah = seed0;
79 // Additive parameter must be odd
80 lal = seed1 | 1;
81 lsh = seed2;
82 lsl = seed3;
83 }
84
85 /**
86 * Creates a copy instance.
87 *
88 * @param source Source to copy.
89 */
90 AbstractL128(AbstractL128 source) {
91 super(source);
92 lah = source.lah;
93 lal = source.lal;
94 lsh = source.lsh;
95 lsl = source.lsl;
96 }
97
98 /**
99 * Copies the state into the generator state.
100 *
101 * @param state the new state
102 */
103 private void setState(long[] state) {
104 lah = state[0];
105 // Additive parameter must be odd
106 lal = state[1] | 1;
107 lsh = state[2];
108 lsl = state[3];
109 }
110
111 /** {@inheritDoc} */
112 @Override
113 protected byte[] getStateInternal() {
114 return composeStateInternal(NumberFactory.makeByteArray(
115 new long[] {lah, lal, lsh, lsl}),
116 super.getStateInternal());
117 }
118
119 /** {@inheritDoc} */
120 @Override
121 protected void setStateInternal(byte[] s) {
122 final byte[][] c = splitStateInternal(s, SEED_SIZE * Long.BYTES);
123 setState(NumberFactory.makeLongArray(c[0]));
124 super.setStateInternal(c[1]);
125 }
126
127 /**
128 * Creates a copy of the UniformRandomProvider and then <em>retreats</em> the state of the
129 * current instance. The copy is returned.
130 *
131 * <p>The jump is performed by advancing the state of the LCG sub-generator by 1 cycle.
132 * The XBG state is unchanged.
133 */
134 @Override
135 public UniformRandomProvider jump() {
136 final UniformRandomProvider copy = copy();
137 // Advance the LCG 1 step.
138 // The LCG is, in effect, "s = m * s + a" where m = ((1LL << 64) + ML)
139 final long sh = lsh;
140 final long sl = lsl;
141 final long u = ML * sl;
142 // High half
143 lsh = ML * sh + LXMSupport.unsignedMultiplyHigh(ML, sl) + sl + lah +
144 // Carry propagation
145 LXMSupport.unsignedAddHigh(u, lal);
146 // Low half
147 lsl = u + lal;
148 resetCachedState();
149 return copy;
150 }
151
152 /**
153 * Creates a copy of the UniformRandomProvider and then <em>retreats</em> the state of the
154 * current instance. The copy is returned.
155 *
156 * <p>The jump is performed by advancing the state of the LCG sub-generator by
157 * 2<sup>64</sup> cycles. The XBG state is unchanged.
158 */
159 @Override
160 public JumpableUniformRandomProvider longJump() {
161 final JumpableUniformRandomProvider copy = copy();
162 // Advance the LCG 2^64 steps
163 // s = m' * s + c' * c
164 // Specialised routine given M128PL=1, C128PL=0 and many terms
165 // can be dropped as the low half is unchanged and there is no carry
166 // sh = m'l * sh // sh
167 // + high(m'l * sl) // dropped as m'l=1 and there is no high part
168 // + m'h * sl
169 // + c'l * ah // dropped as c'l=0
170 // + high(c'l * ah) // dropped as c'l=0
171 // + c'h * al
172 // sl = m'l * sl + c'l * al
173 // = sl
174 lsh = lsh + LXMSupport.M128PH * lsl + LXMSupport.C128PH * lal;
175 resetCachedState();
176 return copy;
177 }
178
179 /**
180 * Create a copy.
181 *
182 * @return the copy
183 */
184 abstract AbstractL128 copy();
185 }