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 }