1 module ithox.qrcode.common.qrcodeversion; 2 3 import ithox.qrcode.common.ecblocks; 4 import ithox.qrcode.common.errorcorrectionlevel; 5 import ithox.qrcode.common.bitmatrix; 6 7 import std.conv; 8 9 class QrCodeVersion 10 { 11 ///int[] 12 enum int[] versionDecodeInfo = [ 13 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 14 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 15 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 16 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69 17 ]; 18 19 protected int versionNumber, totalCodewords; 20 21 protected int[] alignmentPatternCenters; 22 protected EcBlocks[] errorCorrectionBlocks; 23 24 protected static QrCodeVersion[string] versions; 25 26 this(int vn, int[] aps, EcBlocks[] ecbs) 27 { 28 versionNumber = vn; 29 alignmentPatternCenters = aps; 30 errorCorrectionBlocks = ecbs; 31 totalCodewords = 0; 32 auto ecCodewords = ecbs[0].getEcCodewordsPerBlock(); 33 foreach (ecb; ecbs[0].getEcBlocks()) 34 { 35 totalCodewords += ecb.count * (ecb.dataCodewords + ecCodewords); 36 } 37 } 38 39 ///gets the version number. 40 public @property int VersionNumber() 41 { 42 return this.versionNumber; 43 } 44 /** 45 * Gets the alignment pattern centers. 46 * 47 * @return SplFixedArray 48 */ 49 public @property int[] AlignmentPatternCenters() 50 { 51 return alignmentPatternCenters; 52 } 53 54 ///Gets the total number of codewords. 55 public @property int TotalCodewords() 56 { 57 return this.totalCodewords; 58 } 59 60 /** 61 * Gets the dimension for the current version. 62 * 63 * @return integer 64 */ 65 public @property int DimensionForVersion() 66 { 67 return 17 + 4 * this.versionNumber; 68 } 69 70 /** 71 * Gets the number of EC blocks for a specific EC level. 72 * 73 * @param ErrorCorrectionLevel $ecLevel 74 * @return EcBlocks 75 */ 76 public EcBlocks getEcBlocksForLevel(ErrorCorrectionLevel ecLevel) 77 { 78 return this.errorCorrectionBlocks[getOrdinalErrorCorrectionLevel(ecLevel)]; 79 } 80 81 /** 82 * Gets a provisional version number for a specific dimension. 83 * 84 * @param integer $dimension 85 * @return Version 86 * @throws Exception\InvalidArgumentException 87 */ 88 public static QrCodeVersion getProvisionalVersionForDimension(int dimension) 89 { 90 if (dimension % 4 != 1) 91 { 92 throw new Exception("Dimension is not 1 mod 4"); 93 } 94 return getVersionForNumber((dimension - 17) >> 2); 95 } 96 97 /** 98 * Gets a version instance for a specific version number. 99 * 100 * @param integer $versionNumber 101 * @return QrCodeVersion 102 * @throws Exception\InvalidArgumentException 103 */ 104 public static QrCodeVersion getVersionForNumber(int versionNumber) 105 { 106 if (versionNumber < 1 || versionNumber > 40) 107 { 108 throw new Exception("Version number must be between 1 and 40"); 109 } 110 auto p = to!string(versionNumber) in versions; 111 if (p) 112 { 113 return *p; 114 } 115 else 116 { 117 return buildVersion(versionNumber); 118 } 119 120 } 121 122 /** 123 * Build and cache a specific version. 124 * 125 * See ISO 18004:2006 6.5.1 Table 9. 126 * 127 * @param integer versionNumber 128 * @return void 129 */ 130 protected static QrCodeVersion buildVersion(int versionNumber) 131 { 132 int[] patterns; 133 EcBlocks[] ecBlocks; 134 final switch (versionNumber) 135 { 136 case 1: 137 patterns = []; 138 ecBlocks = [new EcBlocks(7, new EcBlock(1, 19)), new EcBlocks(10, 139 new EcBlock(1, 16)), new EcBlocks(13, new EcBlock(1, 13)), 140 new EcBlocks(17, new EcBlock(1, 9)),]; 141 break; 142 case 2: 143 patterns = [6, 18]; 144 ecBlocks = [new EcBlocks(10, new EcBlock(1, 34)), new EcBlocks(16, 145 new EcBlock(1, 28)), new EcBlocks(22, new EcBlock(1, 22)), 146 new EcBlocks(28, new EcBlock(1, 16)),]; 147 break; 148 case 3: 149 patterns = [6, 22]; 150 ecBlocks = [new EcBlocks(15, new EcBlock(1, 55)), new EcBlocks(26, 151 new EcBlock(1, 44)), new EcBlocks(18, new EcBlock(2, 17)), 152 new EcBlocks(22, new EcBlock(2, 13)),]; 153 break; 154 case 4: 155 patterns = [6, 26]; 156 ecBlocks = [new EcBlocks(20, new EcBlock(1, 80)), new EcBlocks(18, 157 new EcBlock(2, 32)), new EcBlocks(26, new EcBlock(3, 24)), 158 new EcBlocks(16, new EcBlock(4, 9)),]; 159 break; 160 case 5: 161 patterns = [6, 30]; 162 ecBlocks = [new EcBlocks(26, new EcBlock(1, 108)), new EcBlocks(24, 163 new EcBlock(2, 43)), new EcBlocks(18, new EcBlock(2, 15), 164 new EcBlock(2, 16)), new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12)),]; 165 break; 166 case 6: 167 patterns = [6, 34]; 168 ecBlocks = [new EcBlocks(18, new EcBlock(2, 68)), new EcBlocks(16, 169 new EcBlock(4, 27)), new EcBlocks(24, new EcBlock(4, 19)), 170 new EcBlocks(28, new EcBlock(4, 15)),]; 171 break; 172 case 7: 173 patterns = [6, 22, 38]; 174 ecBlocks = [new EcBlocks(20, new EcBlock(2, 78)), new EcBlocks(18, 175 new EcBlock(4, 31)), new EcBlocks(18, new EcBlock(2, 14), 176 new EcBlock(4, 15)), new EcBlocks(26, new EcBlock(4, 13)),]; 177 break; 178 case 8: 179 patterns = [6, 24, 42]; 180 ecBlocks = [new EcBlocks(24, new EcBlock(2, 97)), new EcBlocks(22, 181 new EcBlock(2, 38), new EcBlock(2, 39)), new EcBlocks(22, new EcBlock(4, 182 18), new EcBlock(2, 19)), new EcBlocks(26, new EcBlock(4, 183 14), new EcBlock(2, 15)),]; 184 break; 185 case 9: 186 patterns = [6, 26, 46]; 187 ecBlocks = [new EcBlocks(30, new EcBlock(2, 116)), new EcBlocks(22, 188 new EcBlock(3, 36), new EcBlock(2, 37)), new EcBlocks(20, new EcBlock(4, 189 16), new EcBlock(4, 17)), new EcBlocks(24, new EcBlock(4, 190 12), new EcBlock(4, 13)),]; 191 break; 192 case 10: 193 patterns = [6, 28, 50]; 194 ecBlocks = [new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)), 195 new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)), new EcBlocks(24, 196 new EcBlock(6, 19), new EcBlock(2, 20)), new EcBlocks(28, 197 new EcBlock(6, 15), new EcBlock(2, 16)),]; 198 break; 199 case 11: 200 patterns = [6, 30, 54]; 201 ecBlocks = [new EcBlocks(20, new EcBlock(4, 81)), new EcBlocks(30, 202 new EcBlock(1, 50), new EcBlock(4, 51)), new EcBlocks(28, new EcBlock(4, 203 22), new EcBlock(4, 23)), new EcBlocks(24, new EcBlock(3, 204 12), new EcBlock(8, 13)),]; 205 break; 206 case 12: 207 patterns = [6, 32, 58]; 208 ecBlocks = [new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)), 209 new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)), new EcBlocks(26, 210 new EcBlock(4, 20), new EcBlock(6, 21)), new EcBlocks(28, 211 new EcBlock(7, 14), new EcBlock(4, 15)),]; 212 break; 213 case 13: 214 patterns = [6, 34, 62]; 215 ecBlocks = [new EcBlocks(26, new EcBlock(4, 107)), new EcBlocks(22, 216 new EcBlock(8, 37), new EcBlock(1, 38)), new EcBlocks(24, new EcBlock(8, 217 20), new EcBlock(4, 21)), new EcBlocks(22, new EcBlock(12, 218 11), new EcBlock(4, 12)),]; 219 break; 220 case 14: 221 patterns = [6, 26, 46, 66]; 222 ecBlocks = [new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 223 116)), new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 224 41)), new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 225 17)), new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13)),]; 226 break; 227 case 15: 228 patterns = [6, 26, 48, 70]; 229 ecBlocks = [new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)), 230 new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)), new EcBlocks(30, 231 new EcBlock(5, 24), new EcBlock(7, 25)), new EcBlocks(24, 232 new EcBlock(11, 12), new EcBlock(7, 13)),]; 233 break; 234 case 16: 235 patterns = [6, 26, 50, 74]; 236 ecBlocks = [new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)), 237 new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)), new EcBlocks(24, 238 new EcBlock(15, 19), new EcBlock(2, 20)), new EcBlocks(30, 239 new EcBlock(3, 15), new EcBlock(13, 16)),]; 240 break; 241 case 17: 242 patterns = [6, 30, 54, 78]; 243 ecBlocks = [new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 244 108)), new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 245 47)), new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 246 23)), new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15)),]; 247 break; 248 case 18: 249 patterns = [6, 30, 56, 82]; 250 ecBlocks = [new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 251 121)), new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 252 44)), new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 253 23)), new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15)),]; 254 break; 255 case 19: 256 patterns = [6, 30, 58, 86]; 257 ecBlocks = [new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 258 114)), new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 259 45)), new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 260 22)), new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14)),]; 261 break; 262 case 20: 263 patterns = [6, 34, 62, 90]; 264 ecBlocks = [new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 265 108)), new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 266 42)), new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 267 25)), new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16)),]; 268 break; 269 case 21: 270 patterns = [6, 28, 50, 72, 94]; 271 ecBlocks = [new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 272 117)), new EcBlocks(26, new EcBlock(17, 42)), new EcBlocks(28, 273 new EcBlock(17, 22), new EcBlock(6, 23)), new EcBlocks(30, 274 new EcBlock(19, 16), new EcBlock(6, 17)),]; 275 break; 276 case 22: 277 patterns = [6, 26, 50, 74, 98]; 278 ecBlocks = [new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 279 112)), new EcBlocks(28, new EcBlock(17, 46)), new EcBlocks(30, 280 new EcBlock(7, 24), new EcBlock(16, 25)), new EcBlocks(24, 281 new EcBlock(34, 13)),]; 282 break; 283 case 23: 284 patterns = [6, 30, 54, 78, 102]; 285 ecBlocks = [new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 286 122)), new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 287 48)), new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 288 25)), new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16)),]; 289 break; 290 case 24: 291 patterns = [6, 28, 54, 80, 106]; 292 ecBlocks = [new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 293 118)), new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 294 46)), new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 295 25)), new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17)),]; 296 break; 297 case 25: 298 patterns = [6, 32, 58, 84, 110]; 299 ecBlocks = [new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 300 107)), new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 301 48)), new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 302 25)), new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16)),]; 303 break; 304 case 26: 305 patterns = [6, 30, 58, 86, 114]; 306 ecBlocks = [new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 307 115)), new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 308 47)), new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 309 23)), new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17)),]; 310 break; 311 case 27: 312 patterns = [6, 34, 62, 90, 118]; 313 ecBlocks = [new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 314 123)), new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 315 46)), new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 316 24)), new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16)),]; 317 break; 318 case 28: 319 patterns = [6, 26, 50, 74, 98, 122]; 320 ecBlocks = [new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 321 118)), new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 322 46)), new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 323 25)), new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16)),]; 324 break; 325 case 29: 326 patterns = [6, 30, 54, 78, 102, 126]; 327 ecBlocks = [new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 328 117)), new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 329 46)), new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 330 24)), new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16)),]; 331 break; 332 case 30: 333 patterns = [6, 26, 52, 78, 104, 130]; 334 ecBlocks = [new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 335 116)), new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 336 48)), new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 337 25)), new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16)),]; 338 break; 339 case 31: 340 patterns = [6, 30, 56, 82, 108, 134]; 341 ecBlocks = [new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 342 116)), new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 343 47)), new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 344 25)), new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16)),]; 345 break; 346 case 32: 347 patterns = [6, 34, 60, 86, 112, 138]; 348 ecBlocks = [new EcBlocks(30, new EcBlock(17, 115)), new EcBlocks(28, 349 new EcBlock(10, 46), new EcBlock(23, 47)), new EcBlocks(30, new EcBlock(10, 350 24), new EcBlock(35, 25)), new EcBlocks(30, new EcBlock(19, 351 15), new EcBlock(35, 16)),]; 352 break; 353 case 33: 354 patterns = [6, 30, 58, 86, 114, 142]; 355 ecBlocks = [new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 356 116)), new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 357 47)), new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 358 25)), new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16)),]; 359 break; 360 case 34: 361 patterns = [6, 34, 62, 90, 118, 146]; 362 ecBlocks = [new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 363 116)), new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 364 47)), new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 365 25)), new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17)),]; 366 break; 367 case 35: 368 patterns = [6, 30, 54, 78, 102, 126, 150]; 369 ecBlocks = [new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 370 122)), new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 371 48)), new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 372 25)), new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16)),]; 373 break; 374 case 36: 375 patterns = [6, 24, 50, 76, 102, 128, 154]; 376 ecBlocks = [new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 377 122)), new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 378 48)), new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 379 25)), new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16)),]; 380 break; 381 case 37: 382 patterns = [6, 28, 54, 80, 106, 132, 158]; 383 ecBlocks = [new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 384 123)), new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 385 47)), new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 386 25)), new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16)),]; 387 break; 388 case 38: 389 patterns = [6, 32, 58, 84, 110, 136, 162]; 390 ecBlocks = [new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 391 123)), new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 392 47)), new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 393 25)), new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16)),]; 394 break; 395 case 39: 396 patterns = [6, 26, 54, 82, 110, 138, 166]; 397 ecBlocks = [new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 398 118)), new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 399 48)), new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 400 25)), new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16)),]; 401 break; 402 case 40: 403 patterns = [6, 30, 58, 86, 114, 142, 170]; 404 ecBlocks = [new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 405 119)), new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 406 48)), new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 407 25)), new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16)),]; 408 break; 409 } 410 auto c = new QrCodeVersion(versionNumber, patterns, ecBlocks); 411 versions[to!string(versionNumber)] = c; 412 return c; 413 } 414 415 /** 416 * Decodes version information from an integer and returns the version. 417 * 418 * @param integer $versionBits 419 * @return Version|null 420 */ 421 public static QrCodeVersion decodeVersionInformation(int versionBits) 422 { 423 auto bestDifference = int.max; 424 auto bestVersion = 0; 425 foreach (int i, targetVersion; versionDecodeInfo) 426 { 427 if (targetVersion == versionBits) 428 { 429 return getVersionForNumber(i + 7); 430 } 431 import ithox.qrcode.common.formatinformation; 432 433 auto bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); 434 if (bitsDifference < bestDifference) 435 { 436 bestVersion = i + 7; 437 bestDifference = bitsDifference; 438 } 439 } 440 if (bestDifference <= 3) 441 { 442 return getVersionForNumber(bestVersion); 443 } 444 return null; 445 } 446 447 /** 448 * Builds the function pattern for the current version. 449 * 450 * @return BitMatrix 451 */ 452 public BitMatrix buildFunctionPattern() 453 { 454 auto dimension = this.DimensionForVersion; 455 auto bitMatrix = new BitMatrix(dimension); 456 // Top left finder pattern + separator + format 457 bitMatrix.setRegion(0, 0, 9, 9); 458 // Top right finder pattern + separator + format 459 bitMatrix.setRegion(dimension - 8, 0, 8, 9); 460 // Bottom left finder pattern + separator + format 461 bitMatrix.setRegion(0, dimension - 8, 9, 8); 462 // Alignment patterns 463 auto max = this.alignmentPatternCenters.length; 464 for (auto x = 0; x < max; x++) 465 { 466 auto i = this.alignmentPatternCenters[x] - 2; 467 for (auto y = 0; y < max; y++) 468 { 469 if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) 470 { 471 // No alignment patterns near the three finder paterns 472 continue; 473 } 474 bitMatrix.setRegion(this.alignmentPatternCenters[y] - 2, i, 5, 5); 475 } 476 } 477 // Vertical timing pattern 478 bitMatrix.setRegion(6, 9, 1, dimension - 17); 479 // Horizontal timing pattern 480 bitMatrix.setRegion(9, 6, dimension - 17, 1); 481 if (this.versionNumber > 6) 482 { 483 // Version info, top right 484 bitMatrix.setRegion(dimension - 11, 0, 3, 6); 485 // Version info, bottom left 486 bitMatrix.setRegion(0, dimension - 11, 6, 3); 487 } 488 return bitMatrix; 489 } 490 491 /** 492 * Returns a string representation for the version. 493 * 494 * @return string 495 */ 496 public override string toString() 497 { 498 import std.conv; 499 500 return to!string(this.versionNumber); 501 } 502 }