Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Lagged Fibonacci Generator #727

Closed
wants to merge 12 commits into from

2 participants

monarch dodra Andrei Alexandrescu
monarch dodra
Collaborator

D2 test results for 764

Ported from boost:
http://www.boost.org/doc/libs/1_50_0/boost/random/linear_congruential.hpp

While boost provided two different classes for UInt/Real types, this implementation merges both into the same class.

My doubts on the implementation are:
*Aliases: Boost uses doubles as the defaults for the engine, and doesn't provide any aliases for the unsigned types. Should we follow the same scheme? Provided alliases for both?
*Default Alias: Not sure which config to use for the "LaggedFibonacci", so I chose LaggedFibonacci607, but documented it as "implementation chosen".
*Reference type & unseeded engine: The generator is a reference type forward range. In thoery, it can't be used until it has been seeded. I have put in asserts about this. Maybe it should be enforece instead?
*Boost provides a "generate" function, which is used to fill a range of integers. I did not write these, as they can be replaced by simply chosing the correct engine, an manually calling std.algo.fill.

Open to all suggestions/criticism, even the "minor" ones, such as documentation/licensing.

monarch dodra
Collaborator

I will be away from August 6 to August 20. Apologies for not being able to answer comments during that time.

std/random.d
((52 lines not shown))
  1059
+    //********
  1060
+    // Begin Payload
  1061
+    // We use a payload for "cheap" copy/move
  1062
+    // Payload implements:
  1063
+    //   seed(range)
  1064
+    //   fill
  1065
+    //   front
  1066
+    //   popFront
  1067
+    //   discard
  1068
+    //********
  1069
+    struct Payload
  1070
+    {    
  1071
+        Type[longLag] x;
  1072
+        size_t i = 0;
  1073
+
  1074
+        void ThrowSeedException()
1
Andrei Alexandrescu Owner
andralex added a note

lowercase t

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((64 lines not shown))
  1071
+        Type[longLag] x;
  1072
+        size_t i = 0;
  1073
+
  1074
+        void ThrowSeedException()
  1075
+        {
  1076
+            throw new Exception(text("LaggedFibonacciEngine.seed: Input range didn't provide enough"
  1077
+                " elements: Need ", longLag, " elements."));
  1078
+        }
  1079
+
  1080
+        void seed(Range)(Range range)
  1081
+        {
  1082
+            static if (isUnsigned!Type)
  1083
+            {
  1084
+                i = 0;
  1085
+                typeof(range.front) front;
  1086
+                for(size_t j = 0; j < longLag; j++)
1
Andrei Alexandrescu Owner
andralex added a note

use foreach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((67 lines not shown))
  1074
+        void ThrowSeedException()
  1075
+        {
  1076
+            throw new Exception(text("LaggedFibonacciEngine.seed: Input range didn't provide enough"
  1077
+                " elements: Need ", longLag, " elements."));
  1078
+        }
  1079
+
  1080
+        void seed(Range)(Range range)
  1081
+        {
  1082
+            static if (isUnsigned!Type)
  1083
+            {
  1084
+                i = 0;
  1085
+                typeof(range.front) front;
  1086
+                for(size_t j = 0; j < longLag; j++)
  1087
+                {
  1088
+                    if(range.empty) ThrowSeedException();
  1089
+                    front = range.front; range.popFront();
1
Andrei Alexandrescu Owner
andralex added a note

one statement per line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((68 lines not shown))
  1075
+        {
  1076
+            throw new Exception(text("LaggedFibonacciEngine.seed: Input range didn't provide enough"
  1077
+                " elements: Need ", longLag, " elements."));
  1078
+        }
  1079
+
  1080
+        void seed(Range)(Range range)
  1081
+        {
  1082
+            static if (isUnsigned!Type)
  1083
+            {
  1084
+                i = 0;
  1085
+                typeof(range.front) front;
  1086
+                for(size_t j = 0; j < longLag; j++)
  1087
+                {
  1088
+                    if(range.empty) ThrowSeedException();
  1089
+                    front = range.front; range.popFront();
  1090
+                    UIntType val = cast(UIntType)front;
1
Andrei Alexandrescu Owner
andralex added a note

don't stutter, use auto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((69 lines not shown))
  1076
+            throw new Exception(text("LaggedFibonacciEngine.seed: Input range didn't provide enough"
  1077
+                " elements: Need ", longLag, " elements."));
  1078
+        }
  1079
+
  1080
+        void seed(Range)(Range range)
  1081
+        {
  1082
+            static if (isUnsigned!Type)
  1083
+            {
  1084
+                i = 0;
  1085
+                typeof(range.front) front;
  1086
+                for(size_t j = 0; j < longLag; j++)
  1087
+                {
  1088
+                    if(range.empty) ThrowSeedException();
  1089
+                    front = range.front; range.popFront();
  1090
+                    UIntType val = cast(UIntType)front;
  1091
+                    for(size_t k = 1; k < (bits+31)/32; ++k)
1
Andrei Alexandrescu Owner
andralex added a note

use foreach and precompute (bits+31)/32 outside the loop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((72 lines not shown))
  1079
+
  1080
+        void seed(Range)(Range range)
  1081
+        {
  1082
+            static if (isUnsigned!Type)
  1083
+            {
  1084
+                i = 0;
  1085
+                typeof(range.front) front;
  1086
+                for(size_t j = 0; j < longLag; j++)
  1087
+                {
  1088
+                    if(range.empty) ThrowSeedException();
  1089
+                    front = range.front; range.popFront();
  1090
+                    UIntType val = cast(UIntType)front;
  1091
+                    for(size_t k = 1; k < (bits+31)/32; ++k)
  1092
+                    {
  1093
+                        if(range.empty) ThrowSeedException();
  1094
+                        front = range.front; range.popFront();
1
Andrei Alexandrescu Owner
andralex added a note

ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((83 lines not shown))
  1090
+                    UIntType val = cast(UIntType)front;
  1091
+                    for(size_t k = 1; k < (bits+31)/32; ++k)
  1092
+                    {
  1093
+                        if(range.empty) ThrowSeedException();
  1094
+                        front = range.front; range.popFront();
  1095
+                        val += cast(UIntType)front << 32*k;
  1096
+                    }
  1097
+                    x[j] = val & mask;
  1098
+                }
  1099
+                fill();
  1100
+            }
  1101
+            else //static if (isFloatingPoint!Type)
  1102
+            {
  1103
+                i = 0;
  1104
+                typeof(range.front) front;
  1105
+                for(size_t j = 0; j < longLag; ++j)
1
Andrei Alexandrescu Owner
andralex added a note

foreach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((82 lines not shown))
  1089
+                    front = range.front; range.popFront();
  1090
+                    UIntType val = cast(UIntType)front;
  1091
+                    for(size_t k = 1; k < (bits+31)/32; ++k)
  1092
+                    {
  1093
+                        if(range.empty) ThrowSeedException();
  1094
+                        front = range.front; range.popFront();
  1095
+                        val += cast(UIntType)front << 32*k;
  1096
+                    }
  1097
+                    x[j] = val & mask;
  1098
+                }
  1099
+                fill();
  1100
+            }
  1101
+            else //static if (isFloatingPoint!Type)
  1102
+            {
  1103
+                i = 0;
  1104
+                typeof(range.front) front;
1
Andrei Alexandrescu Owner
andralex added a note

why is this defined here? could be defined at initialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((101 lines not shown))
  1108
+                    RealType mult = divisor;
  1109
+                    for(int k = 0; k < bits/32; ++k)
  1110
+                    {
  1111
+                        if(range.empty) ThrowSeedException();
  1112
+                        front = range.front; range.popFront();
  1113
+                        val += cast(uint)front * mult;
  1114
+                        mult *= two32;
  1115
+                    }
  1116
+                    static if(mask != 0)
  1117
+                    {
  1118
+                        if(range.empty) ThrowSeedException();
  1119
+                        front = range.front; range.popFront();
  1120
+                        val += (cast(uint)front & mask) * mult;
  1121
+                    }
  1122
+                    assert(val >= 0);
  1123
+                    assert(val < 1);
1
Andrei Alexandrescu Owner
andralex added a note

consolidate asserts, print text(val) if assert fails

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((114 lines not shown))
  1121
+                    }
  1122
+                    assert(val >= 0);
  1123
+                    assert(val < 1);
  1124
+                    x[j] = val;
  1125
+                }
  1126
+                fill();
  1127
+            }
  1128
+        }
  1129
+
  1130
+        @safe nothrow
  1131
+        void fill()
  1132
+        {
  1133
+            static if (isUnsigned!Type)
  1134
+            {
  1135
+                // two loops to avoid costly modulo operations
  1136
+                for(size_t j = 0; j < shortLag; ++j)
1
Andrei Alexandrescu Owner
andralex added a note

foreach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((116 lines not shown))
  1123
+                    assert(val < 1);
  1124
+                    x[j] = val;
  1125
+                }
  1126
+                fill();
  1127
+            }
  1128
+        }
  1129
+
  1130
+        @safe nothrow
  1131
+        void fill()
  1132
+        {
  1133
+            static if (isUnsigned!Type)
  1134
+            {
  1135
+                // two loops to avoid costly modulo operations
  1136
+                for(size_t j = 0; j < shortLag; ++j)
  1137
+                    x[j] = (x[j] + x[j+(longLag-shortLag)]) & mask;
  1138
+                for(size_t j = shortLag; j < longLag; ++j)
1
Andrei Alexandrescu Owner
andralex added a note

foreach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((122 lines not shown))
  1129
+
  1130
+        @safe nothrow
  1131
+        void fill()
  1132
+        {
  1133
+            static if (isUnsigned!Type)
  1134
+            {
  1135
+                // two loops to avoid costly modulo operations
  1136
+                for(size_t j = 0; j < shortLag; ++j)
  1137
+                    x[j] = (x[j] + x[j+(longLag-shortLag)]) & mask;
  1138
+                for(size_t j = shortLag; j < longLag; ++j)
  1139
+                    x[j] = (x[j] + x[j-shortLag]) & mask;
  1140
+            }
  1141
+            else //static if (isFloatingPoint!Type)
  1142
+            {
  1143
+                // two loops to avoid costly modulo operations
  1144
+                for(size_t j = 0; j < shortLag; ++j)
1
Andrei Alexandrescu Owner
andralex added a note

foreach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((129 lines not shown))
  1136
+                for(size_t j = 0; j < shortLag; ++j)
  1137
+                    x[j] = (x[j] + x[j+(longLag-shortLag)]) & mask;
  1138
+                for(size_t j = shortLag; j < longLag; ++j)
  1139
+                    x[j] = (x[j] + x[j-shortLag]) & mask;
  1140
+            }
  1141
+            else //static if (isFloatingPoint!Type)
  1142
+            {
  1143
+                // two loops to avoid costly modulo operations
  1144
+                for(size_t j = 0; j < shortLag; ++j)
  1145
+                {
  1146
+                    RealType t = x[j] + x[j+(longLag-shortLag)];
  1147
+                    if(t >= one)
  1148
+                        t -= one;
  1149
+                    x[j] = t;
  1150
+                }
  1151
+                for(size_t j = shortLag; j < longLag; ++j)
1
Andrei Alexandrescu Owner
andralex added a note

foreach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((140 lines not shown))
  1147
+                    if(t >= one)
  1148
+                        t -= one;
  1149
+                    x[j] = t;
  1150
+                }
  1151
+                for(size_t j = shortLag; j < longLag; ++j)
  1152
+                {
  1153
+                    RealType t = x[j] + x[j-shortLag];
  1154
+                    if(t >= one)
  1155
+                        t -= one;
  1156
+                    x[j] = t;
  1157
+                }
  1158
+            }
  1159
+        }
  1160
+
  1161
+        @property @safe nothrow const
  1162
+        Type front()
3
Andrei Alexandrescu Owner
andralex added a note

ref?

monarch dodra Collaborator

I don't think so actually:
-None of the random generators return by ref
-Boost doesn't return by ref
-The return type is just a built in integer/floating point

Unless you mean it as a way to "write to front and modify the payload"? I don't think we should leak this kind of control, but don't have the PRNG knowledge to certify it. I'd rather not be the one to open this functionality

Maybe return by const ref then? Your call.

Andrei Alexandrescu Owner
andralex added a note

OK - by value's fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((161 lines not shown))
  1168
+        void popFront()
  1169
+        {
  1170
+            ++i;
  1171
+            if(i == longLag)
  1172
+            {
  1173
+              fill();
  1174
+              i = 0;
  1175
+            }
  1176
+        }
  1177
+
  1178
+        @safe nothrow
  1179
+        void discard(size_t n)
  1180
+        {
  1181
+            while(n >= longLag)
  1182
+            {
  1183
+              fill();
1
Andrei Alexandrescu Owner
andralex added a note

two space indent?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((185 lines not shown))
  1192
+        }
  1193
+    }
  1194
+    //********
  1195
+    // End Payload
  1196
+    //********
  1197
+    Payload* payload;
  1198
+
  1199
+  public:
  1200
+
  1201
+/**
  1202
+   Creates a new $(D LaggedFibonacciEngine) and calls $(D seed()).
  1203
+*/
  1204
+    static
  1205
+    This opCall()
  1206
+    {
  1207
+      This ret;
1
Andrei Alexandrescu Owner
andralex added a note

whoa, a lot of two space indent around here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((202 lines not shown))
  1209
+      return ret;
  1210
+    }
  1211
+  
  1212
+/**
  1213
+   Creates a new $(D LaggedFibonacciEngine) and calls $(D seed(value)).
  1214
+*/
  1215
+    this(T)(T value)
  1216
+        if (isIntegral!T)
  1217
+    {seed(value);}
  1218
+
  1219
+/**
  1220
+   Creates a new $(D LaggedFibonacciEngine) and calls $(D seed(range)).
  1221
+ */
  1222
+    this(Range)(Range range)
  1223
+        if (isInputRange!Range)
  1224
+    {seed(range);}
1
Andrei Alexandrescu Owner
andralex added a note

odd formatting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrei Alexandrescu
Owner

ping?

Andrei Alexandrescu
Owner

Sorry, saw the notice only now. Let's get back to this after Aug 20. Enjoy vacations!

Andrei Alexandrescu
Owner

due for review next week

monarch dodra
Collaborator

Thanks a lot for the re-read. In my defense, I tried to stay close to the boost code, but I think your comments are pertinent, and the code should be more D-Like.

I have nothing to add to any of the comments, except the "ref?" one.

Will provide a new version in 2 days.

monarch dodra
Collaborator

Ok, I did all the above fixes, plus a few internal tweaks. Also added the unittests I had forgotten to add.

Added complexity ratings, and merged a few docs with ditto's.

std/random.d
((191 lines not shown))
  1198
+    {
  1199
+        seed(value);
  1200
+    }
  1201
+
  1202
+/// ditto
  1203
+    this(Range)(Range range)
  1204
+        if (isInputRange!Range)
  1205
+    {
  1206
+        seed(range);
  1207
+    }
  1208
+
  1209
+/**
  1210
+   Tells if the generator has been seeded/initialized
  1211
+ */
  1212
+    @property @safe nothrow const
  1213
+    bool seeded()
2
monarch dodra Collaborator

Is this a good name? Maybe "isSeeded" instead?

Property?

Andrei Alexandrescu Owner
andralex added a note

seeded is fine as long as it's a property

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
... ...
@@ -1008,6 +1008,443 @@ unittest
1008 1008
     }
1009 1009
 }
1010 1010
 
  1011
+/**
  1012
+ * Lagged Fibonacci generator.
  1013
+ *
  1014
+ * The single Engine can be started with either an Unsigned Integral Type, or a Real Type.
1
Andrei Alexandrescu Owner
andralex added a note

Why all of the capitalized names? If they represent reified concepts you'd need to define them. (I don't think that's necessary though.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((39 lines not shown))
  1046
+        enum UIntType mask = ~(~cast(UIntType)0 << bits); //A mask that is the size of "bits"
  1047
+        enum UIntType maxPrivate = mask; //maxPrivate is needed to have a single documented max
  1048
+    }
  1049
+    else //static if (isFloatingPoint!Type)
  1050
+    {
  1051
+        alias Type RealType;
  1052
+        enum RealType two32 = 2.0^^32;
  1053
+        enum RealType divisor = 1.0/(2.0^^bits);
  1054
+        enum size_t kMax = bits/32;     //Amount of whole "u32" types that can fit in a type of size bits
  1055
+        enum uint mask = ~((~cast(uint)0) << (bits%32)); //A mask of the remaining bits
  1056
+        enum RealType maxPrivate = 1; //maxPrivate is needed to have a single documented max
  1057
+    }
  1058
+
  1059
+  private:
  1060
+    // We use a payload for "cheap" copy/move
  1061
+    struct Payload
2
Andrei Alexandrescu Owner
andralex added a note
static
monarch dodra Collaborator

I did not know what static meant in this context, so I inquired over at digitalmars.D.learn.

It turns out that it is not clear whether or not the "outer" feature applies for nested structs or not :
it doesn't work, but it could just be "not yet implemented".

May I request your participation?
http://forum.dlang.org/thread/cwohbwozowasyfpkcoim@forum.dlang.org

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((50 lines not shown))
  1057
+    }
  1058
+
  1059
+  private:
  1060
+    // We use a payload for "cheap" copy/move
  1061
+    struct Payload
  1062
+    {    
  1063
+        Type[longLag] x;
  1064
+        size_t i = 0;
  1065
+
  1066
+        void throwSeedException() const
  1067
+        {
  1068
+            throw new Exception(text("LaggedFibonacciEngine.seed: Input range didn't provide"
  1069
+                " enough elements: Need ", longLag, " elements."));
  1070
+        }
  1071
+
  1072
+        void seed(Range)(Range range)
2
Andrei Alexandrescu Owner
andralex added a note

constraint?

monarch dodra Collaborator

This method is internal to the private Payload struct, the constraint should already be validated in the public "seed(*)" methods. It could warrant an internal static assert for sanity though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrei Alexandrescu andralex commented on the diff
std/random.d
((92 lines not shown))
  1099
+                        range.popFront();
  1100
+                        mult *= two32;
  1101
+                    }
  1102
+                    static if(mask != 0)
  1103
+                    {
  1104
+                        if(range.empty) throwSeedException();
  1105
+                        val += (cast(uint)range.front & mask) * mult;
  1106
+                        range.popFront();
  1107
+                    }
  1108
+                    assert(val >= 0.0, text("LaggedFibonacciEngine: Floating point value is smaller than 0: ", val));
  1109
+                    assert(val < 1.0,  text("LaggedFibonacciEngine: Floating point value is bigger than 1: ", val));
  1110
+                    x[j] = val;
  1111
+                }
  1112
+            }
  1113
+            fill();
  1114
+        }
2
Andrei Alexandrescu Owner
andralex added a note

Since the bodies are so different it would make sense to define two functions with different constraints.

monarch dodra Collaborator

Actually, it is really just an implementation detail about how the internal array is filled.
The constraints are the same, and they both finish with a call to "fill". I'd rather keep the static if inside.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((123 lines not shown))
  1130
+                foreach(j; 0..shortLag)
  1131
+                {
  1132
+                    RealType t = x[j] + x[j+(longLag-shortLag)];
  1133
+                    if(t >= 1.0)
  1134
+                        t -= 1.0;
  1135
+                    x[j] = t;
  1136
+                }
  1137
+                foreach(j; shortLag..longLag)
  1138
+                {
  1139
+                    RealType t = x[j] + x[j-shortLag];
  1140
+                    if(t >= 1.0)
  1141
+                        t -= 1.0;
  1142
+                    x[j] = t;
  1143
+                }
  1144
+            }
  1145
+        }
1
Andrei Alexandrescu Owner
andralex added a note

ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((151 lines not shown))
  1158
+            {
  1159
+                fill();
  1160
+                i = 0;
  1161
+            }
  1162
+        }
  1163
+
  1164
+        @safe nothrow
  1165
+        void discard(size_t n)
  1166
+        {
  1167
+            while(n >= longLag)
  1168
+            {
  1169
+                fill();
  1170
+                n -= longLag;
  1171
+            }
  1172
+            i += n;
  1173
+            if(i >= longLag)
2
Andrei Alexandrescu Owner
andralex added a note

Not clear on the context, but shouldn't this be while?

monarch dodra Collaborator

The while is already processed in "while(n >= longLag)".
After that while we are guaranteed that i + n < 2*longLag.

In theory, I could have just started with i+=n and looped on "i"... But there is an integer overflow risk there => Loop on "n", finish the last iteration on n + i.

I'll add an in-code comment for posterity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrei Alexandrescu andralex commented on the diff
std/random.d
((158 lines not shown))
  1165
+        void discard(size_t n)
  1166
+        {
  1167
+            while(n >= longLag)
  1168
+            {
  1169
+                fill();
  1170
+                n -= longLag;
  1171
+            }
  1172
+            i += n;
  1173
+            if(i >= longLag)
  1174
+            {
  1175
+                fill();
  1176
+                i -= longLag;
  1177
+            }
  1178
+        }
  1179
+    }
  1180
+    Payload* payload;
3
Andrei Alexandrescu Owner
andralex added a note

RefCounted!Payload?

monarch dodra Collaborator

Isn't it simpler to just let the GC handle it? I don't think there is any reason to force RAII on the allocated space here.

Andrei Alexandrescu Owner

fine (this actually works great with the suggestion to make this a class)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((166 lines not shown))
  1173
+            if(i >= longLag)
  1174
+            {
  1175
+                fill();
  1176
+                i -= longLag;
  1177
+            }
  1178
+        }
  1179
+    }
  1180
+    Payload* payload;
  1181
+
  1182
+  public:
  1183
+
  1184
+/**
  1185
+   Creates a new $(D LaggedFibonacciEngine) and calls $(D seed()).
  1186
+*/
  1187
+    static
  1188
+    This opCall()
2
Andrei Alexandrescu Owner
andralex added a note

Unfortunately this is a bit confusing...

LaggedFibonacciEngine!(stuff) x; // not seeded
auto y = LaggedFibonacciEngine!(stuff)(); // seeded

Not sure whether this idiom will make it or not in the long run.

monarch dodra Collaborator

Written like that, I agree, but like this:

LaggedFibonacci607 x; // not seeded (LaggedFibonacci607.init)
LaggedFibonacci607 y = LaggedFibonacci607(); // seeded
LaggedFibonacci607 y = LaggedFibonacci607(31); // seeded

This goes back to my comment that it would really be helpful if D allowed for "Constructor with no argument" (Not to be confused with "Default Constructor"). This would help in particular for all structs that have payload idioms: RefCounted, Array, etc...


But this is not the place to discuss this. Regarding Fibonacci, I see 3 options:
*1:Leave it as is.
*2:Remove ALL constuctors, and request an explicit call to seed.
*3:Make it a class. //Meh

Also, thinking a bit harder about it: Is it really the implementation's job to force reference semantics onto the generator? Shouldn't we just make the generator a BIG struct, but strongly hint that it should be allocated on the heap?

auto gen = new LaggedFibonacci607;
gen.seed();
gen.front;
...

I think it is a better approach here.


I don't have the experience/hindsight to answer these questions myself, so I'm deferring to your best judgement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((215 lines not shown))
  1222
+   None: Uses a default seed.
  1223
+   T value: Uses a $(D MinstdRand0) generator seeded with value
  1224
+   InputRange range: Uses range.
  1225
+    
  1226
+   Throws:
  1227
+   $(D Exception) if the InputRange didn't provide enough elements to seed the generator.
  1228
+   The number of elements required is the 'longLag' template parameter of the LaggedFibonacciEngine struct.
  1229
+*/
  1230
+    void seed()()
  1231
+    {
  1232
+        seed(defaultSeed);
  1233
+    }
  1234
+
  1235
+/// ditto
  1236
+    void seed(T)(T value)
  1237
+        if (isIntegral!T)
1
Andrei Alexandrescu Owner
andralex added a note

Let's add here the constraint that sizeof(value) <= 4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((257 lines not shown))
  1264
+    }
  1265
+
  1266
+/// dito
  1267
+    alias dup save;
  1268
+
  1269
+/**
  1270
+   Returns true if the two generators will produce identical
  1271
+   sequences of outputs.
  1272
+    
  1273
+   $(BIGOH 1) average case complexity.
  1274
+   $(BIGOH longLag) worst case complexity.
  1275
+ */
  1276
+    @safe nothrow const
  1277
+    bool opEquals(const(This) b)
  1278
+    {
  1279
+        return (payload == b.payload) ||
1
Andrei Alexandrescu Owner
andralex added a note

is?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrei Alexandrescu
Owner

Getting there. Nice work!

monarch dodra
Collaborator

Thank you again for the re-read. I did not have time to work on it this week, but I needed your feedback on the constructor/payload issue before continuing anyways. Regarding everything else, I think one more week of work, and everything should be final.

D is still a learning process for me, so I don't want to rush this.

Andrei Alexandrescu
Owner

Just do what the Mersenne Twister does (i.e. ctor with seed and also seed method). We can always add the opCall trick if we decide it's worth it. For now let's aim for conservative uniformity. Thanks!

monarch dodra
Collaborator

Fixed what needed to be fixed. Made everything as conservative as and straight forward as possible. I kept the reference semantics. It is kind of discussed in this thread:
http://forum.dlang.org/thread/spjfsyaqrcpboypgfrsd@forum.dlang.org
And the rationale in this post:
http://forum.dlang.org/thread/spjfsyaqrcpboypgfrsd@forum.dlang.org?page=3#post-bloacgckmnejwhbnjdlp:40forum.dlang.org

Long story short: It is much more convenient for the type itself to provide reference semantics, then for the developers to do it themselves. As a reminder, LaggedFibonacci44497 is 300K in size (!)

Anyways, I don't think there is much else to say here on my end.

I was still curious about nested structs and have a hidden "outer" pointer:
According to http://dlang.org/struct.html, they do.
According to TDPL, they don't.

Could we have your input in this discussion? Or here and I'll relay.
http://forum.dlang.org/thread/cwohbwozowasyfpkcoim@forum.dlang.org

std/random.d
@@ -1008,6 +1008,462 @@ unittest
1008 1008
     }
1009 1009
 }
1010 1010
 
  1011
+/**
  1012
+* Lagged Fibonacci generator.
  1013
+*
  1014
+* The single engine can be started with either an unsigned integral type,
  1015
+* or a floating point type. Unsigned Integral Type engine will generate
  1016
+* numbers in the range [0 .. 2^^bits). Real Type will generate
  1017
+* floating points in the range [0 .. 1), with a step of 1/2^^bits.
  1018
+*
  1019
+* The engine uses reference semantics, and models a ForwardRange.
2
Andrei Alexandrescu Owner

s/ForwardRange/$(D ForwardRange)/

Andrei Alexandrescu Owner

The fact that it uses reference semantics must be emphasize and discussed with examples. Documentation should also mention that this semantics is different from all other types in this module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
@@ -1008,6 +1008,462 @@ unittest
1008 1008
     }
1009 1009
 }
1010 1010
 
  1011
+/**
  1012
+* Lagged Fibonacci generator.
  1013
+*
  1014
+* The single engine can be started with either an unsigned integral type,
  1015
+* or a floating point type. Unsigned Integral Type engine will generate
  1016
+* numbers in the range [0 .. 2^^bits). Real Type will generate
  1017
+* floating points in the range [0 .. 1), with a step of 1/2^^bits.
  1018
+*
  1019
+* The engine uses reference semantics, and models a ForwardRange.
  1020
+*
  1021
+* The engine must be seeded prior to use.
  1022
+*
  1023
+* Ported from boost 1.50 on July 30th 2012
  1024
+*/
  1025
+struct LaggedFibonacciEngine(Type, size_t bits, size_t longLag, size_t shortLag)
1
Andrei Alexandrescu Owner

Since the working set of LaggedFibonacciEngine is so large, it may be worthwhile just making it a class. That would simplify implementation considerably. Make methods final.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrei Alexandrescu andralex commented on the diff
std/random.d
((18 lines not shown))
  1025
+struct LaggedFibonacciEngine(Type, size_t bits, size_t longLag, size_t shortLag)
  1026
+  if (isUnsigned!Type || isFloatingPoint!Type)
  1027
+{
  1028
+  public:
  1029
+    /// Mark this as a Rng
  1030
+    enum bool isUniformRandom = true;
  1031
+    /// Always $(D false) (random generators are infinite ranges).
  1032
+    enum empty = false;
  1033
+    /// Returns the smallest value that the generator can produce.
  1034
+    enum Type min = 0;
  1035
+    /// Returns the largest value that the generator can produce.
  1036
+    enum Type max = maxPrivate;
  1037
+
  1038
+  private:
  1039
+    enum uint defaultSeed = 331u;
  1040
+    alias typeof(this) This; //A *non-qualified* alias for typof(this).
1
Andrei Alexandrescu Owner

typo :o)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
std/random.d
((106 lines not shown))
  1113
+                    assert(val >= 0.0, text("LaggedFibonacciEngine: Floating point value is smaller than 0: ", val));
  1114
+                    assert(val < 1.0, text("LaggedFibonacciEngine: Floating point value is bigger than 1: ", val));
  1115
+                    x[j] = val;
  1116
+                }
  1117
+            }
  1118
+            fill();
  1119
+        }
  1120
+
  1121
+        @safe nothrow
  1122
+        void fill()
  1123
+        {
  1124
+            static if (isUnsigned!Type)
  1125
+            {
  1126
+                // two loops to avoid costly modulo operations
  1127
+                foreach(j; 0..shortLag)
  1128
+                    x[j] = (x[j] + x[j+(longLag-shortLag)]) & mask;
1
Andrei Alexandrescu Owner

I'm seeing inconsistent use of operator spacing, even in the same line. I'll use this as a pretext to require spacing around all operators, like in the rest of phobos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
monarch dodra
Collaborator

Closed for development

monarch dodra monarchdodra closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 457 additions and 0 deletions. Show diff stats Hide diff stats

  1. 457  std/random.d
457  std/random.d
@@ -1008,6 +1008,463 @@ unittest
1008 1008
     }
1009 1009
 }
1010 1010
 
  1011
+/**
  1012
+* Lagged Fibonacci generator.
  1013
+*
  1014
+* The engine can be instanciated with either an unsigned integral type,
  1015
+* or a floating point type. An unsigned integral engine will generate
  1016
+* numbers in the range [0 .. 2^^bits). A floating point engine will generate
  1017
+* floating points in the range [0 .. 1), with a step of 1/2^^bits.
  1018
+*
  1019
+* A $(D LaggedFibonacciEngine) must maintain a very large set of data
  1020
+* to function. Because of this, and contrary to all other generators
  1021
+* in this module, the engine is allocated on the heap, and uses
  1022
+* reference semantics.
  1023
+*
  1024
+* The engine must be seeded prior to use. Use of an un-seeded
  1025
+* (un-initialized) engine will generate an assertion error.
  1026
+*
  1027
+* The $(D LaggedFibonacciEngine) models an $(D Infinite)
  1028
+* $(D ForwardRange).
  1029
+*
  1030
+* Ported from boost 1.50 on July 30th 2012
  1031
+*/
  1032
+struct LaggedFibonacciEngine(Type, size_t bits, size_t longLag, size_t shortLag)
  1033
+  if (isUnsigned!Type || isFloatingPoint!Type)
  1034
+{
  1035
+  public:
  1036
+    /// Mark this as a Rng
  1037
+    enum bool isUniformRandom = true;
  1038
+    /// Always $(D false) (random generators are infinite ranges).
  1039
+    enum empty = false;
  1040
+    /// Returns the smallest value that the generator can produce.
  1041
+    enum Type min = 0;
  1042
+    /// Returns the largest value that the generator can produce.
  1043
+    enum Type max = maxPrivate;
  1044
+
  1045
+  private:
  1046
+    enum uint defaultSeed = 331u;
  1047
+    alias typeof(this) This; //A *non-qualified* alias for typof(this).
  1048
+
  1049
+    //Conditional Aliases/enums/asserts
  1050
+    static if (isUnsigned!Type)
  1051
+    {
  1052
+        static assert(UIntType.sizeof*8 >= bits, text("Provided UIntType not large enough to hold ", bits, "-bit integers."));
  1053
+
  1054
+        alias Type UIntType;
  1055
+        enum size_t kMax = (bits+31)/32; //Total amount of "u32" types needed to fill a type of size bits
  1056
+        enum UIntType mask = cast(UIntType)~(~cast(UIntType)0 << bits); //A mask that is the size of "bits"
  1057
+        enum UIntType maxPrivate = mask; //maxPrivate is needed to have a single documented max
  1058
+    }
  1059
+    else //static if (isFloatingPoint!Type)
  1060
+    {
  1061
+        alias Type RealType;
  1062
+        enum RealType two32 = 2.0^^32;
  1063
+        enum RealType divisor = 1.0/(2.0^^bits);
  1064
+        enum size_t kMax = bits/32; //Amount of whole "u32" types that can fit in a type of size bits
  1065
+        enum uint mask = ~((~cast(uint)0) << (bits%32)); //A mask of the remaining bits
  1066
+        enum RealType maxPrivate = 1; //maxPrivate is needed to have a single documented max
  1067
+    }
  1068
+
  1069
+  private:
  1070
+    //Payload; Reference semantics
  1071
+    static struct Payload
  1072
+    {
  1073
+        Type[longLag] x;
  1074
+        size_t i = 0;
  1075
+
  1076
+        void throwSeedException() const
  1077
+        {
  1078
+            throw new Exception(
  1079
+                text("LaggedFibonacciEngine.seed: Input range didn't provide enough elements: Need ",
  1080
+                    longLag, " elements."));
  1081
+        }
  1082
+
  1083
+        void seed(Range)(Range range)
  1084
+            //Range conditions were validated in public interface
  1085
+        {
  1086
+            i = 0;
  1087
+            static if (isUnsigned!Type)
  1088
+            {
  1089
+                foreach(j; 0..longLag)
  1090
+                {
  1091
+                    UIntType val = 0;
  1092
+                    foreach(k; 0..kMax)
  1093
+                    {
  1094
+                        if(range.empty) throwSeedException();
  1095
+                        val += cast(UIntType)range.front << 32*k;
  1096
+                        range.popFront();
  1097
+                    }
  1098
+                    x[j] = val & mask;
  1099
+                }
  1100
+            }
  1101
+            else //static if (isFloatingPoint!Type)
  1102
+            {
  1103
+                foreach(j; 0..longLag)
  1104
+                {
  1105
+                    RealType val = 0;
  1106
+                    RealType mult = divisor;
  1107
+                    foreach(k; 0..kMax)
  1108
+                    {
  1109
+                        if(range.empty) throwSeedException();
  1110
+                        val += cast(uint)range.front * mult;
  1111
+                        range.popFront();
  1112
+                        mult *= two32;
  1113
+                    }
  1114
+                    static if(mask != 0)
  1115
+                    {
  1116
+                        if(range.empty) throwSeedException();
  1117
+                        val += (cast(uint)range.front & mask) * mult;
  1118
+                        range.popFront();
  1119
+                    }
  1120
+                    assert(val >= 0.0, text("LaggedFibonacciEngine: Floating point value is smaller than 0: ", val));
  1121
+                    assert(val < 1.0, text("LaggedFibonacciEngine: Floating point value is bigger than 1: ", val));
  1122
+                    x[j] = val;
  1123
+                }
  1124
+            }
  1125
+            fill();
  1126
+        }
  1127
+
  1128
+        @safe nothrow
  1129
+        void fill()
  1130
+        {
  1131
+            //The basic algorithm might not be visible at first.
  1132
+            //
  1133
+            //It is the same for both integer/float:
  1134
+            //foreach(i; 0..longLag)
  1135
+            //{
  1136
+            //    x[i] += x[i - shortLag]; //*with modulo wrap around if negative*
  1137
+            //    x[i].ApplyMask
  1138
+            //}
  1139
+            //Where ApplyMask is
  1140
+            //*Integer: "x[i] &= mask"
  1141
+            //*Floats: "if(x[i]>1) x[i] -= 1"
  1142
+            //
  1143
+            //We exploit vectorial operations for perfomance.
  1144
+            //Data is iterated on in strips of shortLag length.
  1145
+            //Entire "stips at once" are processed in addAndMask.
  1146
+            //There is a special "first iteration" case to avoid the costly modulo operation
  1147
+            //There is a special "final iteration" for the last (shorter) strip
  1148
+            @safe nothrow void addAndMask(Type[] r1, Type[] r2) 
  1149
+            {
  1150
+                try //@@@ 8651
  1151
+                {
  1152
+                    r1[] += r2[];
  1153
+                    static if (isUnsigned!Type) r1[] &= mask;
  1154
+                    else foreach(ref t; r1[]) if(t >= 1.0) t -= 1.0;
  1155
+                }
  1156
+                catch(Exception) assert(0, "Unexpected exception in LaggedFibonacciEngine.Payload.Fill");
  1157
+            }
  1158
+    
  1159
+            addAndMask(x[0 .. shortLag], x[longLag - shortLag .. longLag]);
  1160
+            size_t low = shortLag;
  1161
+            for( ; low + shortLag <= longLag ; low += shortLag) //Iteration necessarry to avoid overlap operations
  1162
+                addAndMask(x[low .. low + shortLag], x[low - shortLag .. low]);
  1163
+            addAndMask(x[low .. longLag], x[low - shortLag .. longLag - shortLag]);
  1164
+        }
  1165
+
  1166
+        @property @safe nothrow const
  1167
+        Type front()
  1168
+        {
  1169
+            return x[i];
  1170
+        }
  1171
+
  1172
+        @safe nothrow
  1173
+        void popFront()
  1174
+        {
  1175
+            ++i;
  1176
+            if(i == longLag)
  1177
+            {
  1178
+                fill();
  1179
+                i = 0;
  1180
+            }
  1181
+        }
  1182
+
  1183
+        @safe nothrow
  1184
+        void discard(size_t n)
  1185
+        {
  1186
+            //Do NOT replace with n+=i, or risk an integer overflow
  1187
+            while(n >= longLag)
  1188
+            {
  1189
+                fill();
  1190
+                n -= longLag;
  1191
+            }
  1192
+            //Now that n is smaller, we can take i into account and do the last iteration
  1193
+            i += n;
  1194
+            if(i >= longLag)
  1195
+            {
  1196
+                fill();
  1197
+                i -= longLag;
  1198
+            }
  1199
+        }
  1200
+    }
  1201
+    Payload* payload;
  1202
+
  1203
+  public:
  1204
+/**
  1205
+Tells if the generator has been seeded/initialized
  1206
+*/
  1207
+    @property @safe nothrow const
  1208
+    bool seeded()
  1209
+    {
  1210
+        return payload != null;
  1211
+    }
  1212
+
  1213
+/**
  1214
+Seeds the $(D LaggedFibonacciEngine)
  1215
+Parameters:
  1216
+None: Uses a default seed.
  1217
+T value: Uses a $(D MinstdRand0) generator seeded with value
  1218
+InputRange range: Uses range.
  1219
+Throws:
  1220
+$(D Exception) if the InputRange didn't provide enough elements to seed the generator.
  1221
+The number of elements required is the 'longLag' template parameter of the LaggedFibonacciEngine struct.
  1222
+*/
  1223
+    void seed()()
  1224
+    {
  1225
+        seed(defaultSeed);
  1226
+    }
  1227
+
  1228
+/// ditto
  1229
+    void seed()(uint value)
  1230
+    {
  1231
+        auto gen = MinstdRand0(value);
  1232
+        seed(gen);
  1233
+    }
  1234
+
  1235
+/// ditto
  1236
+    void seed(InputRange)(InputRange range)
  1237
+        if ( isInputRange!InputRange && isIntegral!(ElementType!InputRange) &&
  1238
+            ( ElementType!InputRange.sizeof >= 4 || ElementType!InputRange.sizeof >= Type.sizeof ) )
  1239
+    {
  1240
+        if(!payload) payload = new Payload;
  1241
+        payload.seed(range);
  1242
+    }
  1243
+
  1244
+/**
  1245
+Creates a deep copy of the generator; Saves the range.
  1246
+$(BIGOH longLag) complexity.
  1247
+*/
  1248
+    @property const
  1249
+    auto dup()
  1250
+    {
  1251
+        This ret; //non qualified typeof(this)
  1252
+        if(payload)
  1253
+        {
  1254
+            ret.payload = new Payload;
  1255
+            *ret.payload = *payload;
  1256
+        }
  1257
+        return ret;
  1258
+    }
  1259
+
  1260
+/// dito
  1261
+    alias dup save;
  1262
+
  1263
+/**
  1264
+Returns true if the two generators will produce identical
  1265
+sequences of outputs.
  1266
+$(BIGOH 1) average case complexity.
  1267
+$(BIGOH longLag) worst case complexity.
  1268
+*/
  1269
+    @safe nothrow const
  1270
+    bool opEquals(const(This) b)
  1271
+    {
  1272
+        return (payload is b.payload) ||
  1273
+               (payload && b.payload && *payload == *b.payload);
  1274
+    }
  1275
+
  1276
+    private enum payloadErrorText = "LaggedFibonacciEngine: Attempt to use an un-initialized engine";
  1277
+
  1278
+/**
  1279
+Returns the current value of the generator. Constant complexity.
  1280
+*/
  1281
+    @property @safe nothrow const
  1282
+    Type front()
  1283
+    {
  1284
+        assert(payload, payloadErrorText);
  1285
+        return payload.front;
  1286
+    }
  1287
+
  1288
+/**
  1289
+Advances to the next value of the generator.
  1290
+
  1291
+Amortized constant time: $(BIGOH longLag) in case of generator refill,
  1292
+$(BIGOH 1) otherwise. The generator is refilled every time the generator
  1293
+is advanced by $(D longLag).
  1294
+*/
  1295
+    @safe nothrow
  1296
+    void popFront()
  1297
+    {
  1298
+        assert(payload, payloadErrorText);
  1299
+        payload.popFront();
  1300
+    }
  1301
+
  1302
+/**
  1303
+Advances the state of the generator by $(D z). More efficient than calling popFront $(D z) times.
  1304
+
  1305
+Amortized $(BIGOH z) time: advancing the get pointer is constant time, but each refill
  1306
+(every time the engine is advanced by $(D longLag)) will still require $(D longLag) time.
  1307
+*/
  1308
+    @safe nothrow
  1309
+    void discard(size_t z)
  1310
+    {
  1311
+        assert(payload, payloadErrorText);
  1312
+        payload.discard(z);
  1313
+    }
  1314
+}
  1315
+
  1316
+/**
  1317
+Define $(D_PARAM LaggedFibonacciEngine) generators with well-chosen
  1318
+parameters.
  1319
+$(D LaggedFibonacci) is an implementation chosen LaggedFibonacci engine.
  1320
+*/
  1321
+alias LaggedFibonacciEngine!(double, 48,   607,   273)   LaggedFibonacci607;
  1322
+alias LaggedFibonacciEngine!(double, 48,  1279,   418)  LaggedFibonacci1279; /// ditto
  1323
+alias LaggedFibonacciEngine!(double, 48,  2281,  1252)  LaggedFibonacci2281; /// ditto
  1324
+alias LaggedFibonacciEngine!(double, 48,  3217,   576)  LaggedFibonacci3217; /// ditto
  1325
+alias LaggedFibonacciEngine!(double, 48,  4423,  2098)  LaggedFibonacci4423; /// ditto
  1326
+alias LaggedFibonacciEngine!(double, 48,  9689,  5502)  laggedFibonacci9689; /// ditto
  1327
+alias LaggedFibonacciEngine!(double, 48, 19937,  9842) LaggedFibonacci19937; /// ditto
  1328
+alias LaggedFibonacciEngine!(double, 48, 23209, 13470) LaggedFibonacci23209; /// ditto
  1329
+alias LaggedFibonacciEngine!(double, 48, 44497, 21034) LaggedFibonacci44497; /// ditto
  1330
+alias LaggedFibonacci607 LaggedFibonacci;
  1331
+
  1332
+unittest
  1333
+{
  1334
+    //Data aquired from boost implementation
  1335
+
  1336
+    //First 5 numbers of the different implementations
  1337
+    immutable(long[][]) referenceInt = [
  1338
+        [111357645752581, 223546595107190, 276701472505536, 183354198929810, 259724424734707], //  607
  1339
+        [ 18516896918599, 266714042254566, 214677200432103, 238166017619873, 253427522088996], // 1279
  1340
+        [209321651636266, 234403323170313, 44940711525953,  270585935924073, 182951237378271], // 2281
  1341
+        [102247219917095, 111280010135238, 8455294141221,   268442649885962, 181826007198542], // 3217
  1342
+        [212931708246959,  67779908108345, 109786160258021, 197789232895085, 173746813492116], // 4423
  1343
+        [136413310818494,  11236510562599, 52970671921490,  193128799152157, 216674819310303], // 9689
  1344
+        [  7126909551884,  86072826243616, 80675133038411,  251970503587899, 124423863440468], //19937
  1345
+        [146111616638474, 136512418917669, 259280540566850, 250350614477568, 143029744160684], //23209
  1346
+        [263558154538665, 124374555248232, 92765545546811,  244707388756702, 173997194527172], //44497
  1347
+    ];
  1348
+
  1349
+    immutable(double[][]) referenceFloat = [
  1350
+        [ 0.395622,  0.794197,  0.983041, 0.651405, 0.922727], //  607
  1351
+        [0.0657852,  0.947559,  0.762687, 0.846136, 0.900355], // 1279
  1352
+        [  0.74366,  0.832768,  0.159661, 0.961314, 0.649973], // 2281
  1353
+        [ 0.363255,  0.395346, 0.0300392,   0.9537, 0.645976], // 3217
  1354
+        [ 0.756485,  0.240803,  0.390039, 0.702689, 0.617273], // 4423
  1355
+        [ 0.484637, 0.0399201,   0.18819, 0.686131, 0.769784], // 9689
  1356
+        [0.0253199,  0.305792,  0.286616, 0.895179, 0.442042], //19937
  1357
+        [ 0.519093,   0.48499,   0.92115, 0.889424, 0.508144], //23209
  1358
+        [ 0.936347,  0.441867,  0.329569, 0.869375, 0.618162], //44497
  1359
+    ];
  1360
+
  1361
+    //Numbers [5000-5005)
  1362
+    immutable(long[][]) referenceInt5000 = [
  1363
+        [266635669194050,  70515619676375,  83207904227719, 281373199363211, 116250967224354], //  607
  1364
+        [110135446738590,  36692226031177, 209483500649828, 268031999359600, 248891650590185], // 1279
  1365
+        [217776584741215, 215117240100973,  52618792839041,  22635584224685, 198265981921064], // 2281
  1366
+        [ 93750178568681, 165145932004335,  78808838225286, 232759037338400,  61858783708817], // 3217
  1367
+        [275211622257559,  20961152411805,  16486400488039, 239571949617653, 119147348429321], // 4423
  1368
+        [103616703846189, 134586041061516,   4244911899451,   5940954177811,  10074550564657], // 9689
  1369
+        [255367856135618, 277538191528067, 178667831440893,  22734204862614, 227099404643056], //19937
  1370
+        [121591596418969, 167750783695320,  32717268361943, 281360144972641, 255183279435442], //23209
  1371
+        [279851845097169, 249217869577016, 152361400411229, 261604351718748,  76994486632824], //44497
  1372
+    ];
  1373
+
  1374
+    immutable(double[][]) referenceFloat5000 = [
  1375
+        [ 0.94728, 0.250522,  0.295614,  0.999638, 0.413006], //  607
  1376
+        [ 0.39128, 0.130357,  0.744235,  0.952241, 0.884241], // 1279
  1377
+        [0.773698,  0.76425,   0.18694, 0.0804177, 0.704382], // 2281
  1378
+        [0.333068, 0.586716,  0.279985,  0.826926, 0.219767], // 3217
  1379
+        [0.977748, 0.074469, 0.0585715,  0.851131, 0.423296], // 4423
  1380
+        [ 0.36812, 0.478146,  0.015081, 0.0211065, 0.035792], // 9689
  1381
+        [0.907249, 0.986014,  0.634756, 0.0807681, 0.806819], //19937
  1382
+        [ 0.43198, 0.595971,  0.116235,  0.999592, 0.906593], //23209
  1383
+        [0.994233,   0.8854,  0.541296,  0.929405, 0.273539], //44497
  1384
+    ];
  1385
+
  1386
+    void doTests(T, V)(V values, V values5000)
  1387
+    {
  1388
+        foreach (I, Type; TypeTuple!(
  1389
+                  LaggedFibonacciEngine!(T, 48,   607,   273),
  1390
+                  LaggedFibonacciEngine!(T, 48,  1279,   418),
  1391
+                  LaggedFibonacciEngine!(T, 48,  2281,  1252),
  1392
+                  LaggedFibonacciEngine!(T, 48,  3217,   576),
  1393
+                  LaggedFibonacciEngine!(T, 48,  4423,  2098),
  1394
+                  LaggedFibonacciEngine!(T, 48,  9689,  5502),
  1395
+                  LaggedFibonacciEngine!(T, 48, 19937,  9842),
  1396
+                  LaggedFibonacciEngine!(T, 48, 23209, 13470),
  1397
+                  LaggedFibonacciEngine!(T, 48, 44497, 21034)
  1398
+              )
  1399
+        )
  1400
+        {
  1401
+            auto r = Type();
  1402
+            assert(!r.seeded);
  1403
+            r.seed();
  1404
+            assert(r.seeded);
  1405
+
  1406
+            assert(equal!approxEqual(r.take(5), values[I]));
  1407
+            r.discard(4995);
  1408
+            assert(equal!approxEqual(r.take(5), values5000[I]));
  1409
+
  1410
+            r.seed(); //check re-seed and save
  1411
+            assert(equal!approxEqual(r.save.take(5), values[I]));
  1412
+            r.discard(5000);
  1413
+            assert(equal!approxEqual(r.save.take(5), values5000[I]));
  1414
+
  1415
+            r.discard(5000);
  1416
+            assert(r == r);
  1417
+            assert(equal!approxEqual(r.save.take(5), r.save.take(5)));
  1418
+            auto ra = Type();
  1419
+            auto rb = Type();
  1420
+            ra.seed();
  1421
+            rb.seed();
  1422
+            assert(ra !is rb);
  1423
+            assert(ra == rb);
  1424
+            ra.popFront();
  1425
+            assert(ra != rb);
  1426
+        }
  1427
+
  1428
+        //Verify validity of output "steps"
  1429
+        auto fourGenerator = LaggedFibonacciEngine!(T, 4, 607, 273)();
  1430
+        fourGenerator.seed(unpredictableSeed());
  1431
+        T[] sixteen = [ 0,  1,  2,  3,
  1432
+                        4,  5,  6,  7,
  1433
+                        8,  9, 10, 11,
  1434
+                       12, 13, 14, 15];
  1435
+        static if(is(T == double))
  1436
+        {
  1437
+            sixteen[] /= 16.0;
  1438
+        }
  1439
+
  1440
+        foreach(v; fourGenerator.take(10))
  1441
+        {
  1442
+            sixteen.canFind!approxEqual(v);
  1443
+        }
  1444
+    }
  1445
+
  1446
+    doTests!(ulong)(referenceInt, referenceInt5000);
  1447
+    doTests!(double)(referenceFloat, referenceFloat5000);
  1448
+}
  1449
+unittest
  1450
+{
  1451
+    //Test everything works for an 8 bit generator
  1452
+    auto a = LaggedFibonacciEngine!(ushort, 8,   607,   273)();
  1453
+    ushort b = 5;
  1454
+    ulong c = 5;
  1455
+    a.seed(b); //Can be built seeded from a ushort
  1456
+    a.seed(cast(uint)c); //ulong needs a cast
  1457
+    a.seed(unpredictableSeed()); //unperdictable seed is fine
  1458
+}
  1459
+unittest
  1460
+{
  1461
+    auto a = LaggedFibonacci();
  1462
+    assertThrown(a.seed([1, 2, 3])); //Input range too short
  1463
+}
  1464
+unittest
  1465
+{
  1466
+    auto p = new LaggedFibonacci();
  1467
+}
1011 1468
 
1012 1469
 /**
1013 1470
 A "good" seed for initializing random number engines. Initializing
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.