1 /** Stuff that is useful when porting FORTRAN code to D.
2 
3     Authors:    Lars Tandle Kyllingstad
4     Copyright:  Copyright (c) 2009–2011, Lars T. Kyllingstad. All rights reserved.
5     License:    Boost License 1.0
6 */
7 module scid.core.fortran;
8 
9 
10 import std.conv;
11 import std.traits: Unqual;
12 
13 
14 
15 
16 /** Wrap a one- or two-dimensional array around the given pointer.
17     Meant as a substitute for FORTRAN's dimension statement.
18 */
19 FortranArray!T dimension(T)(T* ptr, size_t len)
20 {
21     FortranArray!T a;
22     a._length = len;
23     a._ptr = ptr;
24     a._ptrm1 = ptr - 1;
25     return a;
26 }
27 
28 
29 /// ditto
30 FortranArray2D!T dimension(T)(T* ptr, size_t rows, size_t cols)
31 {
32     FortranArray2D!T a;
33     a._length = rows*cols;
34     a._ptr = ptr;
35     a._ptrm1 = ptr - 1;
36     a._rows = rows;
37     a._cols = cols;
38     return a;
39 }
40 
41 
42 
43 
44 /** A simple, lightweight, one-dimensional base-1 array. */
45 struct FortranArray(T)
46 {
47 private:
48     // By putting _length and _ptr first, this array is binary compatible
49     // with built-in D arrays.
50     size_t _length;
51     T* _ptr;
52     T* _ptrm1;
53 
54     void boundsCheck(size_t i, string file, int line) const
55     {
56         assert (i>0  &&  i<=_length,
57             file~":"~to!string(line)~":index out of bounds");
58     }
59 
60 
61 public:
62     /// The length of the array.
63     @property size_t length() const { return _length; }
64 
65 
66     /// A pointer to the first element of the array.
67     @property T* ptr() { return _ptr; }
68 
69 
70     static if (is(T == const) || is(T == immutable))
71     {
72         Unqual!T opIndex
73             (string file = __FILE__, int line = __LINE__)
74             (size_t i)
75             const
76         {
77             boundsCheck(i, file, line);
78             return _ptrm1[i];
79         }
80     }
81     else
82     {
83         // Workaround for DMD bug 2460
84         template opIndex(string file = __FILE__, int line = __LINE__) {
85         ref T opIndex
86             (size_t i)
87         {
88             boundsCheck(i, file, line);
89             return _ptrm1[i];
90         }}
91 
92         T opIndexAssign
93             (string file = __FILE__, int line = __LINE__)
94             (T value, size_t i)
95         {
96             boundsCheck(i, file, line);
97             return (_ptrm1[i] = value);
98         }
99 
100         T opIndexOpAssign
101             (string op, string file = __FILE__, int line = __LINE__)
102             (T value, size_t i)
103         {
104             boundsCheck(i, file, line);
105             mixin("return _ptrm1[i] "~op~"= value;");
106         }
107     }
108 }
109 
110 
111 unittest
112 {
113     int[] a = [1, 2, 3];
114     auto b = dimension(a.ptr, 3);
115 
116     // Basic functionality
117     assert (b[3] == a[2]);
118     b[3] = 5;
119     assert (b[3] == 5);
120     b[3] -= 2;
121     assert (b[3] == 3);
122 
123     assert (b.ptr[2] == 3);
124 
125     // Binary compatible with D arrays.
126     int[] d = *(cast(int[]*) &b);
127     assert (d == a);
128 
129     // Works with const and immutable
130     immutable int[] ia = [1,2,3];
131     auto ib = dimension(ia.ptr, 3);
132     assert (ib[3] == 3);
133     assert (!__traits(compiles, { ib[3] = 5; }));
134 
135 }
136 
137 
138 
139 
140 /** A simple, lightweight, two-dimensional base-1 array. */
141 struct FortranArray2D(T)
142 {
143 private:
144     // By putting length and ptr first, this array is binary compatible
145     // with built-in D arrays.
146     size_t _length;
147     T* _ptr;
148     T* _ptrm1;
149     size_t _rows;
150     size_t _cols;
151 
152     void boundsCheck(size_t i, size_t j, string file, int line) const
153     {
154         assert (i>0  &&  i<=_rows,
155             file~":"~to!string(line)~":first index out of bounds");
156         assert (j>0  &&  j<=_cols,
157             file~":"~to!string(line)~":second index out of bounds");
158     }
159 
160 
161 public:
162     /// The number of rows and columns in the array.
163     @property size_t rows() const { return _rows; }
164     @property size_t cols() const { return _cols; }     /// ditto
165 
166 
167     /// The number of elements in the array.
168     @property size_t length() const { return _length; }
169 
170 
171     /// A pointer to the first element of the array.
172     @property T* ptr() { return _ptr; }
173 
174 
175     static if (is(T == const) || is(T == immutable))
176     {
177         Unqual!T opIndex
178             (string file = __FILE__, int line = __LINE__)
179             (size_t i, size_t j)
180             const
181         {
182             boundsCheck(i, j, file, line);
183             return _ptrm1[i + (j-1)*_rows];
184         }
185     }
186     else
187     {
188         // Workaround for DMD bug 2460
189         template opIndex(string file = __FILE__, int line = __LINE__) {
190         ref T opIndex
191             (size_t i, size_t j)
192         {
193             boundsCheck(i, j, file, line);
194             return _ptrm1[i + (j-1)*_rows];
195         }}
196 
197         T opIndexAssign
198             (string file = __FILE__, int line = __LINE__)
199             (T value, size_t i, size_t j)
200         {
201             boundsCheck(i, j, file, line);
202             return (_ptrm1[i + (j-1)*_rows] = value);
203         }
204 
205         T opIndexOpAssign
206             (string op, string file = __FILE__, int line = __LINE__)
207             (T value, size_t i, size_t j)
208         {
209             boundsCheck(i, j, file, line);
210             mixin("return _ptrm1[i + (j-1)*_rows] "~op~"= value;");
211         }
212     }
213 }
214 
215 unittest
216 {
217     int[] a = [1, 2, 3, 4, 5, 6];
218     auto b = dimension(a.ptr, 3, 2);
219 
220     // Basic functionality
221     assert (b[2,2] == a[4]);
222     b[2,2] = 5;
223     assert (b[2,2] == 5);
224     b[2,2] -= 2;
225     assert (b[2,2] == 3);
226 
227     assert (b.ptr[4] == 3);
228 
229     // Binary compatible with D arrays.
230     int[] d = *(cast(int[]*) &b);
231     assert (d == a);
232 
233     // Works with const and immutable
234     const int[] ia = [1, 2, 3, 4, 5, 6];
235     auto ib = dimension(ia.ptr, 3, 2);
236     assert (ib[2,2] == ia[4]);
237     assert (!__traits(compiles, { ib[2,2] = 5; }));
238 }
239 
240 
241 
242 
243 /** Convert an unsigned integer to a signed 32-bit integer.
244 
245     FORTRAN code typically uses 32-bit signed ints for things
246     like array lengths.  D array lengths, on the other hand,
247     are uints in 32-bit mode, and ulongs in 64-bit mode.
248     Use of this function is safer than inserting casts everywhere,
249     because it checks that the given uint/ulong fits in an int.
250     For efficiency, this check is disabled in release mode
251     (which is a good reason to use this function over
252     std.conv.to!int).
253 */
254 int toInt(size_t u) @safe pure nothrow
255 {
256     assert (u <= int.max, "Number doesn't fit in a 32-bit integer");
257     return cast(int) u;
258 }
259 
260 
261 unittest
262 {
263     assert (toInt(10uL) == 10);
264     assert (toInt(10u) == 10);
265 }