1 /**Relatively low-level primitives on which to build higher-level math/stat
2  * functionality.  Some are used internally, some are just things that may be
3  * useful to users of this library.  This module is starting to take on the
4  * appearance of a small utility library.
5  *
6  * Note:  In several functions in this module that return arrays, the last
7  * parameter is an optional buffer for storing the return value.  If this
8  * parameter is ommitted or the buffer is not large enough, one will be
9  * allocated on the GC heap.
10  *
11  * Author:  David Simcha*/
12  /*
13  * License:
14  * Boost Software License - Version 1.0 - August 17th, 2003
15  *
16  * Permission is hereby granted, free of charge, to any person or organization
17  * obtaining a copy of the software and accompanying documentation covered by
18  * this license (the "Software") to use, reproduce, display, distribute,
19  * execute, and transmit the Software, and to prepare derivative works of the
20  * Software, and to permit third-parties to whom the Software is furnished to
21  * do so, all subject to the following:
22  ** The copyright notices in the Software and this entire statement, including
23  * the above license grant, this restriction and the following disclaimer,
24  * must be included in all copies of the Software, in whole or in part, and
25  * all derivative works of the Software, unless such copies or derivative
26  * works are solely in the form of machine-executable object code generated by
27  * a source language processor.
28  *
29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
32  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
33  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
34  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
35  * DEALINGS IN THE SOFTWARE.
36  */
37 module dstats.base;
38 
39 import std.math, std.mathspecial, std.traits, std.typecons, std.algorithm,
40     std.range, std.exception, std.conv, std.functional, std.typetuple,
41     std.numeric;
42 
43 import dstats.alloc, dstats.sort;
44 
45 // Returns the number of dimensions in an array T.
46 package template nDims(T)
47 {
48     static if(isArray!T)
49     {
50         enum nDims = 1 + nDims!(typeof(T.init[0]));
51     }
52     else
53     {
54         enum nDims = 0;
55     }
56 }
57 
58 version(unittest)
59 {
60     static assert(nDims!(uint[]) == 1);
61     static assert(nDims!(float[][]) == 2);
62 }
63 
64 import std..string : strip;
65 
66 immutable double[] logFactorialTable;
67 
68 private enum size_t staticFacTableLen = 10_000;
69 
70 shared static this() {
71     // Allocating on heap instead of static data segment to avoid
72     // false pointer GC issues.
73     double[] sfTemp = new double[staticFacTableLen];
74     sfTemp[0] = 0;
75     for(uint i = 1; i < staticFacTableLen; i++) {
76         sfTemp[i] = sfTemp[i - 1] + log(i);
77     }
78     logFactorialTable = assumeUnique(sfTemp);
79 }
80 
81 version(unittest) {
82     import std.stdio, std.random, std.file;
83 }
84 
85 /**
86 This is the exception that is thrown on invalid arguments to a dstats function.
87 */
88 class DstatsArgumentException : Exception {
89     this(string msg, string file, int line) {
90         super(msg, file, line);
91     }
92 }
93 
94 T dstatsEnforce(T, string file = __FILE__, int line = __LINE__)
95 (T value, lazy const(char)[] msg = null) {
96     if(!value) {
97         const(char)[] lazyMsg = msg;
98         auto exceptMsg = (lazyMsg !is null) ? lazyMsg.idup : "Invalid argument.";
99         throw new DstatsArgumentException(exceptMsg, file, line);
100     }
101 
102     return value;
103 }
104 
105 package void enforceConfidence
106 (string file = __FILE__, int line = __LINE__)(double conf) {
107     dstatsEnforce!(bool, file, line)(conf >= 0 && conf <= 1,
108         text("Confidence intervals must be between 0 and 1, not ", conf, "."));
109 }
110 
111 /**
112 Tests whether T is an input range whose elements can be implicitly
113 converted to doubles.*/
114 template doubleInput(T) {
115     enum doubleInput = isInputRange!(T) && is(ElementType!(T) : double);
116 }
117 
118 /**Tests whether T is iterable and has elements of a type implicitly
119  * convertible to double.*/
120 template doubleIterable(T) {
121     static if(!isIterable!T) {
122         enum doubleIterable = false;
123     } else {
124         enum doubleIterable = is(ForeachType!(T) : double);
125     }
126 }
127 
128 /**
129 Given a tuple possibly containing forward ranges, returns a tuple where
130 save() has been called on all forward ranges.
131  */
132 Tuple!T saveAll(T...)(T args) {
133     Tuple!T ret;
134 
135     foreach(ti, elem; args) {
136         static if(isForwardRange!(typeof(elem))) {
137             ret.field[ti] = elem.save;
138         } else {
139             ret.field[ti] = elem;
140         }
141     }
142 
143     return ret;
144 }
145 
146 /**Bins data into nbin equal width bins, indexed from
147  * 0 to nbin - 1, with 0 being the smallest bin, etc.
148  * The values returned are the counts for each bin.
149  *
150  * Works with any forward range with elements implicitly convertible to double.
151  */
152 Ret[] binCounts(Ret = uint, T)(T data, uint nbin, Ret[] buf = null)
153 if(isForwardRange!(T) && doubleInput!(T)) {
154     dstatsEnforce(nbin > 0, "Cannot bin data into zero bins.");
155 
156     alias Unqual!(ElementType!(T)) E;
157     E min = data.front, max = data.front;
158     foreach(elem; data) {
159         if(elem > max)
160             max = elem;
161         else if(elem < min)
162             min = elem;
163     }
164     E range = max - min;
165 
166     Ret[] bins;
167     if(buf.length < nbin) {
168         bins = new Ret[nbin];
169     } else {
170         bins = buf[0..nbin];
171         bins[] = 0;
172     }
173 
174     foreach(elem; data) {
175         // Using the truncation as a feature.
176         uint whichBin = cast(uint) ((elem - min) * nbin / range);
177 
178         // Handle edge case by putting largest item in largest bin.
179         if(whichBin == nbin) whichBin--;
180 
181         bins[whichBin]++;
182     }
183 
184     return bins;
185 }
186 
187 unittest {
188     double[] data = [0.0, .01, .03, .05, .11, .31, .51, .71, .89, 1];
189     auto res = binCounts(data, 10);
190     assert(res == [4U, 1, 0, 1, 0, 1, 0, 1, 1, 1]);
191 
192     auto buf = new uint[10];
193     foreach(ref elem; buf) {
194         elem = uniform(0, 34534);
195     }
196 
197     res = binCounts(data, 10, buf);
198     assert(res == [4U, 1, 0, 1, 0, 1, 0, 1, 1, 1]);
199 }
200 
201 /**Bins data into nbin equal width bins, indexed from
202  * 0 to nbin - 1, with 0 being the smallest bin, etc.
203  * The values returned are the bin index for each element.
204  *
205  * Default return type is ubyte, because in the dstats.infotheory,
206  * entropy() and related functions specialize on ubytes, and become
207  * substandially faster.  However, if you're using more than 255 bins,
208  * you'll have to provide a different return type as a template parameter.*/
209 Ret[] bin(Ret = ubyte, T)(T data, uint nbin, Ret[] buf = null)
210 if(isForwardRange!(T) && doubleInput!(T) && isIntegral!(Ret)) {
211     dstatsEnforce(nbin > 0, "Cannot bin data into zero bins.");
212     dstatsEnforce(nbin <= (cast(uint) Ret.max + 1), "Cannot bin into " ~
213         to!string(nbin) ~ " bins and store the results in a " ~
214         Ret.stringof ~ ".");
215     alias ElementType!(T) E;
216     Unqual!(E) min = data.front, max = data.front;
217 
218     auto dminmax = data;
219     dminmax.popFront;
220     foreach(elem; dminmax) {
221         if(elem > max)
222             max = elem;
223         else if(elem < min)
224             min = elem;
225     }
226     E range = max - min;
227     Ret[] bins;
228     if(buf.length < data.length) {
229         bins = uninitializedArray!(Ret[])(data.length);
230     } else {
231         bins = buf[0..data.length];
232     }
233 
234     foreach(i, elem; data) {
235         // Using the truncation as a feature.
236         uint whichBin = cast(uint) ((elem - min) * nbin / range);
237 
238         // Handle edge case by putting largest item in largest bin.
239         if(whichBin == nbin)
240             whichBin--;
241 
242         bins[i] = cast(Ret) whichBin;
243     }
244 
245     return bins;
246 }
247 
248 unittest {
249     auto alloc = newRegionAllocator();
250     double[] data = [0.0, .01, .03, .05, .11, .31, .51, .71, .89, 1];
251     auto res = bin(data, 10);
252     assert(res == to!(ubyte[])([0, 0, 0, 0, 1, 3, 5, 7, 8, 9]));
253 
254     auto buf = new ubyte[20];
255     foreach(ref elem; buf) {
256         elem = cast(ubyte) uniform(0U, 255);
257     }
258 
259     res = bin(data, 10, buf);
260     assert(res == to!(ubyte[])([0, 0, 0, 0, 1, 3, 5, 7, 8, 9]));
261 
262     // Make sure this throws:
263     try {
264         auto foo = bin( seq(0U, 1_000U), 512);
265         assert(0);
266     } catch(Exception e) {
267         // It's supposed to throw.
268     }
269 }
270 
271 /**Bins data into nbin equal frequency bins, indexed from
272  * 0 to nbin - 1, with 0 being the smallest bin, etc.
273  * The values returned are the bin index for each element.
274  *
275  * Default return type is ubyte, because in the dstats.infotheory,
276  * entropy() and related functions specialize on ubytes, and become
277  * substandially faster.  However, if you're using more than 256 bins,
278  * you'll have to provide a different return type as a template parameter.*/
279 Ret[] frqBin(Ret = ubyte, T)(T data, uint nbin, Ret[] buf = null)
280 if(doubleInput!(T) && isForwardRange!(T) && hasLength!(T) && isIntegral!(Ret)) {
281     dstatsEnforce(nbin > 0, "Cannot bin data into zero bins.");
282     dstatsEnforce(nbin <= data.length,
283         "Cannot equal frequency bin data into more than data.length bins.");
284     dstatsEnforce(nbin <= (cast(ulong) Ret.max) + 1UL, "Cannot bin into " ~
285         to!string(nbin) ~ " bins and store the results in a " ~
286         Ret.stringof ~ ".");
287 
288     Ret[] result;
289     if(buf.length < data.length) {
290         result = uninitializedArray!(Ret[])(data.length);
291     } else {
292         result = buf[0..data.length];
293     }
294 
295     auto alloc = newRegionAllocator();
296     auto perm = alloc.uninitializedArray!(size_t[])(data.length);
297 
298     foreach(i, ref e; perm) {
299         e = i;
300     }
301 
302     static if(isRandomAccessRange!(T)) {
303         bool compare(size_t lhs, size_t rhs) {
304             return data[lhs] < data[rhs];
305         }
306 
307         qsort!compare(perm);
308     } else {
309         auto dd = alloc.array(data);
310         qsort(dd, perm);
311         alloc.freeLast();
312     }
313 
314     auto rem = data.length % nbin;
315     Ret bin = 0;
316     auto i = 0, frq = data.length / nbin;
317     while(i < data.length) {
318         foreach(j; 0..(bin < rem) ? frq + 1 : frq) {
319             result[perm[i++]] = bin;
320         }
321         bin++;
322     }
323     return result;
324 }
325 
326 unittest {
327     double[] data = [5U, 1, 3, 8, 30, 10, 7];
328     auto res = frqBin(data, 3);
329     assert(res == to!(ubyte[])([0, 0, 0, 1, 2, 2, 1]));
330     data = [3, 1, 4, 1, 5, 9, 2, 6, 5];
331 
332     auto buf = new ubyte[32];
333     foreach(i, ref elem; buf) {
334         elem = cast(ubyte) i;
335     }
336 
337     res = frqBin(data, 4, buf);
338     assert(res == to!(ubyte[])([1, 0, 1, 0, 2, 3, 0, 3, 2]));
339     data = [3U, 1, 4, 1, 5, 9, 2, 6, 5, 3, 4, 8, 9, 7, 9, 2];
340     res = frqBin(data, 4);
341     assert(res == to!(ubyte[])([1, 0, 1, 0, 2, 3, 0, 2, 2, 1, 1, 3, 3, 2, 3, 0]));
342 
343     // Make sure this throws:
344     try {
345         auto foo = frqBin( seq(0U, 1_000U), 512);
346         assert(0);
347     } catch(Exception e) {
348         // It's supposed to throw.
349     }
350 }
351 
352 /**Generates a sequence from [start..end] by increment.  Includes start,
353  * excludes end.  Does so eagerly as an array.
354  *
355  * Examples:
356  * ---
357  * auto s = seq(0, 5);
358  * assert(s == [0, 1, 2, 3, 4]);
359  * ---
360  */
361 CommonType!(T, U)[] seq(T, U, V = uint)(T start, U end, V increment = 1U) {
362     dstatsEnforce(increment > 0, "Cannot have a seq increment <= 0.");
363     dstatsEnforce(end >= start, "End must be >= start in seq.");
364 
365     alias CommonType!(T, U) R;
366     auto output = uninitializedArray!(R[])
367         (cast(size_t) ((end - start) / increment));
368 
369     size_t count = 0;
370     for(T i = start; i < end; i += increment) {
371         output[count++] = i;
372     }
373     return output;
374 }
375 
376 unittest {
377     auto s = seq(0, 5);
378     assert(s == [0, 1, 2, 3, 4]);
379 }
380 
381 /**Given an input array, outputs an array containing the rank from
382  * [1, input.length] corresponding to each element.  Ties are dealt with by
383  * averaging.  This function does not reorder the input range.
384  * Return type is float[] by default, but if you are sure you have no ties,
385  * ints can be used for efficiency (in which case ties will not be averaged),
386  * and if you need more precision when averaging ties, you can use double or
387  * real.
388  *
389  * Works with any input range.
390  *
391  * Examples:
392  * ---
393  * uint[] test = [3, 5, 3, 1, 2];
394  * assert(rank!("a < b", float)(test) == [3.5f, 5f, 3.5f, 1f, 2f]);
395  * assert(test == [3U, 5, 3, 1, 2]);
396  * ---*/
397 Ret[] rank(alias compFun = "a < b", Ret = double, T)(T input, Ret[] buf = null)
398 if(isInputRange!(T) && is(typeof(input.front < input.front))) {
399     auto alloc = newRegionAllocator();
400 
401     static if(!isRandomAccessRange!(T) || !hasLength!(T)) {
402         return rankSort!(compFun, Ret)( alloc.array(input), buf);
403     } else {
404 
405         /* It's faster to duplicate the input and then use rankSort on the
406          * duplicate, but more space efficient to create an index array.  Check
407          * TempAlloc to see whether we can fit everything we need on the current
408          * frame.  If yes, use the faster algorithm since we're effectively
409          * not saving any space by using the space-efficient algorithm.
410          * If no, use the more space-efficient algorithm.
411          */
412         auto bytesNeeded = input.length * (ElementType!(T).sizeof + size_t.sizeof);
413         if(bytesNeeded < alloc.segmentSlack) {
414             return rankSort!(compFun, Ret)( alloc.array(input), buf);
415         } else {
416             return rankUsingIndex!(compFun, Ret)(input, buf);
417         }
418     }
419 
420     assert(0);
421 }
422 
423 /* Space-efficient but slow way of computing ranks.  The extra indirection
424  * caused by creating the index array wreaks havock with CPU caches, especially
425  * for large arrays that don't fit entirely in cache.
426  */
427 private Ret[] rankUsingIndex(alias compFun, Ret, T)(T input, Ret[] buf) {
428     auto alloc = newRegionAllocator();
429     size_t[] indices = alloc.uninitializedArray!(size_t[])(input.length);
430     foreach(i, ref elem; indices) {
431         elem = i;
432     }
433 
434     bool compare(size_t lhs, size_t rhs) {
435         alias binaryFun!(compFun) innerComp;
436         return innerComp(input[lhs], input[rhs]);
437     }
438 
439     dstats.sort.qsort!compare(indices);
440 
441     Ret[] ret;
442     if(buf.length < indices.length) {
443         ret = uninitializedArray!(Ret[])(indices.length);
444     } else {
445         ret = buf[0..indices.length];
446     }
447 
448     foreach(i, index; indices) {
449         ret[index] = i + 1;
450     }
451 
452     auto myIndexed = Indexed!(T)(input, indices);
453 
454     static if(!isIntegral!Ret) {
455         averageTies(myIndexed, ret, indices);
456     }
457 
458     return ret;
459 }
460 
461 private struct Indexed(T) {
462     T someRange;
463     size_t[] indices;
464 
465     ElementType!T opIndex(size_t index) {
466         return someRange[indices[index]];
467     }
468 
469     @property size_t length() {
470         return indices.length;
471     }
472 }
473 
474 /**Same as rank(), but also sorts the input range.
475  * The array returned will still be identical to that returned by rank(), i.e.
476  * the rank of each element will correspond to the ranks of the elements in the
477  * input array before sorting.
478  *
479  * Examples:
480  * ---
481  * uint[] test = [3, 5, 3, 1, 2];
482  * assert(rankSort(test) == [3.5, 5, 3.5, 1.0, 2.0]);
483  * assert(test == [1U, 2, 3, 4, 5]);
484  * ---
485  */
486 Ret[] rankSort(alias compFun = "a < b", Ret = double, T)(T input, Ret[] buf = null)
487 if(isRandomAccessRange!(T) && hasLength!(T) && is(typeof(input.front < input.front))) {
488     auto alloc = newRegionAllocator();
489     Ret[] ranks;
490     if(buf.length < input.length) {
491         ranks = uninitializedArray!(Ret[])(input.length);
492     } else {
493         ranks = buf[0..input.length];
494     }
495 
496     size_t[] perms = alloc.uninitializedArray!(size_t[])(input.length);
497     foreach(i, ref p; perms) {
498         p = i;
499     }
500 
501     dstats.sort.qsort!compFun(input, perms);
502     foreach(i; 0..perms.length)  {
503         ranks[perms[i]] = i + 1;
504     }
505 
506     static if(!isIntegral!Ret) {
507         averageTies(input, ranks, perms);
508     }
509 
510     return ranks;
511 }
512 
513 unittest {
514     uint[] test = [3, 5, 3, 1, 2];
515 
516     float[] dummy;
517     assert(rankUsingIndex!("a < b", float)(test, dummy) == [3.5f, 5f, 3.5f, 1f, 2f]);
518     assert(test == [3U, 5, 3, 1, 2]);
519 
520     double[] dummy2;
521     assert(rankUsingIndex!("a < b", double)(test, dummy2) == [3.5, 5, 3.5, 1, 2]);
522     assert(rankSort(test) == [3.5, 5.0, 3.5, 1.0, 2.0]);
523     assert(test == [1U,2,3,3,5]);
524 
525     uint[] test2 = [3,3,1,2];
526     assert(rank(test2) == [3.5,3.5,1,2]);
527 }
528 
529 private void averageTies(T, U)(T sortedInput, U[] ranks, size_t[] perms)
530 in {
531     assert(sortedInput.length == ranks.length);
532     assert(ranks.length == perms.length);
533 } body {
534     size_t tieCount = 1;
535     foreach(i; 1..ranks.length) {
536         if(sortedInput[i] == sortedInput[i - 1]) {
537             tieCount++;
538         } else if(tieCount > 1) {
539             double avg = 0;
540             immutable increment = 1.0 / tieCount;
541 
542             foreach(perm; perms[i - tieCount..i]) {
543                 avg += ranks[perm] * increment;
544             }
545 
546             foreach(perm; perms[i - tieCount..i]) {
547                 ranks[perm] = avg;
548             }
549             tieCount = 1;
550         }
551     }
552 
553     if(tieCount > 1) { // Handle the end.
554         double avg = 0;
555         immutable increment = 1.0 / tieCount;
556 
557         foreach(perm; perms[perms.length - tieCount..$]) {
558             avg += ranks[perm] * increment;
559         }
560 
561         foreach(perm; perms[perms.length - tieCount..$]) {
562             ranks[perm] = avg;
563         }
564     }
565 }
566 
567 /**Returns an associative array of counts of every element in input.
568  * Works w/ any iterable.
569  *
570  * Examples:
571  * ---
572  * int[] foo = [1,2,3,1,2,4];
573  * uint[int] frq = frequency(foo);
574  * assert(frq.length == 4);
575  * assert(frq[1] == 2);
576  * assert(frq[4] == 1);
577  * ---*/
578 uint[ForeachType!(T)] frequency(T)(T input)
579 if(isIterable!(T)) {
580     typeof(return) output;
581     foreach(i; input) {
582         output[i]++;
583     }
584     return output;
585 }
586 
587 unittest {
588     int[] foo = [1,2,3,1,2,4];
589     uint[int] frq = frequency(foo);
590     assert(frq.length == 4);
591     assert(frq[1] == 2);
592     assert(frq[4] == 1);
593 }
594 
595 /**Given a range of values and a range of categories, separates values
596  * by category.  This function also guarantees that the order within each
597  * category will be maintained.
598  *
599  * Note:  While the general convention of this library is to try to avoid
600  * heap allocations whenever possible so that multithreaded code scales well and
601  * false pointers aren't an issue, this function allocates like crazy
602  * because there's basically no other way to implement it.  Don't use it in
603  * performance-critical multithreaded code.
604  *
605  * Examples:
606  * ---
607  * uint[] values = [1,2,3,4,5,6,7,8];
608  * bool[] categories = [false, false, false, false, true, true, true, true];
609  * auto separated = byCategory(values, categories);
610  * auto tResult = studentsTTest(separated.values);
611  * ---
612  */
613 ElementType!(V)[][ElementType!(C)] byCategory(V, C)(V values, C categories)
614 if(isInputRange!(V) && isInputRange!(C) && !is(ElementType!C == bool)) {
615     alias ElementType!(V) EV;
616     alias ElementType!(C) EC;
617 
618     Appender!(EV[])[EC] aa;
619     while(!values.empty && !categories.empty) {
620         scope(exit) {
621             values.popFront();
622             categories.popFront();
623         }
624 
625         auto category = categories.front;
626         auto ptr = category in aa;
627         if(ptr is null) {
628             aa[category] = typeof(aa[category]).init;
629             ptr = category in aa;
630         }
631 
632         ptr.put(values.front);
633     }
634 
635     EV[][EC] ret;
636     foreach(k, v; aa) {
637         ret[k] = v.data;
638     }
639 
640     return ret;
641 }
642 
643 /**Special case implementation for when ElementType!C is boolean.*/
644 ElementType!(V)[][2] byCategory(V, C)(V values, C categories)
645 if(isInputRange!(V) && isInputRange!(C) && is(ElementType!C == bool)) {
646     typeof(return) ret;
647 
648     static if(hasLength!V || hasLength!C) {
649         // Preallocate.
650         size_t zeroIndex, oneIndex;
651 
652         static if(!hasLength!V) {
653             ret[0].length = categories.length;
654             ret[1].length = values.length;
655         } else static if(!hasLength!C) {
656             ret[0].length = values.length;
657             ret[1].length = values.length;
658         } else {
659             immutable len = min(categories.length, values.length);
660             ret[0].length = len;
661             ret[1].length = len;
662         }
663 
664         while(!values.empty && !categories.empty) {
665             scope(exit) {
666                 values.popFront();
667                 categories.popFront();
668             }
669 
670             auto category = categories.front;
671             if(category) {
672                 ret[1][oneIndex++] = values.front;
673             } else {
674                 ret[0][zeroIndex++] = values.front;
675             }
676         }
677 
678         ret[0] = ret[0][0..zeroIndex];
679         ret[1] = ret[1][0..oneIndex];
680     } else {
681         auto app0 = appender!(ElementType!(V)[])();
682         auto app1 = appender!(ElementType!(V)[])();
683 
684         while(!values.empty && !categories.empty) {
685             scope(exit) {
686                 values.popFront();
687                 categories.popFront();
688             }
689 
690             auto category = categories.front;
691             if(category) {
692                 app1.put(values.front);
693             } else {
694                 app0.put(values.front);
695             }
696         }
697 
698         ret[0] = app0.data;
699         ret[1] = app1.data;
700     }
701 
702     return ret;
703 }
704 
705 unittest {
706     int[] nums = [1,2,3,4,5,6,7,8,9];
707     int[] categories = [0,1,2,0,1,2,0,1,2];
708 
709     // The filter is just to prevent having a length.
710     auto categories2 = filter!"a == a"(map!"a % 2 == 0"(nums));
711 
712     auto result = byCategory(nums, categories);
713     assert(result[0] == [1,4,7]);
714     assert(result[1] == [2,5,8]);
715     assert(result[2] == [3,6,9]);
716 
717     auto res2 = byCategory(filter!"a == a"(nums), categories2);
718     assert(res2[0] == [1,3,5,7,9]);
719     assert(res2[1] == [2,4,6,8]);
720 
721     auto res3 = byCategory(nums,
722         [false, true, false, true, false, true, false, true, false]);
723     assert(res2 == res3);
724 }
725 
726 /**Finds the area under the ROC curve (a curve with sensitivity on the Y-axis
727  * and 1 - specificity on the X-axis).  This is a useful metric for
728  * determining how well a test statistic discriminates between two classes.
729  * The following assumptions are made in this implementation:
730  *
731  * 1.  For some cutoff value c and test statistic T, your decision rule is of
732  *     the form "Class A if T > c, Class B if T < c".
733  *
734  * 2.  In the case of ties, i.e. if class A and class B both have an identical
735  *     value, linear interpolation is used.  This is because changing the
736  *     value of c infinitesimally will change both sensitivity and specificity
737  *     in these cases.
738  */
739 double auroc(R1, R2)(R1 classATs, R2 classBTs)
740 if(isNumeric!(ElementType!R1) && isNumeric!(ElementType!R2)) {
741     auto alloc = newRegionAllocator();
742     auto classA = alloc.array(classATs);
743     auto classB = alloc.array(classBTs);
744     qsort(classA);
745     qsort(classB);
746 
747     // Start cutoff at -infinity, such that we get everything in class A, i.e.
748     // perfect specificity, zero sensitivity.  We arbitrarily define class B
749     // as our "positive" class.
750     double tp = 0, tn = classA.length, fp = 0, fn = classB.length;
751     double[2] lastPoint = 0;
752 
753     Unqual!(CommonType!(ElementType!R1, ElementType!R2)) currentVal;
754 
755     ElementType!R1 popA() {
756         tn--;
757         fp++;
758         auto ret = classA.front;
759         classA.popFront();
760         return ret;
761     }
762 
763     ElementType!R2 popB() {
764         fn--;
765         tp++;
766         auto ret = classB.front;
767         classB.popFront();
768         return ret;
769     }
770 
771     double area = 0;
772     while(!classA.empty && !classB.empty) {
773         if(classA.front < classB.front) {
774             currentVal = popA();
775         } else {
776             currentVal = popB();
777         }
778 
779         // Handle ties.
780         while(!classA.empty && classA.front == currentVal) {
781             popA();
782         }
783 
784         while(!classB.empty && classB.front == currentVal) {
785             popB();
786         }
787 
788         double[2] curPoint;
789         curPoint[0] = 1.0 - tn / (fp + tn);
790         curPoint[1] = tp / (tp + fn);
791 
792         immutable xDist = curPoint[0] - lastPoint[0];
793         area += xDist * lastPoint[1];  // Rectangular part.
794         area += xDist * 0.5 * (curPoint[1] - lastPoint[1]);  // Triangular part.
795         lastPoint[] = curPoint[];
796     }
797 
798     if(classA.length > 0 && classB.length == 0) {
799         // Then we already have a sensitivity of 1, move straight to the right
800         // to the point (1, 1).
801 
802         immutable xDist = 1 - lastPoint[0];
803         area += xDist * lastPoint[1];  // Rectangular part.
804         area += xDist * 0.5 * (1 - lastPoint[1]);  // Triangular part.
805     }
806 
807     return area;
808 }
809 
810 unittest {
811     // Values worked out by hand on paper.  If you don't believe me, work
812     // them out yourself.
813     assert(auroc([4,5,6], [1,2,3]) == 1);
814     assert(approxEqual(auroc([8,6,7,5,3,0,9], [3,6,2,4,3,6]), 0.6904762));
815     assert(approxEqual(auroc([2,7,1,8,2,8,1,8], [3,1,4,1,5,9,2,6]), 0.546875));
816 }
817 
818 ///
819 T sign(T)(T num) pure nothrow if(is(typeof(num < 0))) {
820     if (num > 0) return 1;
821     if (num < 0) return -1;
822     return 0;
823 }
824 
825 unittest {
826     assert(sign(3.14159265)==1);
827     assert(sign(-3)==-1);
828     assert(sign(-2.7182818)==-1);
829 }
830 
831 ///
832 /*Values up to 9,999 are pre-calculated and stored in
833  * an immutable global array, for performance.  After this point, the gamma
834  * function is used, because caching would take up too much memory, and if
835  * done lazily, would cause threading issues.*/
836 double logFactorial(ulong n) {
837     //Input is uint, can't be less than 0, no need to check.
838     if(n < staticFacTableLen) {
839         return logFactorialTable[cast(size_t) n];
840     } else return logGamma(n + 1);
841 }
842 
843 unittest {
844     // Cache branch.
845     assert(cast(uint) round(exp(logFactorial(4)))==24);
846     assert(cast(uint) round(exp(logFactorial(5)))==120);
847     assert(cast(uint) round(exp(logFactorial(6)))==720);
848     assert(cast(uint) round(exp(logFactorial(7)))==5040);
849     assert(cast(uint) round(exp(logFactorial(3)))==6);
850     // Gamma branch.
851     assert(approxEqual(logFactorial(12000), 1.007175584216837e5, 1e-14));
852     assert(approxEqual(logFactorial(14000), 1.196610688711534e5, 1e-14));
853 }
854 
855 ///Log of (n choose k).
856 double logNcomb(ulong n, ulong k)
857 in {
858     assert(k <= n);
859 } body {
860     if(n < k) return -double.infinity;
861     //Extra parentheses increase numerical accuracy.
862     return logFactorial(n) - (logFactorial(k) + logFactorial(n - k));
863 }
864 
865 unittest {
866     assert(cast(uint) round(exp(logNcomb(4,2)))==6);
867     assert(cast(uint) round(exp(logNcomb(30,8)))==5852925);
868     assert(cast(uint) round(exp(logNcomb(28,5)))==98280);
869 }
870 
871 static if(size_t.sizeof == 4) {
872     private enum MAX_PERM_LEN = 12;
873 } else {
874     private enum MAX_PERM_LEN = 20;
875 }
876 
877 /**
878 A struct that generates all possible permutations of a sequence.
879 
880 Notes:
881 
882 Permutations are output in undefined order.
883 
884 The array returned by front is recycled across iterations.  To preserve
885 it across iterations, wrap this range using map!"a.dup" or
886 map!"a.idup".
887 
888 Bugs:  Only supports iterating over up to size_t.max permutations.
889 This means the max permutation length is 12 on 32-bit machines, or 20
890 on 64-bit.  This was a conscious tradeoff to allow this range to have a
891 length of type size_t, since iterating over such huge permutation spaces
892 would be insanely slow anyhow.
893 
894 Examples:
895 ---
896 double[][] res;
897 auto perm = map!"a.dup"(Perm!(double)([1.0, 2.0, 3.0][]));
898 foreach(p; perm) {
899      res ~= p;
900 }
901 
902 auto sorted = sort(res);
903 assert(sorted.canFind([1.0, 2.0, 3.0]));
904 assert(sorted.canFind([1.0, 3.0, 2.0]));
905 assert(sorted.canFind([2.0, 1.0, 3.0]));
906 assert(sorted.canFind([2.0, 3.0, 1.0]));
907 assert(sorted.canFind([3.0, 1.0, 2.0]));
908 assert(sorted.canFind([3.0, 2.0, 1.0]));
909 assert(sorted.length == 6);
910 ---
911  */
912 struct Perm(T) {
913 private:
914 
915     // Optimization:  Since we know this thing can't get too big (there's
916     // an dstatsEnforce statement for it in the c'tor), just use arrays of the max
917     // possible size for stuff and store them inline, if it's all just bytes.
918     static if(T.sizeof == 1) {
919         T[MAX_PERM_LEN] perm;
920     } else {
921         T* perm;
922     }
923 
924     // The length of this range.
925     size_t nPerms;
926 
927     ubyte[MAX_PERM_LEN] Is;
928     ubyte currentIndex;
929 
930     // The length of these arrays.  Stored once to minimize overhead.
931     ubyte len;
932     alias const(T)[] PermArray;
933 
934 public:
935     /**Generate permutations from an input range.
936      * Create a duplicate of this sequence
937      * so that the original sequence is not modified.*/
938     this(U)(U input)
939     if(isForwardRange!(U)) {
940 
941         static if(ElementType!(U).sizeof > 1) {
942             auto arr = array(input);
943             dstatsEnforce(arr.length <= MAX_PERM_LEN, text(
944                 "Can't iterate permutations of an array this long.  (Max length:  ",
945                         MAX_PERM_LEN, ")"));
946             len = cast(ubyte) arr.length;
947             perm = arr.ptr;
948         } else {
949             foreach(elem; input) {
950                 dstatsEnforce(len < MAX_PERM_LEN, text(
951                     "Can't iterate permutations of an array this long.  (Max length:  ",
952                         MAX_PERM_LEN, ")"));
953 
954                 perm[len++] = elem;
955             }
956         }
957 
958         popFront();
959 
960         nPerms = 1;
961         for(size_t i = 2; i <= len; i++) {
962             nPerms *= i;
963         }
964     }
965 
966     /**
967     Returns the current permutation.  The array is const because it is
968     recycled across iterations and modifying it would destroy the state of
969     the permutation generator.
970     */
971     @property const(T)[] front() {
972         return perm[0..len];
973     }
974 
975     /**
976     Get the next permutation in the sequence.  This will overwrite the
977     contents of the array returned by the last call to front.
978     */
979     void popFront() {
980         if(len == 0) {
981             nPerms--;
982             return;
983         }
984         if(currentIndex == len - 1) {
985             currentIndex--;
986             nPerms--;
987             return;
988         }
989 
990         uint max = len - currentIndex;
991         if(Is[currentIndex] == max) {
992 
993             if(currentIndex == 0) {
994                 nPerms--;
995                 assert(nPerms == 0, to!string(nPerms));
996                 return;
997             }
998 
999             Is[currentIndex..len] = 0;
1000             currentIndex--;
1001             return popFront();
1002         } else {
1003             rotateLeft(perm[currentIndex..len]);
1004             Is[currentIndex]++;
1005             currentIndex++;
1006             return popFront();
1007         }
1008     }
1009 
1010     ///
1011     @property bool empty() {
1012         return nPerms == 0;
1013     }
1014 
1015     /**
1016     The number of permutations left.
1017      */
1018     @property size_t length() const pure nothrow {
1019         return nPerms;
1020     }
1021 
1022     ///
1023     @property typeof(this) save() {
1024         auto ret = this;
1025         static if(T.sizeof > 1) {
1026             ret.perm = (ret.perm[0..len].dup).ptr;
1027         }
1028         return ret;
1029     }
1030 }
1031 
1032 /**
1033 Create a Perm struct from a range or of a set of bounds.
1034 
1035 Examples:
1036 ---
1037 auto p = perm([1,2,3]);  // All permutations of [1,2,3].
1038 auto p = perm(5);  // All permutations of [0,1,2,3,4].
1039 auto p = perm(-1, 2); // All permutations of [-1, 0, 1].
1040 ---
1041  */
1042 auto perm(T...)(T stuff) {
1043     static if(isForwardRange!(T[0])) {
1044         return Perm!(ElementType!T)(stuff);
1045     } else static if(T.length == 1) {
1046         static assert(isIntegral!(T[0]),
1047             "If one argument is passed to perm(), it must be an integer.");
1048 
1049         dstatsEnforce(stuff[0] >= 0, "Cannot generate permutations of length < 0.");
1050         dstatsEnforce(stuff[0] <= MAX_PERM_LEN, text(
1051             "Can't iterate permutations of an array of length ",
1052             stuff[0], ".  (Max length:  ", MAX_PERM_LEN, ")"));
1053 
1054         // Optimization:  Since we know the lower
1055         // bound is zero and the upper bound can't be > byte.max, use bytes
1056         // instead of bigger integer types.
1057         return Perm!byte(seq(cast(byte) 0, cast(byte) stuff[0]));
1058     } else {
1059         static assert(stuff.length == 2);
1060         return Perm!(CommonType!(T[0], T[1]))(seq(stuff[0], stuff[1]));
1061     }
1062 }
1063 
1064 unittest {
1065     // Test degenerate case of len == 0;
1066     uint nZero = 0;
1067     foreach(elem; perm(0)) {
1068         assert(elem.length == 0);
1069         nZero++;
1070     }
1071     assert(nZero == 1);
1072 
1073     double[][] res;
1074     auto p1 = map!"a.dup"(perm([1.0, 2.0, 3.0][]));
1075     assert(p1.length == 6);
1076     foreach(p; p1) {
1077         res ~= p;
1078     }
1079     auto sortedRes = sort(res);
1080     assert(sortedRes.contains([1.0, 2.0, 3.0]));
1081     assert(sortedRes.contains([1.0, 3.0, 2.0]));
1082     assert(sortedRes.contains([2.0, 1.0, 3.0]));
1083     assert(sortedRes.contains([2.0, 3.0, 1.0]));
1084     assert(sortedRes.contains([3.0, 1.0, 2.0]));
1085     assert(sortedRes.contains([3.0, 2.0, 1.0]));
1086     assert(res.length == 6);
1087     byte[][] res2;
1088     auto perm2 = map!"a.dup"(perm(3));
1089     foreach(p; perm2) {
1090         res2 ~= p;
1091     }
1092     auto sortedRes2 = sort(res2);
1093     assert(sortedRes2.contains( to!(byte[])([0, 1, 2])));
1094     assert(sortedRes2.contains( to!(byte[])([0, 2, 1])));
1095     assert(sortedRes2.contains( to!(byte[])([1, 0, 2])));
1096     assert(sortedRes2.contains( to!(byte[])([1, 2, 0])));
1097     assert(sortedRes2.contains( to!(byte[])([2, 0, 1])));
1098     assert(sortedRes2.contains( to!(byte[])([2, 1, 0])));
1099     assert(res2.length == 6);
1100 
1101     // Indirect tests:  If the elements returned are unique, there are N! of
1102     // them, and they contain what they're supposed to contain, the result is
1103     // correct.
1104     auto perm3 = perm(0U, 6U);
1105     bool[uint[]] table;
1106     foreach(p; perm3) {
1107         table[p.idup] = true;
1108     }
1109     assert(table.length == 720);
1110     foreach(elem, val; table) {
1111         assert(elem.dup.insertionSort == [0U, 1, 2, 3, 4, 5]);
1112     }
1113     auto perm4 = perm(5);
1114     bool[byte[]] table2;
1115     foreach(p; perm4) {
1116         table2[p.idup] = true;
1117     }
1118     assert(table2.length == 120);
1119     foreach(elem, val; table2) {
1120         assert(elem.dup.insertionSort == to!(byte[])([0, 1, 2, 3, 4]));
1121     }
1122 }
1123 
1124 /**
1125 Generates every possible combination of r elements of the given sequence, or
1126 array indices from zero to N, depending on which c'tor is called.  Uses
1127 an input range interface.
1128 
1129 Note:  The buffer that is returned by front is recycled across iterations.
1130 To duplicate it instead, use map!"a.dup" or map!"a.idup".
1131 
1132 Bugs:  Only supports iterating over up to size_t.max combinations.
1133 This was a conscious tradeoff to allow this range to have a
1134 length of type size_t, since iterating over such huge combination spaces
1135 would be insanely slow anyhow.
1136 
1137 Examples:
1138 ---
1139 auto comb1 = map!"a.dup"(Comb!(uint)(5, 2));
1140 uint[][] vals;
1141 foreach(c; comb1) {
1142     vals ~= c;
1143 }
1144 auto sorted = sort(vals);
1145 assert(sorted.canFind([0u,1]));
1146 assert(sorted.canFind([0u,2]));
1147 assert(sorted.canFind([0u,3]));
1148 assert(sorted.canFind([0u,4]));
1149 assert(sorted.canFind([1u,2]));
1150 assert(sorted.canFind([1u,3]));
1151 assert(sorted.canFind([1u,4]));
1152 assert(sorted.canFind([2u,3]));
1153 assert(sorted.canFind([2u,4]));
1154 assert(sorted.canFind([3u,4]));
1155 assert(sorted.length == 10);
1156 ---
1157  */
1158 struct Comb(T) {
1159 private:
1160     int N;
1161     int R;
1162     int diff;
1163     uint* pos;
1164     T* myArray;
1165     T* chosen;
1166     size_t _length;
1167 
1168     alias const(T)[] CombArray;
1169 
1170     void popFrontNum() {
1171         int index = R - 1;
1172         for(; index != -1 && pos[index] == diff + index; --index) {}
1173         if(index == -1) {
1174             _length--;
1175             return;
1176         }
1177         pos[index]++;
1178         for(uint i = index + 1; i < R; ++i) {
1179             pos[i] = pos[index] + i - index;
1180         }
1181         _length--;
1182     }
1183 
1184     void popFrontArray() {
1185         int index = R - 1;
1186         for(; index != -1 && pos[index] == diff + index; --index) {}
1187         if(index == -1) {
1188             _length--;
1189             return;
1190         }
1191         pos[index]++;
1192         chosen[index] = myArray[pos[index]];
1193         for(uint i = index + 1; i < R; ++i) {
1194             pos[i] = pos[index] + i - index;
1195             chosen[i] = myArray[pos[i]];
1196         }
1197         _length--;
1198     }
1199 
1200     void setLen() {
1201         // Used at construction.
1202         auto rLen = exp( logNcomb(N, R));
1203         dstatsEnforce(rLen < size_t.max, "Too many combinations.");
1204         _length = roundTo!size_t(rLen);
1205     }
1206 
1207 public:
1208 
1209     /**
1210     Ctor to generate all possible combinations of array indices for a length r
1211     array.  This is a special-case optimization and is faster than simply
1212     using the other ctor to generate all length r combinations from
1213     seq(0, length).
1214 
1215     For efficiency, uint is used instead of size_t since, on a 64-bit system,
1216     generating all possible combinations of an array bigger than uint.max
1217     wouldn't be feasible anyhow.
1218     */
1219     static if(is(T == uint)) {
1220         this(uint n, uint r)
1221         in {
1222             assert(n >= r);
1223         } body {
1224             if(r > 0) {
1225                 pos = (seq(0U, r)).ptr;
1226                 pos[r - 1]--;
1227             }
1228             N = n;
1229             R = r;
1230             diff = N - R;
1231             popFront();
1232             setLen();
1233         }
1234     }
1235 
1236     /**General ctor.  array is a sequence from which to generate the
1237      * combinations.  r is the length of the combinations to be generated.*/
1238     this(T[] array, uint r) {
1239         if(r > 0) {
1240             pos = (seq(0U, r)).ptr;
1241             pos[r - 1]--;
1242         }
1243         N = to!uint(array.length);
1244         R = r;
1245         diff = N - R;
1246         auto temp = array.dup;
1247         myArray = temp.ptr;
1248         chosen = (new T[r]).ptr;
1249         foreach(i; 0..r) {
1250             chosen[i] = myArray[pos[i]];
1251         }
1252         popFront();
1253         setLen();
1254     }
1255 
1256     /**
1257     Gets the current combination.
1258     */
1259     @property const(T)[] front() {
1260         static if(!is(T == uint)) {
1261             return chosen[0..R].dup;
1262         } else {
1263             return (myArray is null) ? pos[0..R] : chosen[0..R];
1264         }
1265     }
1266 
1267     /**
1268     Advances to the next combination.  The array returned by front will be
1269     overwritten with the new results.
1270     */
1271     void popFront() {
1272         return (myArray is null) ? popFrontNum() : popFrontArray();
1273     }
1274 
1275     ///
1276     @property bool empty() const pure nothrow {
1277         return length == 0;
1278     }
1279 
1280     ///
1281     @property size_t length() const pure nothrow {
1282         return _length;
1283     }
1284 
1285     ///
1286     @property typeof(this) save() {
1287         auto ret = this;
1288         ret.pos = (pos[0..R].dup).ptr;
1289 
1290         if(chosen !is null) {
1291             ret.chosen = (chosen[0..R].dup).ptr;
1292         }
1293 
1294         return ret;
1295     }
1296 }
1297 
1298 /**Create a Comb struct from a range or of a set of bounds.
1299  *
1300  * Examples:
1301  * ---
1302  * auto c1 = comb([1,2,3], 2);  // Any two elements from [1,2,3].
1303  * auto c2 = comb(5, 3);  // Any three elements from [0,1,2,3,4].
1304  * ---
1305  */
1306 auto comb(T)(T stuff, uint r) {
1307     static if(isForwardRange!(T)) {
1308         return Comb!(ElementType!T)(stuff, r);
1309     } else {
1310         static assert(isIntegral!T, "Can only call comb on ints and ranges.");
1311         return Comb!(uint)(cast(uint) stuff, r);
1312     }
1313 }
1314 
1315 unittest {
1316     // Test degenerate case of r == 0.  Shouldn't segfault.
1317     uint nZero = 0;
1318     foreach(elem; comb(5, 0)) {
1319         assert(elem.length == 0);
1320         nZero++;
1321     }
1322     assert(nZero == 1);
1323 
1324     nZero = 0;
1325     uint[] foo = [1,2,3,4,5];
1326     foreach(elem; comb(foo, 0)) {
1327         assert(elem.length == 0);
1328         nZero++;
1329     }
1330     assert(nZero == 1);
1331 
1332     // Test indexing verison first.
1333     auto comb1 = map!"a.dup"(comb(5, 2));
1334     uint[][] vals;
1335     foreach(c; comb1) {
1336         vals ~= c;
1337     }
1338 
1339     auto sortedVals = sort(vals);
1340     assert(sortedVals.contains([0u,1].dup));
1341     assert(sortedVals.contains([0u,2].dup));
1342     assert(sortedVals.contains([0u,3].dup));
1343     assert(sortedVals.contains([0u,4].dup));
1344     assert(sortedVals.contains([1u,2].dup));
1345     assert(sortedVals.contains([1u,3].dup));
1346     assert(sortedVals.contains([1u,4].dup));
1347     assert(sortedVals.contains([2u,3].dup));
1348     assert(sortedVals.contains([2u,4].dup));
1349     assert(sortedVals.contains([3u,4].dup));
1350     assert(vals.length == 10);
1351 
1352     // Now, test the array version.
1353     auto comb2 = map!"a.dup"(comb(seq(5U, 10U), 3));
1354     vals = null;
1355     foreach(c; comb2) {
1356         vals ~= c;
1357     }
1358     sortedVals = sort(vals);
1359     assert(sortedVals.contains([5u, 6, 7].dup));
1360     assert(sortedVals.contains([5u, 6, 8].dup));
1361     assert(sortedVals.contains([5u, 6, 9].dup));
1362     assert(sortedVals.contains([5u, 7, 8].dup));
1363     assert(sortedVals.contains([5u, 7, 9].dup));
1364     assert(sortedVals.contains([5u, 8, 9].dup));
1365     assert(sortedVals.contains([6U, 7, 8].dup));
1366     assert(sortedVals.contains([6u, 7, 9].dup));
1367     assert(sortedVals.contains([6u, 8, 9].dup));
1368     assert(sortedVals.contains([7u, 8, 9].dup));
1369     assert(vals.length == 10);
1370 
1371     // Now a test of a larger dataset where more subtle bugs could hide.
1372     // If the values returned are unique even after sorting, are composed of
1373     // the correct elements, and there is the right number of them, this thing
1374     // works.
1375 
1376     bool[uint[]] results;  // Keep track of how many UNIQUE items we have.
1377     auto comb3 = Comb!(uint)(seq(10U, 22U), 6);
1378     foreach(c; comb3) {
1379         auto dupped = c.dup.sort();
1380         // Make sure all elems are unique and within range.
1381         assert(dupped.length == 6);
1382         assert(dupped[0] > 9 && dupped[0] < 22);
1383         foreach(i; 1..dupped.length) {
1384             // Make sure elements are unique.  Remember, the array is sorted.
1385             assert(dupped[i] > dupped[i - 1]);
1386             assert(dupped[i] > 9 && dupped[i] < 22);
1387         }
1388         results[dupped.release().idup] = true;
1389     }
1390     assert(results.length == 924);  // (12 choose 6).
1391 }
1392 
1393 /**Converts a range with arbitrary element types (usually strings) to a
1394  * range of reals lazily.  Ignores any elements that could not be successfully
1395  * converted.  Useful for creating an input range that can be used with this
1396  * lib out of a File without having to read the whole file into an array first.
1397  * The advantages to this over just using std.algorithm.map are that it's
1398  * less typing and that it ignores non-convertible elements, such as blank
1399  * lines.
1400  *
1401  * If rangeIn is an inputRange, then the result of this function is an input
1402  * range.  Otherwise, the result is a forward range.
1403  *
1404  * Note:  The reason this struct doesn't have length or random access,
1405  * even if this is supported by rangeIn, is because it has to be able to
1406  * filter out non-convertible elements.
1407  *
1408  * Examples:
1409  * ---
1410  * // Perform a T-test to see whether the mean of the data being input as text
1411  * // from stdin is different from zero.  This data might not even fit in memory
1412  * // if it were read in eagerly.
1413  *
1414  * auto myRange = toNumericRange( stdin.byLine() );
1415  * TestRes result = studentsTTest(myRange);
1416  * writeln(result);
1417  * ---
1418  */
1419 ToNumericRange!R toNumericRange(R)(R rangeIn) if(isInputRange!R) {
1420     alias ToNumericRange!R RT;
1421     return RT(rangeIn);
1422 }
1423 
1424 ///
1425 struct ToNumericRange(R) if(isInputRange!R) {
1426 private:
1427     alias ElementType!R E;
1428     R inputRange;
1429     real _front;
1430 
1431 public:
1432     this(R inputRange) {
1433         this.inputRange = inputRange;
1434         try {
1435             _front = to!real(inputRange.front);
1436         } catch(ConvException) {
1437             popFront();
1438         }
1439     }
1440 
1441     @property real front() {
1442         return _front;
1443     }
1444 
1445     void popFront() {
1446         while(true) {
1447             inputRange.popFront();
1448             if(inputRange.empty) {
1449                 return;
1450             }
1451             auto inFront = inputRange.front;
1452 
1453             // If inFront is some string, strip the whitespace.
1454             static if( is(typeof(strip(inFront)))) {
1455                 inFront = strip(inFront);
1456             }
1457 
1458             try {
1459                 _front = to!real(inFront);
1460                 return;
1461             } catch(ConvException) {
1462                 continue;
1463             }
1464         }
1465     }
1466 
1467     @property bool empty() {
1468         return inputRange.empty;
1469     }
1470 }
1471 
1472 unittest {
1473     // Test both with non-convertible element as first element and without.
1474     // This is because non-convertible elements as the first element are
1475     // handled as a special case in the implementation.
1476     string[2] dataArr = ["3.14\n2.71\n8.67\nabracadabra\n362436",
1477                  "foobar\n3.14\n2.71\n8.67\nabracadabra\n362436"];
1478 
1479     foreach(data; dataArr) {
1480         std.file.write("NumericFileTestDeleteMe.txt", data);
1481         scope(exit) std.file.remove("NumericFileTestDeleteMe.txt");
1482         auto myFile = File("NumericFileTestDeleteMe.txt");
1483         auto rng = toNumericRange(myFile.byLine());
1484         assert(approxEqual(rng.front, 3.14));
1485         rng.popFront;
1486         assert(approxEqual(rng.front, 2.71));
1487         rng.popFront;
1488         assert(approxEqual(rng.front, 8.67));
1489         rng.popFront;
1490         assert(approxEqual(rng.front, 362435));
1491         assert(!rng.empty);
1492         rng.popFront;
1493         assert(rng.empty);
1494         myFile.close();
1495     }
1496 }
1497 
1498 // Used for ILP optimizations.
1499 package template StaticIota(size_t upTo) {
1500     static if(upTo == 0) {
1501         alias TypeTuple!() StaticIota;
1502     } else {
1503         alias TypeTuple!(StaticIota!(upTo - 1), upTo - 1) StaticIota;
1504     }
1505 }
1506 
1507 package:
1508 
1509 // If SciD is available, it is used for matrix storage and operations
1510 // such as Cholesky decomposition.  Otherwise, some old, crappy routines
1511 // that were around since before SciD are used.  
1512 version(scid) {
1513     import scid.matvec, scid.linalg, scid.exception;
1514     
1515     alias ExternalMatrixView!double DoubleMatrix;
1516     
1517     DoubleMatrix doubleMatrix(
1518         size_t nRows, 
1519         size_t nColumns,
1520         RegionAllocator alloc
1521     ) {
1522         return DoubleMatrix(nRows, nColumns, alloc);
1523     }
1524     
1525     void choleskySolve(DoubleMatrix a, double[] b, double[] x) {
1526         choleskyDestructive(a);
1527         auto ans = externalVectorView(x);
1528         auto vec = externalVectorView(b);
1529         scid.linalg.choleskySolve(a, vec, ans);
1530     }
1531     
1532     void invert(DoubleMatrix from, DoubleMatrix to) {
1533         try {
1534             to[] = inv(from);
1535         } catch(Exception) {
1536             foreach(i; 0..to.rows) foreach(j; 0..to.columns) {
1537                 to[i, j] = double.nan;
1538             }
1539         }
1540     }
1541         
1542 } else {
1543     version = noscid;
1544     
1545     struct DoubleMatrix {
1546         double[][] arrayOfArrays;
1547         //alias arrayOfArraysFun this;
1548         
1549         const pure nothrow @property {
1550             size_t rows() {
1551                 return arrayOfArrays.length;
1552             }
1553             
1554             size_t columns() {
1555                 return (arrayOfArrays.length) ? 
1556                     (arrayOfArrays[0].length) : 0;
1557             }
1558         }                    
1559         
1560         ref double opIndex(size_t i, size_t j) {
1561             return arrayOfArrays[i][j];
1562         }
1563     }
1564     
1565     DoubleMatrix doubleMatrix(
1566         size_t nRows, 
1567         size_t nColumns,
1568         RegionAllocator alloc
1569     ) {
1570         return DoubleMatrix(
1571             alloc.uninitializedArray!(double[][])(nRows, nColumns)
1572         );
1573     }    
1574     
1575     void invert(DoubleMatrix from, DoubleMatrix to) {
1576         invert(from.arrayOfArrays, to.arrayOfArrays);
1577     }
1578     
1579     // Uses Gauss-Jordan elim. w/ row pivoting to invert from.  Stores the results
1580     // in to and leaves from in an undefined state.
1581     package void invert(double[][] from, double[][] to) {
1582         // Normalize.
1583         foreach(i, row; from) {
1584             double absMax = 1.0 / reduce!(max)(map!(abs)(row[0..from.length]));
1585             row[] *= absMax;
1586             to[i][] = 0;
1587             to[i][i] = absMax;
1588         }
1589 
1590         foreach(col; 0..from.length) {
1591             size_t bestRow;
1592             double biggest = 0;
1593             foreach(row; col..from.length) {
1594                 if(abs(from[row][col]) > biggest) {
1595                     bestRow = row;
1596                     biggest = abs(from[row][col]);
1597                 }
1598             }
1599 
1600             swap(from[col], from[bestRow]);
1601             swap(to[col], to[bestRow]);
1602             immutable pivotFactor = from[col][col];
1603 
1604             foreach(row; 0..from.length) if(row != col) {
1605                 immutable ratio = from[row][col] / pivotFactor;
1606 
1607                 // If you're ever looking to optimize this code, good luck.  The
1608                 // bottleneck is almost ENTIRELY this one line:
1609                 from[row][] -= from[col][] * ratio;
1610                 to[row][] -= to[col][] * ratio;
1611             }
1612         }
1613 
1614         foreach(i; 0..from.length) {
1615             immutable diagVal = from[i][i];
1616             from[i][] /= diagVal;
1617             to[i][] /= diagVal;
1618         }
1619     }
1620 
1621     unittest {
1622         auto mat = [[1.0, 2, 3], [4.0, 7, 6], [7.0, 8, 9]];
1623         auto toMat = [new double[3], new double[3], new double[3]];
1624         invert(mat, toMat);
1625         assert(approxEqual(toMat[0], [-0.625, -0.25, 0.375]));
1626         assert(approxEqual(toMat[1], [-0.25, 0.5, -0.25]));
1627         assert(approxEqual(toMat[2], [0.708333, -0.25, 0.041667]));
1628     }
1629 
1630     void solve(DoubleMatrix mat, double[] vec) {
1631         solve(mat.arrayOfArrays, vec);
1632     }
1633 
1634     // Solve a system of linear equations mat * b = vec for b.  The result is
1635     // stored in vec, and mat is left in an undefined state. Uses Gaussian
1636     // elimination w/ row pivoting.  Roughly 4x faster than using inversion to
1637     // solve the system, and uses roughly half the memory.
1638     void solve(double[][] mat, double[] vec) {
1639         // Normalize.
1640         foreach(i, row; mat) {
1641             double absMax = 1.0 / reduce!(max)(map!(abs)(row[0..mat.length]));
1642             row[] *= absMax;
1643             vec[i] *= absMax;
1644         }
1645 
1646         foreach(col; 0..mat.length - 1) {
1647             size_t bestRow;
1648             double biggest = 0;
1649             foreach(row; col..mat.length) {
1650                 if(abs(mat[row][col]) > biggest) {
1651                     bestRow = row;
1652                     biggest = abs(mat[row][col]);
1653                 }
1654             }
1655 
1656             swap(mat[col], mat[bestRow]);
1657             swap(vec[col], vec[bestRow]);
1658             immutable pivotFactor = mat[col][col];
1659 
1660             foreach(row; col + 1..mat.length) {
1661                 immutable ratio = mat[row][col] / pivotFactor;
1662 
1663                 // If you're ever looking to optimize this code, good luck.  The
1664                 // bottleneck is almost ENTIRELY this one line:
1665                 mat[row][col..$] -= mat[col][col..$] * ratio;
1666                 vec[row] -= vec[col] * ratio;
1667             }
1668         }
1669 
1670         foreach(i; 0..mat.length) {
1671             double diagVal = mat[i][i];
1672             mat[i][] /= diagVal;
1673             vec[i] /= diagVal;
1674         }
1675 
1676         // Do back substitution.
1677         for(size_t row = mat.length - 1; row != size_t.max; row--) {
1678             auto v1 = vec[row + 1..$];
1679             auto v2 = mat[row][$ - v1.length..$];
1680             vec[row] -= dotProduct(v1, v2);
1681         }
1682     }
1683 
1684     unittest {
1685         auto mat = [[2.0, 1, -1], [-3.0, -1, 2], [-2.0, 1, 2]];
1686         auto vec = [8.0, -11, -3];
1687         solve(mat, vec);
1688         assert(approxEqual(vec, [2, 3, -1]));
1689 
1690         auto mat2 = [[1.0, 2, 3], [4.0, 7, 6], [7.0, 8, 9]];
1691         auto vec2 = [8.0, 6, 7];
1692         solve(mat2, vec2);
1693         assert(approxEqual(vec2, [-3.875, -0.75, 4.45833]));
1694     }
1695 
1696     // Cholesky decomposition functions adapted from Don Clugston's MathExtra
1697     // lib, used w/ permission.
1698     void choleskyDecompose(double[][] a, double[] diag) {
1699         immutable N = diag.length;
1700 
1701         foreach(i; 0..N) {
1702             const ai = a[i];
1703             double sum = ai[i];
1704 
1705             for(sizediff_t k = i - 1; k >= 0; --k) {
1706                 sum -= ai[k] * ai[k];
1707             }
1708 
1709             if (sum > 0.0) {
1710                 diag[i] = sqrt(sum);
1711 
1712                 foreach(j; i + 1..N) {
1713                     sum = ai[j] - dotProduct(ai[0..i], a[j][0..i]);
1714                     a[j][i] = sum / diag[i];
1715                 }
1716             } else {
1717                 // not positive definite (could be caused by rounding errors)
1718                 diag[i] = 0;
1719                 // make this whole row zero so they have no further effect
1720                 foreach(j; i + 1..N) a[j][i] = 0;
1721             }
1722         }
1723     }
1724 
1725     void choleskySolve(double[][] a, double[] diag, double[] b, double[] x) {
1726         immutable N = x.length;
1727 
1728         foreach(i; 0..N) {
1729             const ai = a[i];
1730 
1731             if(diag[i] > 0)  {
1732                 double sum = b[i];
1733                 sum -= dotProduct(ai[0..i], x[0..i]);
1734                 x[i] = sum / diag[i];
1735             } else x[i] = 0; // skip pos definite rows
1736         }
1737 
1738         for(sizediff_t i = N - 1; i >= 0; --i) {
1739             if (diag[i] > 0) {
1740                 double sum = x[i];
1741                 for(sizediff_t k = i + 1; k < N; ++k) sum -= a[k][i] * x[k];
1742                 x[i] = sum / diag[i];
1743             } else x[i] = 0; // skip pos definite rows
1744         }
1745         // Convert failed columns in solution to NANs if required.
1746         foreach(i; 0..N) {
1747             if(diag[i].isNaN() || diag[i] <= 0) x[i] = double.nan;
1748         }
1749     }
1750 
1751     void choleskySolve(DoubleMatrix a, double[] b, double[] x) {
1752         auto alloc = newRegionAllocator();
1753         auto diag = alloc.uninitializedArray!(double[])(x.length);
1754         choleskyDecompose(a.arrayOfArrays, diag);
1755         choleskySolve(a.arrayOfArrays, diag, b, x);
1756     }
1757 }