1 /**
2  * Utility functions used across the library.
3  *
4  * Copyright: 2017 Netflix, Inc.
5  * License: $(LINK2 http://www.apache.org/licenses/LICENSE-2.0, Apache License Version 2.0)
6  */
7 module vectorflow.utils;
8 
9 private{
10 import std.ascii : isDigit;
11 import std.random;
12 import std.stdio : stdout;
13 import std.traits : isSomeString;
14 
15 extern(C) int isatty(int);
16 }
17 
18 /// RNG used across the library
19 auto static RAND_GEN = Xorshift(42); // way faster than Mersenne-Twister
20 
21 auto init_matrix_rand(T)(T[][] M, double rand_scale)
22 {
23     foreach(i; 0..M.length)
24         foreach(j; 0..M[i].length)
25             M[i][j] = rand_scale * (2 * uniform01(RAND_GEN) - 1);
26     return M;
27 }
28 
29 auto allocate_matrix_zero(T)(ulong num_row, ulong num_col)
30 {
31     T[][] M;
32     M.length = num_row;
33     foreach(i; 0..num_row)
34     {
35         M[i].length = num_col;
36         foreach(j; 0..num_col)
37             M[i][j] = 0;
38     }
39     return M;
40 }
41 
42 /**
43 * Fast but unsafe function to parse a string into a long.
44 */
45 long to_long(T)(in T str) pure
46     if(isSomeString!T)
47 {
48     long val = 0;
49     foreach(c; str)
50         val = 10 * val + (c - '0');
51     return val;
52 }
53 
54 /**
55 * Fast but unsafe function to parse a string into a float.
56 * 
57 * If you trust your input, this is much faster than to!float.
58 * Doesn't handle Inf numbers nor Nan, and doesn't throw exceptions.
59 * Adapted from Phobos std.conv source code. See NOTICE for licence details.
60 */
61 float to_float(T)(in T p) pure
62     if(isSomeString!T)
63 {
64     static immutable real[14] negtab =
65         [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L,
66                 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ];
67     static immutable real[13] postab =
68         [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L,
69                 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ];
70 
71     int ind = 0;
72     ulong len = p.length;
73 
74     float ldval = 0.0;
75     char dot = 0;
76     int exp = 0;
77     long msdec = 0, lsdec = 0;
78     ulong msscale = 1;
79 
80     char sign = 0;
81     switch (p[ind])
82     {
83         case '-':
84             sign++;
85             ind++;
86             break;
87         case '+':
88             ind++;
89             break;
90         default: {}
91     }
92 
93     while (ind < len)
94     {
95         int i = p[ind];
96         while (isDigit(i))
97         {
98             if (msdec < (0x7FFFFFFFFFFFL-10)/10)
99                 msdec = msdec * 10 + (i - '0');
100             else if (msscale < (0xFFFFFFFF-10)/10)
101             {
102                 lsdec = lsdec * 10 + (i - '0');
103                 msscale *= 10;
104             }
105             else
106                 exp++;
107 
108             exp -= dot;
109             ind++;
110             if (ind == len)
111                 break;
112             i = p[ind];
113         }
114         if (i == '.' && !dot)
115         {
116             ind++;
117             dot++;
118         }
119         else
120             break;
121     }
122     if (ind < len && (p[ind] == 'e' || p[ind] == 'E'))
123     {
124         char sexp;
125         int e;
126 
127         sexp = 0;
128         ind++;
129         switch (p[ind])
130         {
131             case '-':    sexp++;
132                          goto case;
133             case '+':    ind++;
134                          break;
135             default: {}
136         }
137         e = 0;
138         while (ind < len && isDigit(p[ind]))
139         {
140             if (e < 0x7FFFFFFF / 10 - 10)   // prevent integer overflow
141             {
142                 e = e * 10 + p[ind] - '0';
143             }
144             ind++;
145         }
146         exp += (sexp) ? -e : e;
147     }
148 
149     ldval = msdec;
150     if (msscale != 1)
151         ldval = ldval * msscale + lsdec;
152     if (ldval)
153     {
154         uint u = 0;
155         int pow = 4096;
156 
157         while (exp > 0)
158         {
159             while (exp >= pow)
160             {
161                 ldval *= postab[u];
162                 exp -= pow;
163             }
164             pow >>= 1;
165             u++;
166         }
167         while (exp < 0)
168         {
169             while (exp <= -pow)
170             {
171                 ldval *= negtab[u];
172                 exp += pow;
173             }
174             pow >>= 1;
175             u++;
176         }
177     }
178     return (sign) ? -ldval : ldval;
179 }
180 
181 
182 final class Hasher {
183     // MurmurHash3 was written by Austin Appleby, and is placed in the public
184     // domain. The author hereby disclaims copyright to this source code.
185     // Original C++ source code at: https://code.google.com/p/smhasher/
186 
187     private static uint _rotl32(uint x, int r) pure nothrow @safe
188     {
189         return (x << r) | (x >> (32 - r));
190     }
191 
192     // Finalization mix - force all bits of a hash block to avalanche
193     private static uint fmix32(uint h) pure nothrow @safe
194     {
195       h ^= h >> 16;
196       h *= 0x85ebca6b;
197       h ^= h >> 13;
198       h *= 0xc2b2ae35;
199       h ^= h >> 16;
200 
201       return h;
202     }
203 
204     public static uint MurmurHash3(T)(in T key, uint seed = 42) pure nothrow
205         if(isSomeString!T)
206     {
207       const ubyte * data = cast(const(ubyte*))key;
208       uint len = cast(uint)key.length;
209       const int nblocks = len / 4;
210 
211       uint h1 = seed;
212 
213       const uint c1 = 0xcc9e2d51;
214       const uint c2 = 0x1b873593;
215 
216       // body
217       const uint * blocks = cast(const (uint *))(data + nblocks*4);
218 
219       for(int i = -nblocks; i; i++)
220       {
221         uint k1 = blocks[i];
222 
223         k1 *= c1;
224         k1 = _rotl32(k1,15);
225         k1 *= c2;
226         
227         h1 ^= k1;
228         h1 = _rotl32(h1,13); 
229         h1 = h1*5+0xe6546b64;
230       }
231 
232       // tail
233       const ubyte * tail = cast(const (ubyte*))(data + nblocks*4);
234 
235       uint k1 = 0;
236 
237       switch(len & 3)
238       {
239         case 3: k1 ^= tail[2] << 16; goto case 2;
240         case 2: k1 ^= tail[1] << 8; goto case 1;
241         case 1: k1 ^= tail[0];
242                 k1 *= c1;
243                 k1 = _rotl32(k1,15);
244                 k1 *= c2;
245                 h1 ^= k1; goto default;
246         default:
247             break;
248       }
249 
250       // finalization
251       h1 ^= len;
252       h1 = fmix32(h1);
253 
254       return h1;
255     }
256 }
257 
258 package static void ct_msg(string msg)()
259 {
260     pragma(msg, "[VECTORFLOW-COMPILE] " ~ msg);
261 }
262 
263 package bool isTerminal()
264 {
265     return isatty(stdout.fileno) == 1;
266 }
267 
268 package mixin template opCallNew()
269 {
270     static auto opCall(T...)(T args)
271     {
272         return new typeof(this)(args);
273     }
274 }