1 // Written in the D programming language. 2 /* 3 NYSL Version 0.9982 4 5 A. This software is "Everyone'sWare". It means: 6 Anybody who has this software can use it as if he/she is 7 the author. 8 9 A-1. Freeware. No fee is required. 10 A-2. You can freely redistribute this software. 11 A-3. You can freely modify this software. And the source 12 may be used in any software with no limitation. 13 A-4. When you release a modified version to public, you 14 must publish it with your name. 15 16 B. The author is not responsible for any kind of damages or loss 17 while using or misusing this software, which is distributed 18 "AS IS". No warranty of any kind is expressed or implied. 19 You use AT YOUR OWN RISK. 20 21 C. Copyrighted to Kazuki KOMATSU 22 23 D. Above three clauses are applied both to source and binary 24 form of this software. 25 */ 26 27 /** 28 このモジュールは、式テンプレートベースの試験的な行列演算モジュールです。 29 */ 30 module carbon.linear; 31 32 import std.algorithm, 33 std.array, 34 std.ascii, 35 std.conv, 36 std.exception, 37 std.format, 38 std.functional, 39 std.math, 40 std.range, 41 std.traits, 42 std.typecons, 43 std.string, 44 std.typetuple; 45 46 version(unittest) import std.stdio; 47 48 49 /** 50 行列の格納方式 51 */ 52 enum Major 53 { 54 row, 55 column, 56 } 57 58 /** 59 デフォルトでの行列の格納方式は「行優先」です。 60 */ 61 enum defaultMajor = Major.row; 62 63 alias Msize_t = ptrdiff_t; 64 65 enum Msize_t wild = -2, 66 dynamic = -1; 67 68 69 /** 70 行列型かどうかを判定します 71 */ 72 enum isMatrix(T) = 73 is(typeof((const T m) 74 { 75 size_t rsize = m.rows; 76 size_t csize = m.cols; 77 78 size_t i = 0; 79 80 auto e = m.at(i, i); 81 })); 82 83 84 /// 85 enum isNarrowMatrix(T) = 86 is(typeof((const T m) 87 { 88 size_t rsize = m.rows; 89 size_t csize = m.cols; 90 91 auto e1 = m[0, 0]; 92 auto e2 = m.at(0, 0); 93 94 static assert(is(typeof(e2) : typeof(e1))); 95 static assert(is(typeof(e1) : typeof(e2))); 96 })); 97 98 99 100 /// 101 unittest{ 102 static struct S(size_t R, size_t C) 103 { 104 enum rows = R; 105 enum cols = C; 106 107 auto opIndex(size_t i, size_t j) const 108 { 109 return i + j; 110 } 111 112 alias at = opIndex; 113 } 114 115 static assert(isNarrowMatrix!(S!(0, 0))); 116 static assert(isMatrix!(S!(1, 1))); 117 } 118 119 120 /** 121 大きさが推論される行列かどうか判定します 122 */ 123 enum bool isAbstractMatrix(T) = !isMatrix!T && 124 is(typeof((T t) 125 { 126 auto e = t[0, 0]; 127 auto f = t.at(0, 0); 128 static assert(!is(CommonType!(typeof(e), typeof(f)) == void)); 129 130 131 enum InferredResult result = T.inferSize(wild, wild); 132 133 static if(result.isValid) 134 { 135 enum inferredRows = result.rows; 136 enum inferredCols = result.cols; 137 } 138 139 enum someValue = 4; //非負の整数 140 static assert(!T.inferSize(wild, wild).isValid); 141 static assert(T.inferSize(wild, someValue).isValid); 142 static assert(T.inferSize(wild, someValue).isValid); 143 })); 144 145 146 /** 147 ベクトル型かどうか判定します 148 */ 149 enum bool isVector(T) = 150 is(typeof((const T v) 151 { 152 size_t size = v.length; 153 154 static if(isMatrix!T) 155 { 156 assert(min(v.rows, v.cols) == 1); 157 assert(max(v.rows, v.cols) == size); 158 } 159 160 auto e = v.at(size-1); 161 162 static if(isMatrix!T) 163 { 164 static assert(is(typeof(e) : typeof(v.at(size-1)))); 165 static assert(is(typeof(v.at(size-1)) : typeof(e))); 166 } 167 })); 168 169 170 /** 171 ベクトル型かどうか判定します 172 */ 173 enum isNarrowVector(V) = isMatrix!V && isVector!V && !isAbstractMatrix!V && 174 is(typeof((const V v) 175 { 176 static if(hasStaticRows!V && hasStaticCols!V) 177 static assert(staticRows!V == 1 || staticCols!V == 1); 178 else static if(hasStaticRows!V) 179 static assert(staticRows!V == 1); 180 else static if(hasStaticCols!V) 181 static assert(staticCols!V == 1); 182 else 183 static assert(0); 184 185 auto e = v[0]; 186 static assert(is(typeof(e) : typeof(v.at(0, 0)))); 187 static assert(is(typeof(v.at(0, 0)) : typeof(e))); 188 })); 189 190 191 unittest{ 192 static struct V 193 { 194 enum rows = 1; 195 enum cols = 3; 196 enum length = 3; 197 198 auto opIndex(size_t i, size_t j) const { return j; } 199 auto opIndex(size_t j) const { return j; } 200 201 alias at = opIndex; 202 } 203 204 static assert(isMatrix!V); 205 static assert(isNarrowMatrix!V); 206 static assert(isVector!V); 207 static assert(!isAbstractMatrix!V); 208 static assert(isNarrowVector!V); 209 210 static assert(isVector!(int[])); 211 static assert(isVector!(int[][])); 212 static assert(isVector!(int[][1])); 213 static assert(isVector!(int[1][])); 214 } 215 216 217 /// 218 unittest{ 219 import std.bigint, std.typetuple; 220 alias TT = TypeTuple!(ubyte, ushort, uint, ulong, 221 byte, short, int, long, 222 float, double, real, 223 creal, cfloat, cdouble/*,*/ 224 /*BigInt*/); 225 226 static struct M(T, size_t r, size_t c) 227 { 228 enum rows = r; 229 enum cols = c; 230 231 auto opIndex(size_t i, size_t j) const { return T.init; } 232 alias at = opIndex; 233 } 234 235 foreach(T; TT) 236 { 237 static assert(isNarrowMatrix!(M!(T, 3, 3))); 238 static assert(isNarrowMatrix!(M!(const(T), 3, 3))); 239 static assert(isNarrowMatrix!(M!(immutable(T), 3, 3))); 240 static assert(isMatrix!(M!(T, 3, 3))); 241 static assert(isMatrix!(M!(const(T), 3, 3))); 242 static assert(isMatrix!(M!(immutable(T), 3, 3))); 243 } 244 } 245 246 247 /** 248 行列型でも、ベクトル型でもないような型 249 */ 250 enum bool isNotVectorOrMatrix(T) = 251 !( isMatrix!T 252 || isAbstractMatrix!T 253 || isVector!T 254 ); 255 256 /// 257 unittest{ 258 import std.bigint, std.numeric, std.typetuple; 259 260 alias TT = TypeTuple!(ubyte, ushort, uint, ulong, 261 byte, short, int, long, 262 float, double, real, 263 creal, cfloat, cdouble); 264 265 foreach(T; TT) 266 { 267 static assert(isNotVectorOrMatrix!(T)); 268 static assert(isNotVectorOrMatrix!(const(T))); 269 static assert(isNotVectorOrMatrix!(immutable(T))); 270 } 271 272 static assert(isNotVectorOrMatrix!BigInt); 273 // static assert(isNotVectorOrMatrix!(CustomFloat!16)); In dmd2.064, isNotVectorOrMatrix!(CustomFloat!16) is true. 274 } 275 276 277 /** 278 行列やベクトルのビューを取得します。 279 */ 280 enum bool hasView(T) = is(typeof((T m){ 281 auto v = m.view; 282 283 static if(isMatrix!T) 284 static assert(isMatrix!(typeof(v))); 285 else static if(isAbstractMatrix!T) 286 static assert(isAbstractMatrix!(typeof(v))); 287 else static if(isVector!T) 288 static assert(isVector!T); 289 })); 290 291 292 293 auto ref at(R)(auto ref R r, size_t i, size_t j) 294 if(!isNarrowMatrix!R && is(typeof(r[i][j]))) 295 { 296 static if(defaultMajor == Major.row) 297 return r[i][j]; 298 else 299 return r[j][i]; 300 } 301 302 303 void assignAt(R, E)(auto ref R r, size_t i, size_t j, auto ref E e) 304 if(!isNarrowMatrix!R && is(typeof({ r[i][j] = forward!e; }))) 305 { 306 static if(defaultMajor == Major.row) 307 r[i][j] = forward!e; 308 else 309 r[j][i] = forward!e; 310 } 311 312 313 auto ref at(R)(auto ref R r, size_t i, size_t j) 314 if(!isNarrowMatrix!R && is(typeof(r[i])) && !is(typeof(r[i][j]))) 315 in{ 316 static if(defaultMajor == Major.row) 317 assert(j == 0); 318 else 319 assert(i == 0); 320 } 321 body{ 322 return r[i+j]; 323 } 324 325 326 void assignAt(R, E)(auto ref R r, size_t i, size_t j, auto ref E e) 327 if(!isNarrowMatrix!R && is(typeof({ r[i] = forward!e; })) && !is(typeof(r[i][j]))) 328 in{ 329 static if(defaultMajor == Major.row) 330 assert(j == 0); 331 else 332 assert(i == 0); 333 } 334 body{ 335 r[i+j] = e; 336 } 337 338 339 auto ref at(M)(auto ref M m, size_t i) 340 if(isMatrix!M && is(typeof(m.length))) 341 { 342 static if(hasStaticRows!M) 343 static if(staticRows!M == 1) 344 { 345 enum ReturnA = true; 346 return m.at(0, i); 347 } 348 349 static if(!is(typeof({static assert(ReturnA);}))) 350 { 351 static if(hasStaticCols!M) 352 static if(staticCols!M == 1) 353 { 354 enum ReturnB = true; 355 return m.at(i, 0); 356 } 357 358 static if(!is(typeof({static assert(ReturnB);}))) 359 { 360 if(m.rows == 1) 361 return m.at(0, i); 362 else if(m.cols == 1) 363 return m.at(i, 0); 364 365 assert(0); 366 } 367 } 368 } 369 370 371 void assignAt(M, E)(auto ref M m, size_t i, auto ref E e) 372 if(isMatrix!M && is(typeof(m.length)) && is(typeof(m.assignAt(0, 0, forward!e)))) 373 { 374 static if(hasStaticRows!M) 375 static if(staticRows!M == 1) 376 return m.assignAt(0, i, forward!e); 377 378 static if(hasStaticCols!M) 379 static if(staticCols!M == 1) 380 return m.assignAt(i, 0, forward!e); 381 382 if(m.rows == 1) 383 return m.assignAt(0, i, forward!e); 384 else if(m.cols == 1) 385 return m.assignAt(i, 0, forward!e); 386 387 assert(0); 388 } 389 390 391 size_t rows(R)(auto ref R r) @property 392 if(!isNarrowMatrix!R && is(typeof(r[0][0])) && hasLength!R && hasLength!(typeof(r[0]))) 393 { 394 static if(defaultMajor == Major.row) 395 return r.length; 396 else 397 { 398 if(r.length) 399 return r[0].length; 400 else 401 return 0; 402 } 403 } 404 405 406 size_t rows(R)(auto ref R r) @property 407 if(!isNarrowMatrix!R && is(typeof(r[0])) && !is(typeof(r[0][0])) && hasLength!R) 408 { 409 static if(defaultMajor == Major.row) 410 return r.length; 411 else 412 return 1; 413 } 414 415 416 size_t cols(R)(auto ref R r) @property 417 if(is(typeof(r[0][0])) && hasLength!R && hasLength!(typeof(r[0]))) 418 { 419 static if(defaultMajor == Major.column) 420 return r.length; 421 else 422 { 423 if(r.length) 424 return r[0].length; 425 else 426 return 0; 427 } 428 } 429 430 431 size_t cols(R)(auto ref R r) @property 432 if(is(typeof(r[0])) && !is(typeof(r[0][0])) && hasLength!R) 433 { 434 static if(defaultMajor == Major.row) 435 return 1; 436 else 437 return r.length; 438 } 439 440 441 size_t staticRows(T)() @property 442 if(isNarrowMatrix!T) 443 { 444 static if(is(typeof(T.staticRows))) 445 return T.staticRows; 446 else static if(is(typeof({ enum srows = T.rows; }))) 447 return T.rows; 448 else 449 static assert(0); 450 } 451 452 453 size_t staticRows(R)() @property 454 if(!isNarrowMatrix!R && is(typeof(R.init[0][0])) && hasLength!R && hasLength!(std.range.ElementType!R)) 455 { 456 static if((defaultMajor == Major.row) && is(typeof({ enum srows = R.length; }))) 457 return R.length; 458 else static if((defaultMajor == Major.column) && is(typeof({ enum srows = std.range.ElementType!R.length; }))) 459 return std.range.ElementType!R.length; 460 else 461 static assert(0); 462 } 463 464 465 size_t staticRows(R)() @property 466 if(!isNarrowMatrix!R && is(typeof(R.init[0])) && !is(typeof(R.init[0][0])) && hasLength!R) 467 { 468 static if((defaultMajor == Major.row) && is(typeof({ enum srows = R.length; }))) 469 return R.length; 470 else static if(defaultMajor == Major.column) 471 return 1; 472 else 473 static assert(0); 474 } 475 476 477 size_t staticCols(T)() @property 478 if(isNarrowMatrix!T) 479 { 480 static if(is(typeof(T.staticCols))) 481 return T.staticCols; 482 else static if(is(typeof({ enum srows = T.cols; }))) 483 return T.cols; 484 else 485 static assert(0); 486 } 487 488 489 size_t staticCols(R)() @property 490 if(!isNarrowMatrix!R && is(typeof(R.init[0][0])) && hasLength!R && hasLength!(std.range.ElementType!R)) 491 { 492 static if((defaultMajor == Major.column) && is(typeof({ enum scols = R.length; }))) 493 return R.length; 494 else static if((defaultMajor == Major.row) && is(typeof({ enum scols = std.range.ElementType!R.length; }))) 495 return std.range.ElementType!R.length; 496 else 497 static assert(0); 498 } 499 500 501 size_t staticCols(R)() @property 502 if(!isNarrowMatrix!R && is(typeof(R.init[0])) && !is(typeof(R.init[0][0])) && hasLength!R) 503 { 504 static if((defaultMajor == Major.column) && is(typeof({ enum scols = R.length; }))) 505 return R.length; 506 else static if(defaultMajor == Major.row) 507 return 1; 508 else 509 static assert(0); 510 } 511 512 513 unittest{ 514 int[][] arr2d = [ 515 [ 1, 2, 3], 516 [ 4, 5, 6], 517 [ 7, 8, 9], 518 [10, 11, 12] 519 ]; 520 521 assert(arr2d.rows == 4); 522 assert(arr2d.cols == 3); 523 524 foreach(i; 0 .. arr2d.rows) 525 foreach(j; 0 .. arr2d.cols) 526 assert(arr2d.at(i, j) == arr2d[i][j]); 527 528 static assert(isMatrix!(int[][])); 529 530 531 int[] arr1d = [1, 2, 3, 4]; // 列ベクトル 532 533 assert(arr1d.rows == 4); 534 assert(arr1d.cols == 1); 535 static assert(staticCols!(int[])() == 1); 536 537 foreach(i; 0 .. arr1d.rows) 538 foreach(j; 0 .. arr1d.cols) 539 assert(arr1d.at(i, j) == arr1d[i] && arr1d.at(i) == arr1d[i]); 540 541 arr2d = [arr1d]; // 行ベクトル 542 assert(arr2d.rows == 1); 543 assert(arr2d.cols == 4); 544 545 foreach(i; 0 .. arr2d.rows) 546 foreach(j; 0 .. arr2d.cols) 547 assert(arr2d.at(i, j) == arr2d[i][j] && arr2d.at(j) == arr2d[i][j]); 548 549 static assert(staticRows!(int[4][3]) == 3); 550 static assert(staticCols!(int[4][3]) == 4); 551 static assert(staticRows!(int[][4]) == 4); 552 static assert(staticCols!(int[4][]) == 4); 553 static assert(staticRows!(int[4]) == 4); 554 555 static struct M(T, size_t r, size_t c) 556 { 557 enum rows = r; 558 enum cols = c; 559 560 auto opIndex(size_t i, size_t j) const {return T.init;} 561 alias at = opIndex; 562 } 563 564 565 static assert(staticRows!(M!(int, 4, 3)) == 4); 566 static assert(staticCols!(M!(int, 4, 3)) == 3); 567 } 568 569 570 unittest{ 571 static assert(isMatrix!(int[])); 572 static assert(!isNarrowMatrix!(int[])); 573 static assert(isVector!(int[])); 574 static assert(isNarrowVector!(int[])); 575 576 static assert(isMatrix!(int[][])); 577 static assert(!isNarrowMatrix!(int[][])); 578 static assert(isVector!(int[][])); 579 static assert(!isNarrowVector!(int[][])); 580 581 static assert(isMatrix!(int[][][])); 582 static assert(!isNarrowMatrix!(int[][][])); 583 static assert(isVector!(int[][][])); 584 static assert(!isNarrowVector!(int[][][])); 585 586 587 static struct M0 588 { 589 enum rows = 3; 590 //enum cols = 3; 591 592 auto at(size_t i, size_t j) const 593 { 594 return i; 595 } 596 } 597 598 static assert(!isMatrix!(M0)); 599 static assert(!isNarrowMatrix!(M0)); 600 static assert(!isVector!(M0)); 601 static assert(!isNarrowVector!(M0)); 602 603 604 static struct M1 605 { 606 enum rows = 3; 607 enum cols = 3; 608 609 auto at(size_t i, size_t j) const 610 { 611 return i; 612 } 613 } 614 615 static assert(isMatrix!(M1)); 616 static assert(!isNarrowMatrix!(M1)); 617 static assert(!isVector!(M1)); 618 static assert(!isNarrowVector!(M1)); 619 620 621 static struct M2 622 { 623 enum rows = 1; 624 enum cols = 3; 625 enum length = rows; 626 627 auto at(size_t i, size_t j) const 628 { 629 return i; 630 } 631 632 auto at(size_t j) const 633 { 634 return at(0, j); 635 } 636 } 637 638 static assert(isMatrix!(M2)); 639 static assert(!isNarrowMatrix!(M2)); 640 static assert(isVector!(M2)); 641 static assert(!isNarrowVector!(M2)); 642 } 643 644 645 /** 646 行の大きさが静的な行列型かどうか判定します 647 */ 648 enum hasStaticRows(T) = is(typeof(staticRows!T())); 649 650 unittest{ 651 int[4] arr4; 652 static assert(hasStaticRows!(int[4])); 653 } 654 655 656 /** 657 列の大きさが静的な行列型かどうか判定します 658 */ 659 enum hasStaticCols(T) = is(typeof(staticCols!T())); 660 661 unittest{ 662 int[4][] arr4x; 663 static assert(hasStaticCols!(typeof(arr4x))); 664 } 665 666 667 /** 668 大きさが静的なベクトル型かどうか判定します 669 */ 670 enum hasStaticLength(T) = is(typeof({ enum len = T.length; })); 671 672 unittest{ 673 static assert(hasStaticLength!(int[4])); 674 } 675 676 677 /** 678 行の大きさが動的な行列型かどうか判定します 679 */ 680 enum hasDynamicRows(T) = !hasStaticRows!T && is(typeof({ size_t rs = T.init.rows; })); 681 682 683 /** 684 列の大きさが動的な行列型かどうか判定します 685 */ 686 enum hasDynamicColumns(T) = !hasStaticCols!T && is(typeof({ size_t cs = T.init.cols; })); 687 688 689 /** 690 大きさが動的なベクトル型かどうか判定します 691 */ 692 enum hasDynamicLength(T) = !hasStaticLength!T && is(typeof({ size_t len = T.init.length; })); 693 694 695 /** 696 大きさが推論される行列が、推論結果を返す際の結果の型です。 697 */ 698 struct InferredResult 699 { 700 bool isValid; 701 size_t rows; 702 size_t cols; 703 } 704 705 /+ 706 version(none): 707 708 709 /** 710 Matrix -> NarrowMatrix 711 */ 712 auto toNarrowMatrix(Major major = defaultMajor, M)(M m) 713 if(isMatrix!M && !isNarrowMatrix!M) 714 { 715 static struct Result() 716 { 717 static if(isVector!M) 718 { 719 auto ref opIndex(size_t i) inout 720 { 721 return _m.at(i); 722 } 723 724 725 auto ref length() inout 726 { 727 return _m.length; 728 } 729 } 730 731 auto ref opIndex(size_t i, size_t j) inout 732 { 733 return _m.at(i, j); 734 } 735 736 737 alias at = opIndex; 738 739 740 static if(hasStaticRows!M) 741 { 742 enum rows = staticRows!M; 743 } 744 else 745 { 746 auto ref rows() const @property 747 { 748 return _m.rows; 749 } 750 } 751 752 753 static if(hasStaticCols!M) 754 { 755 enum cols = staticCols!M; 756 } 757 else 758 { 759 auto ref cols() const @property 760 { 761 return _m.cols; 762 } 763 } 764 765 766 private: 767 M _m; 768 } 769 770 return Result!()(m); 771 } 772 773 unittest { 774 auto nm = [1, 2, 3, 4].toNarrowMatrix(); 775 static assert(isNarrowMatrix!(typeof(nm))); 776 static assert(isNarrowVector!(typeof(nm))); 777 } 778 779 780 781 version(none): 782 783 /** 784 ベクトルを、行列ベクトル型へ変換します 785 */ 786 auto toNarrowVector(Major major = defaultMajor, V)(V v) 787 if(isVector!V && !isNarrowVector!V) 788 { 789 static struct Result() 790 { 791 static if(major == Major.row) 792 { 793 enum rows = 1; 794 795 static if(hasStaticLength!V) 796 enum cols = V.length; 797 else 798 @property size_t cols() const { return _v.length; } 799 } 800 else 801 { 802 enum cols = 1; 803 804 static if(hasStaticLength!V) 805 enum rows = V.length; 806 else 807 @property size_t rows() const { return _v.length; } 808 } 809 810 @property auto ref length() const { return _v.length; } 811 alias opDollar = length; 812 813 auto ref opIndex(size_t i) inout 814 in{ 815 assert(i < length); 816 } 817 body{ 818 return _v[i]; 819 } 820 821 822 auto ref opIndex(size_t i, size_t j) inout 823 in{ 824 assert(i < rows); 825 assert(j < cols); 826 } 827 body{ 828 return _v[i+j]; 829 } 830 831 832 mixin(defaultExprOps!(true)); 833 834 private: 835 V _v; 836 } 837 838 839 return Result!()(v); 840 } 841 +/ 842 843 /** 844 行列型の要素の型を取得します 845 */ 846 template ElementType(A) 847 if(isMatrix!A || isAbstractMatrix!A) 848 { 849 alias typeof(A.init.at(0, 0)) ElementType; 850 } 851 852 /// 853 unittest{ 854 static struct S 855 { 856 enum rows = 1; 857 enum cols = 1; 858 859 int opIndex(size_t, size_t) const 860 { 861 return 1; 862 } 863 864 alias at = opIndex; 865 } 866 867 static assert(is(ElementType!S == int)); 868 } 869 870 871 /** 872 その行列の要素がstd.algorithm.swapを呼べるかどうかをチェックします 873 */ 874 template hasLvalueElements(A) 875 if(isMatrix!A || isAbstractMatrix!A) 876 { 877 enum hasLvalueElements = is(typeof((A a){ 878 import std.algorithm : swap; 879 880 swap(a.at(0, 0), a.at(0, 0)); 881 })); 882 } 883 884 /// 885 unittest{ 886 static struct M 887 { 888 enum rows = 1; 889 enum cols = 1; 890 891 ref int opIndex(size_t i, size_t j) inout 892 { 893 static int a; 894 return a; 895 } 896 897 alias at = opIndex; 898 } 899 900 static assert(hasLvalueElements!(M)); 901 } 902 903 904 //version(none): 905 906 907 /** 908 A型の行列の要素にElementType!A型の値が代入可能かどうかをチェックします。 909 */ 910 template hasAssignableElements(A) 911 if(isMatrix!A || isAbstractMatrix!A) 912 { 913 enum hasAssignableElements = is(typeof((A a){ 914 ElementType!A e; 915 a.assignAt(0, 0, e); 916 })); 917 } 918 unittest{ 919 static struct M 920 { 921 enum rows = 1; 922 enum cols = 1; 923 924 auto opIndex(size_t i, size_t j) const 925 { 926 return i; 927 } 928 929 void opIndexAssign(typeof(this[0, 0]) a, size_t i, size_t j){} 930 931 auto at(size_t i, size_t j) const 932 { 933 return this[i, j]; 934 } 935 936 void assignAt(size_t i, size_t j, typeof(this[0, 0]) e){} 937 } 938 939 static assert(hasAssignableElements!(M)); 940 } 941 942 943 /** 944 aとbが等しいか、もしくはどちらかが0であるとtrueとなります。 945 2つの行列が演算可能かどうかを判定するのに使います。 946 */ 947 private bool isEqOrEitherEqX(alias pred = "a == b", T, X = T)(T a, T b, X x = 0) 948 { 949 return binaryFun!pred(a, b) || a == x || b == x; 950 } 951 952 /// 953 unittest{ 954 static assert(isEqOrEitherEqX(0, 0)); 955 static assert(isEqOrEitherEqX(1, 1)); 956 static assert(isEqOrEitherEqX(0, 1)); 957 static assert(isEqOrEitherEqX(1, 0)); 958 } 959 960 961 /** 962 ポインタを管理します 963 */ 964 struct PMatrix(M) 965 if(isMatrix!M) 966 { 967 this(ref M m) 968 { 969 ptr = &m; 970 } 971 972 973 this(M* m) 974 { 975 ptr = m; 976 } 977 978 979 auto ref opIndex(size_t r, size_t c) inout 980 { 981 return (*ptr)[r, c]; 982 } 983 984 985 static if(is(typeof({ enum r = M.rows; }))) 986 enum rows = M.rows; 987 else 988 { 989 auto ref rows() inout @property { return ptr.rows; } 990 991 static if(is(typeof({ ptr.rows = ptr.rows; }))) 992 void rows(typeof(ptr.rows) r) { ptr.rows = r; } 993 } 994 995 996 static if(is(typeof({ enum r = M.cols; }))) 997 enum cols = M.cols; 998 else 999 { 1000 auto ref cols() inout @property { return ptr.cols; } 1001 1002 static if(is(typeof({ ptr.cols = ptr.cols; }))) 1003 void cols(typeof(ptr.cols) r) { ptr.cols = r; } 1004 } 1005 1006 1007 M* ptr; 1008 ref inout(typeof(this)) pref() inout pure nothrow @safe @nogc { return this; } 1009 1010 1011 void opAssign(X)(auto ref X m) 1012 if(is(typeof(*ptr = m))) 1013 { 1014 *ptr = m; 1015 } 1016 1017 1018 mixin(ExpressionOperators!(ETOSpec.all & ~ETOSpec.opAssign, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 1019 } 1020 1021 1022 auto pref(NMatrix)(ref NMatrix nm) @property @trusted 1023 if(isMatrix!NMatrix || isAbstractMatrix!NMatrix) 1024 { 1025 return PMatrix!NMatrix(nm); 1026 } 1027 1028 unittest 1029 { 1030 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 1031 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 1032 1033 1034 SMatrix!(int, 2, 2) m; 1035 m = [[0, 1], 1036 [2, 3]]; 1037 1038 auto t = m.pref; 1039 assert(t[0, 0] == 0); 1040 assert(t[0, 1] == 1); 1041 assert(t[1, 0] == 2); 1042 assert(t[1, 1] == 3); 1043 } 1044 1045 1046 1047 /** 1048 正しい演算かどうか判定します 1049 1050 Example: 1051 ---- 1052 static struct S(T, size_t r, size_t c){enum rows = r; enum cols = c; T opIndex(size_t i, size_t j) const {return T.init;}} 1053 alias Static1x1 = S!(int, 1, 1); 1054 alias Static1x2 = S!(int, 1, 2); 1055 alias Static2x1 = S!(int, 2, 1); 1056 alias Static2x2 = S!(int, 2, 2); 1057 1058 static struct D(T){size_t rows = 1, cols = 1; T opIndex(size_t i, size_t j) const {return T.init;}} 1059 alias Dynamic = D!(int); 1060 1061 static struct I(T){ 1062 enum rows = 0, cols = 0; 1063 T opIndex(size_t i, size_t j) const { return T.init; } 1064 static InferredResult inferSize(size_t rs, size_t cs){ 1065 if(rs == 0 && cs == 0) 1066 return InferredResult(false, 0, 0); 1067 else if(rs == 0 || cs == 0) 1068 return InferredResult(true, max(rs, cs), max(rs, cs)); 1069 else 1070 return InferredResult(true, rs, cs); 1071 } 1072 } 1073 alias Inferable = I!int; 1074 static assert(Inferable.inferSize(1, 0).isValid); 1075 1076 alias T = Inferable; 1077 static assert(T.rows == wild); 1078 static assert(T.cols == wild); 1079 1080 1081 static assert( isValidOperator!(Static1x1, "+", Static1x1)); 1082 static assert(!isValidOperator!(Static1x1, "+", Static1x2)); 1083 static assert( isValidOperator!(Static1x2, "+", Static1x2)); 1084 static assert(!isValidOperator!(Static1x2, "+", Static1x1)); 1085 1086 static assert( isValidOperator!(Static1x1, "+", Dynamic)); 1087 static assert( isValidOperator!(Static1x2, "+", Dynamic)); 1088 static assert( isValidOperator!(Dynamic, "+", Static1x1)); 1089 static assert( isValidOperator!(Dynamic, "+", Static1x2)); 1090 1091 static assert( isValidOperator!(Static1x1, "+", Inferable)); 1092 static assert( isValidOperator!(Static1x2, "+", Inferable)); 1093 static assert( isValidOperator!(Inferable, "+", Static1x1)); 1094 static assert( isValidOperator!(Inferable, "+", Static1x2)); 1095 1096 static assert( isValidOperator!(Static1x1, "*", Static1x1)); 1097 static assert( isValidOperator!(Static1x1, "*", Static1x2)); 1098 static assert(!isValidOperator!(Static1x2, "*", Static1x2)); 1099 static assert(!isValidOperator!(Static1x2, "*", Static1x1)); 1100 1101 static assert( isValidOperator!(Static1x1, "*", Dynamic)); 1102 static assert( isValidOperator!(Static1x2, "*", Dynamic)); 1103 static assert( isValidOperator!(Dynamic, "*", Static1x1)); 1104 static assert( isValidOperator!(Dynamic, "*", Static1x2)); 1105 1106 static assert( isValidOperator!(Static1x1, "*", Inferable)); 1107 static assert( isValidOperator!(Static1x2, "*", Inferable)); 1108 static assert( isValidOperator!(Inferable, "*", Static1x1)); 1109 static assert( isValidOperator!(Inferable, "*", Static1x2)); 1110 ---- 1111 */ 1112 template isValidOperator(L, string op, R) 1113 { 1114 static if(op != "=") 1115 { 1116 static if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R)) 1117 enum isValidOperator = is(typeof(mixin("typeof(L.init.at(0, 0)).init " ~ op ~ " typeof(R.init.at(0, 0)).init"))) && isValidOperatorImpl!(L, op, R); 1118 else static if(isMatrix!L || isAbstractMatrix!L) 1119 enum isValidOperator = is(typeof(mixin("typeof(L.init.at(0, 0)).init " ~ op ~ " R.init"))) && isValidOperatorImpl!(L, op, R); 1120 else static if(isMatrix!R || isAbstractMatrix!R) 1121 enum isValidOperator = is(typeof(mixin("L.init " ~ op ~ " typeof(R.init.at(0, 0)).init"))) && isValidOperatorImpl!(L, op, R); 1122 else 1123 static assert(0); 1124 } 1125 else 1126 { 1127 static if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R)){ 1128 enum isValidOperator = isAssignable!(typeof(L.init.at(0, 0)), typeof(R.init.at(0, 0))) && isValidOperatorImpl!(L, "+", R); 1129 }else static if(isMatrix!L || isAbstractMatrix!L) 1130 enum isValidOperator = isAssignable!(typeof(L.init.at(0,0)), R) && isValidOperatorImpl!(L, "+", R); 1131 else static if(isMatrix!R || isAbstractMatrix!R) 1132 enum isValidOperator = isAssignable!(L, typeof(R.init.at(0, 0))) && isValidOperatorImpl!(L, "+", R); 1133 else 1134 static assert(0); 1135 } 1136 } 1137 1138 1139 template isValidOperatorImpl(L, string op, R) 1140 if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R) && op != "*") 1141 { 1142 struct Inferred(M, size_t r, size_t c) 1143 { 1144 enum size_t rows = M.inferSize(r, c).rows; 1145 enum size_t cols = M.inferSize(r, c).cols; 1146 1147 auto opIndex(size_t i, size_t j) const { return ElementType!M.init; } 1148 } 1149 1150 static if(op != "+" && op != "-") 1151 enum isValidOperatorImpl = false; 1152 else static if(isAbstractMatrix!L && isAbstractMatrix!R) 1153 enum isValidOperatorImpl = true; 1154 else static if(isAbstractMatrix!L) 1155 { 1156 static if(hasStaticRows!R && hasStaticCols!R) 1157 enum isValidOperatorImpl = L.inferSize(staticRows!R, staticCols!R).isValid && isValidOperatorImpl!(Inferred!(L, staticRows!R, staticCols!R), op, R); 1158 else static if(hasStaticRows!R) 1159 enum isValidOperatorImpl = L.inferSize(staticRows!R, wild).isValid && isValidOperatorImpl!(Inferred!(L, staticRows!R, wild), op, R); 1160 else static if(hasStaticCols!R) 1161 enum isValidOperatorImpl = L.inferSize(wild, staticCols!R).isValid && isValidOperatorImpl!(Inferred!(L, wild, staticCols!R), op, R); 1162 else 1163 enum isValidOperatorImpl = true; 1164 } 1165 else static if(isAbstractMatrix!R) 1166 enum isValidOperatorImpl = isValidOperatorImpl!(R, op, L); 1167 else 1168 { 1169 static if(hasStaticRows!L) 1170 { 1171 static if(hasStaticRows!R) 1172 enum _isValidR = staticRows!L == staticRows!R; 1173 else 1174 enum _isValidR = true; 1175 } 1176 else 1177 enum _isValidR = true; 1178 1179 static if(hasStaticCols!L) 1180 { 1181 static if(hasStaticCols!R) 1182 enum _isValidC = staticCols!L == staticCols!R; 1183 else 1184 enum _isValidC = true; 1185 } 1186 else 1187 enum _isValidC = true; 1188 1189 enum isValidOperatorImpl = _isValidR && _isValidC; 1190 } 1191 } 1192 1193 1194 template isValidOperatorImpl(L, string op, R) 1195 if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R) && op == "*") 1196 { 1197 struct Inferred(M, size_t r, size_t c) 1198 if(r == wild || c == wild) 1199 { 1200 enum size_t rows = M.inferSize(r, c).rows; 1201 enum size_t cols = M.inferSize(r, c).cols; 1202 1203 auto opIndex(size_t i, size_t j) const { return M.init[i, j]; } 1204 } 1205 1206 1207 static if(isAbstractMatrix!L && isAbstractMatrix!R) 1208 enum isValidOperatorImpl = false; 1209 else static if(isAbstractMatrix!L) 1210 { 1211 static if(hasStaticRows!R) 1212 enum isValidOperatorImpl = isValidOperatorImpl!(Inferred!(L, wild, staticRows!R), op, R); 1213 else 1214 enum isValidOperatorImpl = true; 1215 } 1216 else static if(isAbstractMatrix!R) 1217 { 1218 static if(hasStaticCols!L) 1219 enum isValidOperatorImpl = isValidOperatorImpl!(L, op, Inferred!(R, staticCols!L, wild)); 1220 else 1221 enum isValidOperatorImpl = true; 1222 } 1223 else 1224 { 1225 static if(hasStaticCols!L && hasStaticRows!R) 1226 enum isValidOperatorImpl = staticCols!L == staticRows!R; 1227 else 1228 enum isValidOperatorImpl = true; 1229 } 1230 } 1231 1232 1233 template isValidOperatorImpl(L, string op, R) 1234 if(((isMatrix!L || isAbstractMatrix!L) && isNotVectorOrMatrix!R) || ((isMatrix!R || isAbstractMatrix!R) && isNotVectorOrMatrix!L)) 1235 { 1236 static if(op != "+" && op != "-" && op != "*" && op != "/") 1237 enum isValidOperatorImpl = false; 1238 else 1239 enum isValidOperatorImpl = true; 1240 } 1241 1242 1243 unittest{ 1244 static struct S(T, size_t r, size_t c) 1245 { 1246 enum rows = r; 1247 enum cols = c; 1248 T at(size_t i, size_t j) const { return T.init; } 1249 alias opIndex = at; 1250 1251 static assert(isNarrowMatrix!(typeof(this))); 1252 static assert(isMatrix!(typeof(this))); 1253 static assert(hasStaticRows!(typeof(this))); 1254 static assert(hasStaticCols!(typeof(this))); 1255 } 1256 alias Static1x1 = S!(int, 1, 1); 1257 alias Static1x2 = S!(int, 1, 2); 1258 alias Static2x1 = S!(int, 2, 1); 1259 alias Static2x2 = S!(int, 2, 2); 1260 1261 static struct D(T) 1262 { 1263 size_t rows = 1, cols = 1; 1264 T at(size_t i, size_t j) const { return T.init; } 1265 alias opIndex = at; 1266 1267 static assert(isNarrowMatrix!(typeof(this))); 1268 static assert(isMatrix!(typeof(this))); 1269 static assert(!hasStaticRows!(typeof(this))); 1270 static assert(!hasStaticCols!(typeof(this))); 1271 } 1272 alias Dynamic = D!(int); 1273 1274 static struct I(T) 1275 { 1276 T at(size_t i, size_t j) const { return T.init; } 1277 alias opIndex = at; 1278 1279 static InferredResult inferSize(Msize_t rs, Msize_t cs) 1280 { 1281 if(rs == wild && cs == wild) 1282 return InferredResult(false, 0, 0); 1283 else if(rs == wild || cs == wild) 1284 return InferredResult(true, max(rs, cs), max(rs, cs)); 1285 else 1286 return InferredResult(true, rs, cs); 1287 } 1288 } 1289 alias Inferable = I!int; 1290 static assert(Inferable.inferSize(1, wild).isValid); 1291 1292 alias T = Inferable; 1293 static assert(isAbstractMatrix!(I!int)); 1294 1295 1296 static assert( isValidOperator!(Static1x1, "+", Static1x1)); 1297 static assert(!isValidOperator!(Static1x1, "+", Static1x2)); 1298 static assert( isValidOperator!(Static1x2, "+", Static1x2)); 1299 static assert(!isValidOperator!(Static1x2, "+", Static1x1)); 1300 1301 static assert( isValidOperator!(Static1x1, "+", Dynamic)); 1302 static assert( isValidOperator!(Static1x2, "+", Dynamic)); 1303 static assert( isValidOperator!(Dynamic, "+", Static1x1)); 1304 static assert( isValidOperator!(Dynamic, "+", Static1x2)); 1305 1306 static assert( isValidOperator!(Static1x1, "+", Inferable)); 1307 static assert( isValidOperator!(Static1x2, "+", Inferable)); 1308 static assert( isValidOperator!(Inferable, "+", Static1x1)); 1309 static assert( isValidOperator!(Inferable, "+", Static1x2)); 1310 1311 static assert( isValidOperator!(Static1x1, "*", Static1x1)); 1312 static assert( isValidOperator!(Static1x1, "*", Static1x2)); 1313 static assert(!isValidOperator!(Static1x2, "*", Static1x2)); 1314 static assert(!isValidOperator!(Static1x2, "*", Static1x1)); 1315 1316 static assert( isValidOperator!(Static1x1, "*", Dynamic)); 1317 static assert( isValidOperator!(Static1x2, "*", Dynamic)); 1318 static assert( isValidOperator!(Dynamic, "*", Static1x1)); 1319 static assert( isValidOperator!(Dynamic, "*", Static1x2)); 1320 1321 static assert( isValidOperator!(Static1x1, "*", Inferable)); 1322 static assert( isValidOperator!(Static1x2, "*", Inferable)); 1323 static assert( isValidOperator!(Inferable, "*", Static1x1)); 1324 static assert( isValidOperator!(Inferable, "*", Static1x2)); 1325 1326 foreach(i, E1; TypeTuple!(byte, short, int, long)) 1327 { 1328 foreach(E2; TypeTuple!(byte, short, int, long)[0 .. i+1]) 1329 { 1330 static assert( isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 1))); 1331 static assert(!isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 2))); 1332 static assert( isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 2))); 1333 static assert(!isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 1))); 1334 1335 static assert( isValidOperator!(S!(E1, 1, 1), "=", D!(E2))); 1336 static assert( isValidOperator!(S!(E1, 1, 2), "=", D!(E2))); 1337 static assert( isValidOperator!(D!(E1), "=", S!(E2, 1, 1))); 1338 static assert( isValidOperator!(D!(E1), "=", S!(E2, 1, 2))); 1339 1340 static assert( isValidOperator!(S!(E1, 1, 1), "=", I!(E2))); 1341 static assert( isValidOperator!(S!(E1, 1, 2), "=", I!(E2))); 1342 static assert( isValidOperator!(I!(E1), "=", S!(E2, 1, 1))); 1343 static assert( isValidOperator!(I!(E1), "=", S!(E2, 1, 2))); 1344 1345 static assert( isValidOperator!(S!(E1, 1, 1), "=", E2)); 1346 static assert( isValidOperator!(D!(E1), "=", E2)); 1347 static assert( isValidOperator!(I!(E1), "=", E2)); 1348 } 1349 1350 foreach(E2; TypeTuple!(byte, short, int, long)[i+1 .. $]) 1351 { 1352 static assert(!isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 1))); 1353 static assert(!isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 2))); 1354 static assert(!isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 2))); 1355 static assert(!isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 1))); 1356 1357 static assert(!isValidOperator!(S!(E1, 1, 1), "=", D!(E2))); 1358 static assert(!isValidOperator!(S!(E1, 1, 2), "=", D!(E2))); 1359 static assert(!isValidOperator!(D!(E1), "=", S!(E2, 1, 1))); 1360 static assert(!isValidOperator!(D!(E1), "=", S!(E2, 1, 2))); 1361 1362 static assert(!isValidOperator!(S!(E1, 1, 1), "=", I!(E2))); 1363 static assert(!isValidOperator!(S!(E1, 1, 2), "=", I!(E2))); 1364 static assert(!isValidOperator!(I!(E1), "=", S!(E2, 1, 1))); 1365 static assert(!isValidOperator!(I!(E1), "=", S!(E2, 1, 2))); 1366 1367 static assert(!isValidOperator!(S!(E1, 1, 1), "=", E2)); 1368 static assert(!isValidOperator!(D!(E1), "=", E2)); 1369 static assert(!isValidOperator!(I!(E1), "=", E2)); 1370 } 1371 } 1372 } 1373 1374 1375 /** 1376 式テンプレート演算子の種類 1377 */ 1378 enum ETOSpec : uint 1379 { 1380 none = 0, 1381 matrixAddMatrix = (1 << 0), 1382 matrixSubMatrix = (1 << 1), 1383 matrixMulMatrix = (1 << 2), 1384 matrixAddScalar = (1 << 3), 1385 scalarAddMatrix = (1 << 4), 1386 matrixSubScalar = (1 << 5), 1387 scalarSubMatrix = (1 << 6), 1388 matrixMulScalar = (1 << 7), 1389 scalarMulMatrix = (1 << 8), 1390 matrixDivScalar = (1 << 9), 1391 scalarDivMatrix = (1 << 10), 1392 addScalar = (1 << 11), 1393 subScalar = (1 << 12), 1394 mulScalar = (1 << 13), 1395 divScalar = (1 << 14), 1396 opEquals = (1 << 15), 1397 toString = (1 << 16), 1398 opAssign = (1 << 17), 1399 swizzle = (1 << 18), 1400 modifiedOperator = addScalar | subScalar | mulScalar | divScalar | opAssign, 1401 all = (1 << 19) -1, 1402 } 1403 1404 1405 /** 1406 式テンプレートでの演算子の種類を返します 1407 */ 1408 template ETOperatorSpec(A, string op, B) 1409 if(isValidOperator!(A, op, B)) 1410 { 1411 static if((isMatrix!A || isAbstractMatrix!A) && (isMatrix!B || isAbstractMatrix!B)) 1412 enum ETOSpec ETOperatorSpec = op == "+" ? ETOSpec.matrixAddMatrix 1413 : (op == "-" ? ETOSpec.matrixSubMatrix 1414 : ETOSpec.matrixMulMatrix); 1415 else static if(isNotVectorOrMatrix!A) 1416 enum ETOSpec ETOperatorSpec = op == "+" ? ETOSpec.scalarAddMatrix 1417 : (op == "-" ? ETOSpec.scalarSubMatrix 1418 : (op == "*" ? ETOSpec.scalarMulMatrix 1419 : ETOSpec.scalarDivMatrix)); 1420 else 1421 enum ETOSpec ETOperatorSpec = op == "+" ? ETOSpec.matrixAddScalar 1422 : (op == "-" ? ETOSpec.matrixSubScalar 1423 : (op == "*" ? ETOSpec.matrixMulScalar 1424 : ETOSpec.matrixDivScalar)); 1425 } 1426 1427 /// 1428 unittest{ 1429 static struct S(T, size_t r, size_t c) 1430 { 1431 enum rows = r; 1432 enum cols = c; 1433 T opIndex(size_t i, size_t j) const {return T.init;} 1434 alias at = opIndex; 1435 } 1436 alias Matrix2i = S!(int, 2, 2); 1437 1438 static assert(ETOperatorSpec!(Matrix2i, "+", Matrix2i) == ETOSpec.matrixAddMatrix); 1439 static assert(ETOperatorSpec!(Matrix2i, "-", Matrix2i) == ETOSpec.matrixSubMatrix); 1440 static assert(ETOperatorSpec!(Matrix2i, "*", Matrix2i) == ETOSpec.matrixMulMatrix); 1441 static assert(ETOperatorSpec!(Matrix2i, "*", int) == ETOSpec.matrixMulScalar); 1442 static assert(ETOperatorSpec!(int, "*", Matrix2i) == ETOSpec.scalarMulMatrix); 1443 } 1444 1445 1446 /** 1447 式テンプレートでの、式を表します 1448 */ 1449 struct MatrixExpression(Lhs, string s, Rhs) 1450 if(isValidOperator!(Lhs, s, Rhs) 1451 && (isAbstractMatrix!Lhs || isAbstractMatrix!Rhs) 1452 && !(isMatrix!Lhs || isMatrix!Rhs)) 1453 { 1454 enum etoSpec = ETOperatorSpec!(Lhs, s, Rhs); 1455 1456 1457 static InferredResult inferSize(Msize_t r, Msize_t c) 1458 { 1459 static if(isAbstractMatrix!Lhs && isAbstractMatrix!Rhs) 1460 { 1461 static assert(s != "*"); 1462 auto rLhs = Lhs.inferSize(r, c); 1463 auto rRhs = Rhs.inferSize(r, c); 1464 1465 bool b = rLhs.isValid && rRhs.isValid && rLhs.rows == rRhs.rows && rLhs.cols == rRhs.cols; 1466 return InferredResult(b, rLhs.rows, rLhs.cols); 1467 } 1468 else static if(isAbstractMatrix!Lhs) 1469 return Lhs.inferSize(r, c); 1470 else 1471 return Rhs.inferSize(r, c); 1472 } 1473 1474 1475 auto opIndex(size_t i, size_t j) const 1476 { 1477 static if(etoSpec == ETOSpec.matrixAddMatrix) 1478 return this.lhs[i, j] + this.rhs[i, j]; 1479 else static if(etoSpec == ETOSpec.matrixSubMatrix) 1480 return this.lhs[i, j] - this.rhs[i, j]; 1481 else static if(etoSpec == ETOSpec.matrixMulMatrix) 1482 { 1483 static assert(0); 1484 return typeof(this.lhs.at(0, 0) + this.rhs.at(0, 0)).init; 1485 } 1486 else 1487 { 1488 static if(isMatrix!Lhs || isAbstractMatrix!Lhs) 1489 return mixin("this.lhs.at(i, j) " ~ s ~ " this.rhs"); 1490 else 1491 return mixin("this.lhs " ~ s ~ " this.rhs.at(i, j)"); 1492 } 1493 } 1494 1495 1496 mixin(defaultExprOps!(true)); 1497 1498 private: 1499 Lhs lhs; 1500 Rhs rhs; 1501 } 1502 1503 1504 /// ditto 1505 struct MatrixExpression(Lhs, string s, Rhs) 1506 if(isValidOperator!(Lhs, s, Rhs) 1507 && !((isAbstractMatrix!Lhs || isAbstractMatrix!Rhs) 1508 && !(isMatrix!Lhs || isMatrix!Rhs))) 1509 { 1510 enum etoSpec = ETOperatorSpec!(Lhs, s, Rhs); 1511 1512 1513 static if((isMatrix!Lhs || isAbstractMatrix!Lhs) && (isMatrix!Rhs || isAbstractMatrix!Rhs)) 1514 { 1515 static if(s == "*") 1516 { 1517 static if(hasStaticRows!Lhs) 1518 { 1519 enum rows = Lhs.rows; 1520 private enum staticrows = rows; 1521 } 1522 else static if(isAbstractMatrix!Lhs && hasStaticRows!Rhs) 1523 { 1524 enum rows = Lhs.inferSize(wild, Rhs.rows).rows; 1525 private enum staticrows = rows; 1526 } 1527 else static if(hasDynamicRows!Lhs) 1528 { 1529 @property size_t rows() const { return this.lhs.rows; } 1530 private enum staticrows = wild; 1531 } 1532 else static if(isAbstractMatrix!Lhs && hasDynamicRows!Rhs) 1533 { 1534 @property size_t rows() const { return this.lhs.inferSize(wild, rhs.rows).rows; } 1535 private enum staticrows = wild; 1536 } 1537 else 1538 static assert(0); 1539 1540 1541 static if(hasStaticCols!Rhs) 1542 { 1543 enum cols = Rhs.cols; 1544 private enum staticcols = cols; 1545 } 1546 else static if(isAbstractMatrix!Rhs && hasStaticCols!Lhs) 1547 { 1548 enum cols = Rhs.inferSize(Lhs.cols, wild).cols; 1549 private enum staticcols = cols; 1550 } 1551 else static if(hasDynamicRows!Rhs) 1552 { 1553 @property size_t cols() const { return this.rhs.cols; } 1554 private enum staticcols = wild; 1555 } 1556 else static if(isAbstractMatrix!Rhs && hasDynamicColumns!Lhs) 1557 { 1558 @property size_t cols() const { return this.rhs.inferSize(lhs.cols, wild).cols; } 1559 private enum staticcols = wild; 1560 } 1561 else 1562 static assert(0); 1563 } 1564 else 1565 { 1566 static if(hasStaticRows!Lhs) 1567 { 1568 enum rows = Lhs.rows; 1569 private enum staticrows = rows; 1570 } 1571 else static if(hasStaticRows!Rhs) 1572 { 1573 enum rows = Rhs.rows; 1574 private enum staticrows = rows; 1575 } 1576 else static if(hasDynamicRows!Lhs) 1577 { 1578 @property size_t rows() const { return this.lhs.rows; } 1579 private enum staticrows = wild; 1580 } 1581 else 1582 { 1583 @property size_t rows() const { return this.rhs.rows; } 1584 private enum staticrows = wild; 1585 } 1586 1587 static if(hasStaticCols!Lhs) 1588 { 1589 enum cols = Lhs.cols; 1590 private enum staticcols = cols; 1591 } 1592 else static if(hasStaticCols!Rhs) 1593 { 1594 enum cols = Rhs.cols; 1595 private enum staticcols = cols; 1596 } 1597 else static if(hasDynamicColumns!Lhs) 1598 { 1599 @property size_t cols() const { return this.lhs.cols; } 1600 private enum staticcols = wild; 1601 } 1602 else 1603 { 1604 @property size_t cols() const { return this.rhs.cols; } 1605 private enum staticcols = wild; 1606 } 1607 } 1608 } 1609 else static if(isMatrix!Lhs || isAbstractMatrix!Lhs) 1610 { 1611 static assert(!isAbstractMatrix!Lhs); 1612 1613 static if(hasStaticRows!Lhs) 1614 { 1615 enum rows = Lhs.rows; 1616 private enum staticrows = rows; 1617 } 1618 else 1619 { 1620 @property size_t rows() const { return this.lhs.rows; } 1621 private enum staticrows = wild; 1622 } 1623 1624 static if(hasStaticCols!Lhs) 1625 { 1626 enum cols = Lhs.cols; 1627 private enum staticcols = cols; 1628 } 1629 else 1630 { 1631 @property size_t cols() const { return this.lhs.cols; } 1632 private enum staticcols = wild; 1633 } 1634 } 1635 else 1636 { 1637 static assert(!isAbstractMatrix!Rhs); 1638 1639 static if(hasStaticRows!Rhs) 1640 { 1641 enum rows = Rhs.rows; 1642 private enum staticrsght = rows; 1643 } 1644 else 1645 { 1646 @property size_t rows() const { return this.rhs.rows; } 1647 private enum staticrsght = wild; 1648 } 1649 1650 static if(hasStaticCols!Rhs) 1651 { 1652 enum cols = Rhs.cols; 1653 private enum staticcsght = cols; 1654 } 1655 else 1656 { 1657 @property size_t cols() const { return this.rhs.cols; } 1658 private enum staticcsght = wild; 1659 } 1660 } 1661 1662 1663 auto opIndex(size_t i, size_t j) const 1664 in{ 1665 assert(i < this.rows); 1666 assert(j < this.cols); 1667 } 1668 body{ 1669 static if(etoSpec == ETOSpec.matrixAddMatrix) 1670 return this.lhs.at(i, j) + this.rhs.at(i, j); 1671 else static if(etoSpec == ETOSpec.matrixSubMatrix) 1672 return this.lhs.at(i, j) - this.rhs.at(i, j); 1673 else static if(etoSpec == ETOSpec.matrixMulMatrix) 1674 { 1675 auto sum = cast(Unqual!(typeof(this.lhs.at(0, 0) * this.rhs.at(0, 0))))0; 1676 1677 static if(hasStaticCols!Lhs) 1678 immutable cnt = Lhs.cols; 1679 else static if(hasStaticRows!Rhs) 1680 immutable cnt = Rhs.rows; 1681 else static if(hasDynamicColumns!Lhs) 1682 immutable cnt = this.lhs.cols; 1683 else 1684 immutable cnt = this.rhs.rows; 1685 1686 foreach(k; 0 .. cnt) 1687 sum += this.lhs.at(i, k) * this.rhs.at(k, j); 1688 return sum; 1689 } 1690 else 1691 { 1692 static if(isMatrix!Lhs || isAbstractMatrix!Lhs) 1693 return mixin("this.lhs.at(i, j) " ~ s ~ " this.rhs"); 1694 else 1695 return mixin("this.lhs " ~ s ~ " this.rhs.at(i, j)"); 1696 } 1697 } 1698 1699 mixin(defaultExprOps!(false)); 1700 1701 private: 1702 Lhs lhs; 1703 Rhs rhs; 1704 } 1705 1706 1707 /// ditto 1708 auto matrixExpression(string s, A, B)(auto ref A a, auto ref B b) 1709 if(isValidOperator!(A, s, B)) 1710 { 1711 return MatrixExpression!(A, s, B)(a, b); 1712 static assert(isNarrowMatrix!(MatrixExpression!(A, s, B)) 1713 || isAbstractMatrix!(MatrixExpression!(A, s, B))); 1714 } 1715 1716 1717 /** 1718 specに示された演算子をオーバーロードします。specはETOSpecで指定する必要があります。 1719 */ 1720 template ExpressionOperators(size_t spec, size_t rs, size_t cs) 1721 { 1722 enum stringMixin = 1723 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1724 q{ 1725 static if(is(typeof({enum _unused_ = this.rows;}))) static assert(rows != 0); 1726 static if(is(typeof({enum _unused_ = this.cols;}))) static assert(cols != 0); 1727 1728 alias at = opIndex; 1729 } ~ 1730 1731 1732 ( rs == 1 ? 1733 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1734 q{ 1735 alias cols length; 1736 alias length opDollar; 1737 1738 1739 auto ref opIndex(size_t i) inout 1740 in{ 1741 assert(i < this.cols); 1742 } 1743 body{ 1744 return this[0, i]; 1745 } 1746 1747 static if(is(typeof({this[0, 0] = this[0, 0];}))) 1748 { 1749 auto ref opIndexAssign(S)(S value, size_t i) 1750 if(is(typeof(this[0, i] = value))) 1751 in{ 1752 assert(i < this.cols); 1753 } 1754 body{ 1755 return this[0, i] = value; 1756 } 1757 1758 1759 auto ref opIndexOpAssign(string op, S)(S value, size_t i) 1760 if(is(typeof(mixin("this[0, i] " ~ op ~ "= value")))) 1761 in{ 1762 assert(i < this.cols); 1763 } 1764 body{ 1765 return mixin("this[0, i] " ~ op ~ "= value"); 1766 } 1767 } 1768 } : ( cs == 1 ? 1769 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1770 q{ 1771 alias rows length; 1772 alias length opDollar; 1773 1774 1775 auto ref opIndex(size_t i) inout 1776 in{ 1777 assert(i < this.rows); 1778 } 1779 body{ 1780 return this[i, 0]; 1781 } 1782 1783 static if(is(typeof({this[0, 0] = this[0, 0];}))) 1784 { 1785 auto ref opIndexAssign(S)(S value, size_t i) 1786 if(is(typeof(this[i, 0] = value))) 1787 in{ 1788 assert(i < this.rows); 1789 } 1790 body{ 1791 return this[0, i] = value; 1792 } 1793 1794 1795 auto ref opIndexOpAssign(string op, S)(S value, size_t i) 1796 if(is(typeof(mixin("this[i, 0] " ~ op ~ "= value")))) 1797 in{ 1798 assert(i < this.rows); 1799 } 1800 body{ 1801 return mixin("this[i, 0] " ~ op ~ "= value"); 1802 } 1803 } 1804 } : "" 1805 )) ~ 1806 1807 1808 /* 1809 ((rs == 1 || cs == 1) && (spec & ETOSpec.opAssign) ? 1810 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1811 q{ 1812 void opAssign(Array)(Array arr) 1813 if(!isNarrowMatrix!Array && is(typeof(arr[size_t.init])) && isAssignable!(typeof(this[size_t.init]), typeof(arr[size_t.init]))) 1814 { 1815 foreach(i; 0 .. this.length) 1816 this[i] = arr[i]; 1817 } 1818 } : "" 1819 ) ~ 1820 */ 1821 1822 1823 (rs == 1 && cs == 1 ? 1824 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1825 q{ 1826 auto det() const @property 1827 { 1828 return this[0, 0]; 1829 } 1830 1831 alias det this; 1832 1833 1834 auto ref opAssign(S)(S value) 1835 if(is(typeof(this[0, 0] = value))) 1836 { 1837 return this[0, 0] = value; 1838 } 1839 1840 1841 auto ref opOpAssign(S)(S value) 1842 if(is(typeof(mixin("this[0, 0] " ~ op ~ "= value")))) 1843 { 1844 return mixin("this[0, 0] " ~ op ~ "= value"); 1845 } 1846 } : "" 1847 ) ~ 1848 1849 1850 (spec & ETOSpec.opEquals ? 1851 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1852 q{ 1853 bool opEquals(Rhs)(auto ref const Rhs mat) const 1854 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1855 { 1856 static assert(isValidOperator!(Unqual!(typeof(this)), "+", Rhs)); 1857 1858 static if(isAbstractMatrix!Rhs) 1859 { 1860 auto result = Rhs.inferSize(this.rows, this.cols); 1861 if(!result.isValid) 1862 return false; 1863 } 1864 else 1865 { 1866 if(this.rows != mat.rows) 1867 return false; 1868 1869 if(this.cols != mat.cols) 1870 return false; 1871 } 1872 1873 foreach(i; 0 .. this.rows) 1874 foreach(j; 0 .. this.cols) 1875 if(this[i, j] != mat.at(i, j)) 1876 return false; 1877 return true; 1878 } 1879 } : "" 1880 ) ~ 1881 1882 1883 (spec & ETOSpec.toString ? 1884 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1885 q{ 1886 @property 1887 void toString(scope void delegate(const(char)[]) sink, string formatString) const @system 1888 { 1889 sink.formattedWrite(formatString, this.toRange); 1890 } 1891 } : "" 1892 ) ~ 1893 1894 1895 (spec & ETOSpec.opAssign ? 1896 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1897 q{ 1898 void opAssign(M)(M m) 1899 if((isMatrix!M || isAbstractMatrix!M) && is(typeof(this[0, 0] = m.at(0, 0)))) 1900 in{ 1901 static if(isAbstractMatrix!M) 1902 assert(m.inferSize(this.rows, this.cols).isValid); 1903 else 1904 { 1905 assert(m.rows == this.rows); 1906 assert(m.cols == this.cols); 1907 } 1908 } 1909 body{ 1910 static assert(isValidOperator!(typeof(this), "=", M)); 1911 1912 foreach(i; 0 .. this.rows) 1913 foreach(j; 0 .. this.cols) 1914 this[i, j] = m.at(i, j); 1915 } 1916 } : "" 1917 ) ~ 1918 1919 1920 (spec & ETOSpec.matrixAddMatrix ? 1921 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1922 q{ 1923 auto opBinary(string op : "+", Rhs)(auto ref Rhs mat) const 1924 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1925 in{ 1926 static if(isAbstractMatrix!Rhs) 1927 assert(mat.inferSize(this.rows, this.cols).isValid); 1928 else 1929 { 1930 assert(mat.rows == this.rows); 1931 assert(mat.cols == this.cols); 1932 } 1933 } 1934 body{ 1935 static assert(isValidOperator!(typeof(this), op, Rhs)); 1936 return matrixExpression!"+"(this, mat); 1937 } 1938 } : "" 1939 ) ~ 1940 1941 1942 (spec & ETOSpec.matrixSubMatrix ? 1943 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1944 q{ 1945 auto opBinary(string op : "-", Rhs)(auto ref Rhs mat) const 1946 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1947 in{ 1948 static if(isAbstractMatrix!Rhs) 1949 assert(mat.inferSize(this.rows, this.cols).isValid); 1950 else 1951 { 1952 assert(mat.rows == this.rows); 1953 assert(mat.cols == this.cols); 1954 } 1955 } 1956 body{ 1957 static assert(isValidOperator!(typeof(this), op, Rhs)); 1958 return matrixExpression!"-"(this, mat); 1959 } 1960 } : "" 1961 ) ~ 1962 1963 1964 (spec & ETOSpec.matrixMulMatrix ? 1965 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1966 q{ 1967 auto opBinary(string op : "*", Rhs)(auto ref Rhs mat) const 1968 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1969 in{ 1970 static if(isAbstractMatrix!Rhs) 1971 assert(mat.inferSize(this.cols, wild).isValid); 1972 else 1973 assert(mat.rows == this.cols); 1974 } 1975 body{ 1976 static assert(isValidOperator!(typeof(this), op, Rhs)); 1977 return matrixExpression!"*"(this, mat); 1978 } 1979 } : "" 1980 ) ~ 1981 1982 1983 (spec & ETOSpec.matrixAddScalar ? 1984 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1985 q{ 1986 auto opBinary(string op : "+", S)(S s) const 1987 if(isNotVectorOrMatrix!S) 1988 { 1989 static assert(isValidOperator!(typeof(this), op, S)); 1990 return matrixExpression!"+"(this, s); 1991 } 1992 } : "" 1993 ) ~ 1994 1995 1996 (spec & ETOSpec.scalarAddMatrix ? 1997 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1998 q{ 1999 auto opBinaryRight(string op : "+", S)(S s) const 2000 if(isNotVectorOrMatrix!S) 2001 { 2002 static assert(isValidOperator!(S, op, typeof(this))); 2003 return matrixExpression!"+"(s, this); 2004 } 2005 } : "" 2006 ) ~ 2007 2008 2009 (spec & ETOSpec.matrixSubScalar ? 2010 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2011 q{ 2012 auto opBinary(string op : "-", S)(S s) const 2013 if(isNotVectorOrMatrix!S) 2014 { 2015 static assert(isValidOperator!(typeof(this), op, S)); 2016 return matrixExpression!"-"(this, s); 2017 } 2018 } : "" 2019 ) ~ 2020 2021 2022 (spec & ETOSpec.scalarSubMatrix ? 2023 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2024 q{ 2025 auto opBinaryRight(string op : "-", S)(S s) const 2026 if(isNotVectorOrMatrix!S) 2027 { 2028 static assert(isValidOperator!(S, op, typeof(this))); 2029 return matrixExpression!"-"(s, this); 2030 } 2031 } : "" 2032 ) ~ 2033 2034 2035 (spec & ETOSpec.matrixMulScalar ? 2036 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2037 q{ 2038 auto opBinary(string op : "*", S)(S s) const 2039 if(isNotVectorOrMatrix!S) 2040 { 2041 static assert(isValidOperator!(typeof(this), op, S)); 2042 return matrixExpression!"*"(this, s); 2043 } 2044 } : "" 2045 ) ~ 2046 2047 2048 (spec & ETOSpec.scalarMulMatrix ? 2049 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2050 q{ 2051 auto opBinaryRight(string op : "*", S)(S s) const 2052 if(isNotVectorOrMatrix!S) 2053 { 2054 static assert(isValidOperator!(S, op, typeof(this))); 2055 return matrixExpression!"*"(s, this); 2056 } 2057 } : "" 2058 ) ~ 2059 2060 2061 (spec & ETOSpec.matrixDivScalar ? 2062 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2063 q{ 2064 auto opBinary(string op : "/", S)(S s) const 2065 if(isNotVectorOrMatrix!S) 2066 { 2067 static assert(isValidOperator!(typeof(this), op, S)); 2068 return matrixExpression!"/"(this, s); 2069 } 2070 } : "" 2071 ) ~ 2072 2073 2074 (spec & ETOSpec.scalarDivMatrix ? 2075 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2076 q{ 2077 auto opBinaryRight(string op : "/", S)(S s) const 2078 if(isNotVectorOrMatrix!S) 2079 { 2080 static assert(isValidOperator!(S, op, typeof(this))); 2081 return matrixExpression!"/"(s, this); 2082 } 2083 } : "" 2084 ) ~ 2085 2086 2087 (spec & ETOSpec.addScalar ? 2088 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2089 q{ 2090 void opOpAssign(string op : "+", S)(S scalar) 2091 if(is(typeof(this[0, 0] += scalar))) 2092 { 2093 foreach(r; 0 .. rows) 2094 foreach(c; 0 .. cols) 2095 this[r, c] += scalar; 2096 } 2097 } : "" 2098 ) ~ 2099 2100 2101 (spec & ETOSpec.subScalar ? 2102 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2103 q{ 2104 void opOpAssign(string op : "-", S)(S scalar) 2105 if(is(typeof(this[0, 0] -= scalar))) 2106 { 2107 foreach(r; 0 .. rows) 2108 foreach(c; 0 .. cols) 2109 this[r, c] -= scalar; 2110 } 2111 } : "" 2112 ) ~ 2113 2114 2115 (spec & ETOSpec.mulScalar ? 2116 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2117 q{ 2118 void opOpAssign(string op : "*", S)(S scalar) 2119 if(is(typeof(this[0, 0] *= scalar))) 2120 { 2121 foreach(r; 0 .. rows) 2122 foreach(c; 0 .. cols) 2123 this[r, c] *= scalar; 2124 } 2125 } : "" 2126 ) ~ 2127 2128 2129 (spec & ETOSpec.divScalar ? 2130 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2131 q{ 2132 void opOpAssign(string op : "/", S)(S scalar) 2133 if(is(typeof(this[0, 0] /= scalar))) 2134 { 2135 foreach(r; 0 .. rows) 2136 foreach(c; 0 .. cols) 2137 this[r, c] /= scalar; 2138 } 2139 } : "" 2140 ) ~ 2141 2142 2143 ((spec & ETOSpec.swizzle) && (rs == 1 || cs == 1) ? 2144 (rs*cs >= 1 ? 2145 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2146 q{ 2147 auto ref a() inout @property { return this[0]; } 2148 alias x = a; 2149 alias r = a; 2150 alias re = a; 2151 } : "" 2152 ) ~ 2153 (rs*cs >= 2 ? 2154 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2155 q{ 2156 auto ref b() inout @property { return this[1]; } 2157 alias y = b; 2158 alias im = b; 2159 alias i = b; 2160 } : "" 2161 ) ~ 2162 (rs*cs >= 3 ? 2163 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2164 q{ 2165 auto ref c() inout @property { return this[2]; } 2166 alias z = c; 2167 alias j = c; 2168 } : "" 2169 ) ~ 2170 (rs*cs >= 4 ? 2171 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2172 q{ 2173 auto ref d() inout @property { return this[3]; } 2174 alias k = d; 2175 alias w = d; 2176 } : "" 2177 ) ~ 2178 (rs*cs >= 5 ? 2179 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2180 q{ 2181 auto ref e() inout @property { return this[4]; } 2182 } : "" 2183 ) ~ 2184 (rs*cs >= 6 ? 2185 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2186 q{ 2187 auto ref f() inout @property { return this[5]; } 2188 } : "" 2189 ) ~ 2190 (rs*cs >= 7 ? 2191 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2192 q{ 2193 auto ref g() inout @property { return this[6]; } 2194 } : "" 2195 ) ~ 2196 (rs*cs >= 8 ? 2197 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2198 q{ 2199 auto ref h() inout @property { return this[7]; } 2200 } : "" 2201 ) : "" 2202 ); 2203 2204 2205 mixin template templateMixin() 2206 { 2207 mixin(stringMixin); 2208 } 2209 } 2210 2211 2212 /// ditto 2213 template ExpressionOperatorsInferable(size_t spec) 2214 { 2215 enum stringMixin = 2216 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2217 q{ 2218 alias at = opIndex; 2219 } ~ 2220 2221 2222 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2223 q{ 2224 bool opEquals(Rhs)(auto ref in Rhs mat) const 2225 if(isMatrix!Rhs && !is(Unqual!Rhs == typeof(this)) && !isAbstractMatrix!(Rhs)) 2226 { 2227 static assert(isValidOperator!(Unqual!(typeof(this)), "+", Rhs)); 2228 2229 if(!this.inferSize(mat.rows, mat.cols).isValid) 2230 return false; 2231 2232 foreach(i; 0 .. mat.rows) 2233 foreach(j; 0 .. mat.cols) 2234 if(this[i, j] != mat.at(i, j)) 2235 return false; 2236 return true; 2237 } 2238 } ~ 2239 2240 2241 (spec & ETOSpec.matrixAddMatrix ? 2242 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2243 q{ 2244 auto opBinary(string op : "+", Rhs)(auto ref const Rhs mat) const 2245 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 2246 in{ 2247 static if(!isAbstractMatrix!Rhs) 2248 assert(this.inferSize(mat.rows, mat.cols).isValid); 2249 } 2250 body{ 2251 static assert(isValidOperator!(typeof(this), op, Rhs)); 2252 return matrixExpression!"+"(this, mat); 2253 } 2254 } : "" 2255 ) ~ 2256 2257 2258 (spec & ETOSpec.matrixSubMatrix ? 2259 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2260 q{ 2261 auto opBinary(string op : "-", Rhs)(auto ref const Rhs mat) const 2262 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 2263 in{ 2264 static if(!isAbstractMatrix!Rhs) 2265 assert(this.inferSize(mat.rows, mat.cols).isValid); 2266 } 2267 body{ 2268 static assert(isValidOperator!(typeof(this), op, Rhs)); 2269 return matrixExpression!"-"(this, mat); 2270 } 2271 } : "" 2272 ) ~ 2273 2274 2275 (spec & ETOSpec.matrixMulMatrix ? 2276 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2277 q{ 2278 auto opBinary(string op : "*", Rhs)(auto ref const Rhs mat) const 2279 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 2280 in{ 2281 static if(!isAbstractMatrix!Rhs) 2282 assert(this.inferSize(wild, mat.rows).isValid); 2283 } 2284 body{ 2285 static assert(isValidOperator!(typeof(this), op, Rhs)); 2286 return matrixExpression!"*"(this, mat); 2287 } 2288 } : "" 2289 ) ~ 2290 2291 2292 (spec & ETOSpec.matrixAddScalar ? 2293 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2294 q{ 2295 auto opBinary(string op : "+", S)(const S s) const 2296 if(isNotVectorOrMatrix!S) 2297 { 2298 static assert(isValidOperator!(typeof(this), op, S)); 2299 return matrixExpression!"+"(this, s); 2300 } 2301 } : "" 2302 ) ~ 2303 2304 2305 (spec & ETOSpec.scalarAddMatrix ? 2306 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2307 q{ 2308 auto opBinaryRight(string op : "+", S)(const S s) const 2309 if(isNotVectorOrMatrix!S) 2310 { 2311 static assert(isValidOperator!(S, op, typeof(this))); 2312 return matrixExpression!"+"(s, this); 2313 } 2314 } : "" 2315 ) ~ 2316 2317 2318 (spec & ETOSpec.matrixSubScalar ? 2319 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2320 q{ 2321 auto opBinary(string op : "-", S)(const S s) const 2322 if(isNotVectorOrMatrix!S) 2323 { 2324 static assert(isValidOperator!(typeof(this), op, S)); 2325 return matrixExpression!"-"(this, s); 2326 } 2327 } : "" 2328 ) ~ 2329 2330 2331 (spec & ETOSpec.scalarSubMatrix ? 2332 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2333 q{ 2334 auto opBinaryRight(string op : "-", S)(const S s) const 2335 if(isNotVectorOrMatrix!S) 2336 { 2337 static assert(isValidOperator!(S, op, typeof(this))); 2338 return matrixExpression!"-"(s, this); 2339 } 2340 } : "" 2341 ) ~ 2342 2343 2344 (spec & ETOSpec.matrixMulScalar ? 2345 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2346 q{ 2347 auto opBinary(string op : "*", S)(const S s) const 2348 if(isNotVectorOrMatrix!S) 2349 { 2350 static assert(isValidOperator!(typeof(this), op, S)); 2351 return matrixExpression!"*"(this, s); 2352 } 2353 } : "" 2354 ) ~ 2355 2356 2357 (spec & ETOSpec.scalarMulMatrix ? 2358 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2359 q{ 2360 auto opBinaryRight(string op : "*", S)(const S s) const 2361 if(isNotVectorOrMatrix!S) 2362 { 2363 static assert(isValidOperator!(S, op, typeof(this))); 2364 return matrixExpression!"*"(s, this); 2365 } 2366 } : "" 2367 ) ~ 2368 2369 2370 (spec & ETOSpec.matrixDivScalar ? 2371 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2372 q{ 2373 auto opBinary(string op : "/", S)(const S s) const 2374 if(isNotVectorOrMatrix!S) 2375 { 2376 static assert(isValidOperator!(typeof(this), op, S)); 2377 return matrixExpression!"/"(this, s); 2378 } 2379 } : "" 2380 ) ~ 2381 2382 (spec & ETOSpec.scalarDivMatrix ? 2383 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2384 q{ 2385 auto opBinaryRight(string op : "/", S)(const S s) const 2386 if(isNotVectorOrMatrix!S) 2387 { 2388 static assert(isValidOperator!(S, op, typeof(this))); 2389 return matrixExpression!"/"(s, this); 2390 } 2391 } : "" 2392 ) ~ 2393 2394 2395 (spec & ETOSpec.addScalar ? 2396 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2397 q{ 2398 void opOpAssign(string op : "+", S)(const S scalar) 2399 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] += scalar))) 2400 { 2401 foreach(r; 0 .. rows) 2402 foreach(c; 0 .. cols) 2403 this[r, c] += scalar; 2404 } 2405 } : "" 2406 ) ~ 2407 2408 2409 (spec & ETOSpec.subScalar ? 2410 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2411 q{ 2412 void opOpAssign(string op : "-", S)(const S scalar) 2413 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] -= scalar))) 2414 { 2415 foreach(r; 0 .. rows) 2416 foreach(c; 0 .. cols) 2417 this[r, c] -= scalar; 2418 } 2419 } : "" 2420 ) ~ 2421 2422 2423 (spec & ETOSpec.mulScalar ? 2424 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2425 q{ 2426 void opOpAssign(string op : "*", S)(const S scalar) 2427 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] *= scalar))) 2428 { 2429 foreach(r; 0 .. rows) 2430 foreach(c; 0 .. cols) 2431 this[r, c] *= scalar; 2432 } 2433 } : "" 2434 ) ~ 2435 2436 2437 (spec & ETOSpec.divScalar ? 2438 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2439 q{ 2440 void opOpAssign(string op : "+", S)(const S scalar) 2441 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] /= scalar))) 2442 { 2443 foreach(r; 0 .. rows) 2444 foreach(c; 0 .. cols) 2445 this[r, c] /= scalar; 2446 } 2447 } : "" 2448 ); 2449 2450 2451 mixin template templateMixin() 2452 { 2453 mixin(stringMixin); 2454 } 2455 } 2456 2457 2458 /** 2459 全ての演算子をオーバーロードします。 2460 */ 2461 template defaultExprOps(bool isInferable = false) 2462 { 2463 enum defaultExprOps = 2464 isInferable ? 2465 q{ 2466 mixin(ExpressionOperatorsInferable!(ETOSpec.all).stringMixin); 2467 //mixin(ExpressionOperatorsInferable!(ETOSpec.all & ~ETOSpec.opEquals & ~ETOSpec.toString).stringMixin); 2468 //mixin(ExpressionOperatorsInferable!(ETOSpec.modifiedOperator).stringMixin); 2469 //const{mixin(ExpressionOperatorsInferable!(ETOSpec.all & ~ETOSpec.modifiedOperator).stringMixin);} 2470 //immutable{mixin(ExpressionOperatorsInferable!(ETOSpec.all & ~ETOSpec.opEquals & ~ETOSpec.toString).stringMixin);} 2471 } 2472 : 2473 q{ 2474 mixin(ExpressionOperators!(ETOSpec.all, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 2475 //mixin(ExpressionOperators!(ETOSpec.all & ~ETOSpec.opEquals & ~ETOSpec.toString, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 2476 //mixin(ExpressionOperators!(ETOSpec.modifiedOperator, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 2477 //const{mixin(ExpressionOperators!(ETOSpec.all & ~ETOSpec.modifiedOperator, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin);} 2478 //immutable{mixin(ExpressionOperators!(ETOSpec.all & ~ETOSpec.opEquals & ~ETOSpec.toString, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin);} 2479 }; 2480 } 2481 2482 2483 unittest{ 2484 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2485 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2486 2487 2488 static struct M(size_t rs, size_t cs) 2489 { 2490 enum rows = rs; 2491 enum cols = cs; 2492 2493 size_t opIndex(size_t i, size_t j) inout {return i + j;} 2494 2495 //inout: 2496 mixin(defaultExprOps!(false)); 2497 } 2498 2499 2500 alias S3 = M!(3, 3); 2501 alias S23 = M!(2, 3); 2502 2503 static assert(isNarrowMatrix!S3); 2504 static assert(hasStaticRows!S3); 2505 static assert(hasStaticCols!S3); 2506 static assert(isNarrowMatrix!S23); 2507 static assert(hasStaticRows!S23); 2508 static assert(hasStaticCols!S23); 2509 2510 2511 M!(1, 1) m11; 2512 static assert(isNarrowMatrix!(typeof(m11))); 2513 size_t valueM11 = m11; 2514 2515 M!(3, 1) m31; 2516 M!(1, 3) m13; 2517 valueM11 = m13 * m31; 2518 2519 2520 // swizzle 2521 M!(1, 8) m18; 2522 assert(m18[0] == m18.a); 2523 assert(m18[1] == m18.b); 2524 assert(m18[2] == m18.c); 2525 assert(m18[3] == m18.d); 2526 assert(m18[4] == m18.e); 2527 assert(m18[5] == m18.f); 2528 assert(m18[6] == m18.g); 2529 assert(m18[7] == m18.h); 2530 2531 assert(m18[0] == m18.r); 2532 assert(m18[1] == m18.i); 2533 assert(m18[2] == m18.j); 2534 assert(m18[3] == m18.k); 2535 2536 assert(m18[0] == m18.x); 2537 assert(m18[1] == m18.y); 2538 assert(m18[2] == m18.z); 2539 assert(m18[3] == m18.w); 2540 2541 assert(m18[0] == m18.re); 2542 assert(m18[1] == m18.im); 2543 2544 2545 static struct I 2546 { 2547 size_t opIndex(size_t i, size_t j) inout { return i == j ? 1 : 0;} 2548 alias at = opIndex; 2549 2550 static InferredResult inferSize(Msize_t r, Msize_t c) 2551 { 2552 if(r == wild && c == wild) 2553 return InferredResult(false); 2554 else if(r == c || r == wild || c == wild) 2555 return InferredResult(true, max(r, c), max(r, c)); 2556 else 2557 return InferredResult(false); 2558 } 2559 2560 mixin(defaultExprOps!(true)); 2561 } 2562 2563 //static assert(isNarrowMatrix!I); 2564 static assert(isAbstractMatrix!I); 2565 static assert( I.inferSize(wild, 1).isValid); 2566 static assert( I.inferSize(3, 3).isValid); 2567 static assert(!I.inferSize(1, 3).isValid); 2568 2569 2570 static struct D{ 2571 size_t rows; 2572 size_t cols; 2573 2574 size_t opIndex(size_t i, size_t j) inout {return i + j;} 2575 2576 mixin(defaultExprOps!(false)); 2577 } 2578 static assert(isNarrowMatrix!D); 2579 static assert(hasDynamicRows!D); 2580 static assert(hasDynamicColumns!D); 2581 2582 2583 S3 a; 2584 auto add = a + a; 2585 static assert(isNarrowMatrix!(typeof(add))); 2586 static assert(hasStaticRows!(typeof(add))); 2587 static assert(hasStaticCols!(typeof(add))); 2588 assert(add[0, 0] == 0); assert(add[0, 1] == 2); assert(add[0, 2] == 4); 2589 assert(add[1, 0] == 2); assert(add[1, 1] == 4); assert(add[1, 2] == 6); 2590 assert(add[2, 0] == 4); assert(add[2, 1] == 6); assert(add[2, 2] == 8); 2591 2592 auto mul = a * a; 2593 static assert(isNarrowMatrix!(typeof(mul))); 2594 static assert(hasStaticRows!(typeof(mul))); 2595 static assert(hasStaticCols!(typeof(mul))); 2596 assert(mul[0, 0] == 5); assert(mul[0, 1] == 8); assert(mul[0, 2] ==11); 2597 assert(mul[1, 0] == 8); assert(mul[1, 1] ==14); assert(mul[1, 2] ==20); 2598 assert(mul[2, 0] ==11); assert(mul[2, 1] ==20); assert(mul[2, 2] ==29); 2599 2600 auto sadd = a + 3; 2601 static assert(isNarrowMatrix!(typeof(sadd))); 2602 static assert(hasStaticRows!(typeof(sadd))); 2603 static assert(hasStaticCols!(typeof(sadd))); 2604 assert(sadd[0, 0] == 3); assert(sadd[0, 1] == 4); assert(sadd[0, 2] == 5); 2605 assert(sadd[1, 0] == 4); assert(sadd[1, 1] == 5); assert(sadd[1, 2] == 6); 2606 assert(sadd[2, 0] == 5); assert(sadd[2, 1] == 6); assert(sadd[2, 2] == 7); 2607 2608 // auto addMasS = a + m11; 2609 2610 auto add5 = a + a + cast(const)(a) * 3; 2611 static assert(isNarrowMatrix!(typeof(add5))); 2612 static assert(hasStaticRows!(typeof(add5))); 2613 static assert(hasStaticCols!(typeof(add5))); 2614 assert(add5 == a * 5); 2615 2616 I i; 2617 auto addi = a + i; 2618 static assert(isNarrowMatrix!(typeof(addi))); 2619 static assert(hasStaticRows!(typeof(addi))); static assert(typeof(addi).rows == 3); 2620 static assert(hasStaticCols!(typeof(addi))); static assert(typeof(addi).cols == 3); 2621 assert(addi[0, 0] == 1); assert(addi[0, 1] == 1); assert(addi[0, 2] == 2); 2622 assert(addi[1, 0] == 1); assert(addi[1, 1] == 3); assert(addi[1, 2] == 3); 2623 assert(addi[2, 0] == 2); assert(addi[2, 1] == 3); assert(addi[2, 2] == 5); 2624 2625 auto i2 = i * 2; 2626 static assert(!isNarrowMatrix!(typeof(i2))); 2627 static assert(isAbstractMatrix!(typeof(i2))); 2628 static assert( typeof(i2).inferSize(wild, 1).isValid); 2629 static assert( typeof(i2).inferSize(3, 3).isValid); 2630 static assert(!typeof(i2).inferSize(1, 3).isValid); 2631 2632 auto addi2 = a + i2; 2633 static assert(isNarrowMatrix!(typeof(addi2))); 2634 static assert(hasStaticRows!(typeof(addi2))); static assert(typeof(addi2).rows == 3); 2635 static assert(hasStaticCols!(typeof(addi2))); static assert(typeof(addi2).cols == 3); 2636 assert(addi2[0, 0] == 2); assert(addi2[0, 1] == 1); assert(addi2[0, 2] == 2); 2637 assert(addi2[1, 0] == 1); assert(addi2[1, 1] == 4); assert(addi2[1, 2] == 3); 2638 assert(addi2[2, 0] == 2); assert(addi2[2, 1] == 3); assert(addi2[2, 2] == 6); 2639 2640 static assert(!is(typeof(S23.init + i))); 2641 static assert(!is(typeof(i + S23.init))); 2642 assert(S23.init * i == S23.init); 2643 assert(i * S23.init == S23.init); 2644 2645 2646 import core.exception, std.exception; 2647 2648 D d33 = D(3, 3); 2649 auto addsd = a + d33; 2650 static assert(isNarrowMatrix!(typeof(addsd))); 2651 static assert(hasStaticRows!(typeof(addsd))); 2652 static assert(hasStaticCols!(typeof(addsd))); 2653 assert(addsd == a * 2); 2654 assert(addsd == d33 * 2); 2655 2656 auto addsdr = d33 + a; 2657 static assert(isNarrowMatrix!(typeof(addsdr))); 2658 static assert(hasStaticRows!(typeof(addsdr))); 2659 static assert(hasStaticCols!(typeof(addsdr))); 2660 assert(addsdr == addsd); 2661 assert(addsdr == addsd); 2662 2663 version(Release){}else assert(collectException!AssertError(D(2, 3) + a)); 2664 version(Release){}else assert(collectException!AssertError(D(2, 3) + i)); 2665 assert(D(2, 3) * i == D(2, 3)); 2666 2667 version(Release){}else assert(collectException!AssertError(D(2, 3) + D(2, 2))); 2668 version(Release){}else assert(collectException!AssertError(D(2, 3) + D(3, 3))); 2669 assert((D(2, 3) + D(2, 3)).rows == 2); 2670 assert((D(2, 3) + D(2, 3)).cols == 3); 2671 assert((D(2, 3) * D(3, 4)).rows == 2); 2672 assert((D(2, 3) * D(3, 4)).cols == 4); 2673 2674 auto mulds = d33 * 3; 2675 assert(mulds == d33 + d33 + d33); 2676 } 2677 2678 2679 /** 2680 InferableMatrixをある大きさの行列へ固定します。 2681 */ 2682 auto congeal(Msize_t rs, Msize_t cs, A)(A mat) 2683 if(isAbstractMatrix!A && A.inferSize(rs, cs).isValid) 2684 { 2685 static struct Result() 2686 { 2687 enum size_t rows = A.inferSize(rs, cs).rows; 2688 enum size_t cols = A.inferSize(rs, cs).cols; 2689 2690 auto ref opIndex(size_t i, size_t j) inout 2691 in{ 2692 assert(i < rows); 2693 assert(j < cols); 2694 } 2695 body{ 2696 return _mat[i, j]; 2697 } 2698 2699 2700 static if(carbon.linear.hasAssignableElements!A) 2701 { 2702 void opIndexAssign(E)(E e, size_t i, size_t j) 2703 in{ 2704 assert(i < rows); 2705 assert(j < cols); 2706 } 2707 body{ 2708 _mat[i, j] = e; 2709 } 2710 } 2711 2712 mixin(defaultExprOps!(false)); 2713 2714 private: 2715 A _mat; 2716 } 2717 2718 return Result!()(mat); 2719 } 2720 2721 /// 2722 unittest{ 2723 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2724 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2725 2726 2727 static struct I{ 2728 2729 size_t opIndex(size_t i, size_t j) inout { return i == j ? 1 : 0;} 2730 2731 static InferredResult inferSize(Msize_t r, Msize_t c) 2732 { 2733 if(r == wild && c == wild) 2734 return InferredResult(false); 2735 else if(isEqOrEitherEqX(r, c, wild)) 2736 return InferredResult(true, max(r, c), max(r, c)); 2737 else 2738 return InferredResult(false); 2739 } 2740 2741 mixin(defaultExprOps!(true)); 2742 } 2743 2744 static assert(!isNarrowMatrix!I); 2745 static assert(isAbstractMatrix!I); 2746 static assert( I.inferSize(wild, 1).isValid); 2747 static assert( I.inferSize(3, 3).isValid); 2748 static assert(!I.inferSize(1, 3).isValid); 2749 2750 I id; 2751 auto i3x3 = id.congeal!(3, 3)(); 2752 static assert(isNarrowMatrix!(typeof(i3x3))); 2753 static assert(hasStaticRows!(typeof(i3x3))); 2754 static assert(hasStaticCols!(typeof(i3x3))); 2755 static assert(i3x3.rows == 3); 2756 static assert(i3x3.cols == 3); 2757 assert(i3x3[0, 0] == 1);assert(i3x3[1, 0] == 0);assert(i3x3[2, 0] == 0); 2758 assert(i3x3[0, 1] == 0);assert(i3x3[1, 1] == 1);assert(i3x3[2, 1] == 0); 2759 assert(i3x3[0, 2] == 0);assert(i3x3[1, 2] == 0);assert(i3x3[2, 2] == 1); 2760 } 2761 2762 2763 /// ditto 2764 auto congeal(A)(auto ref A mat, size_t r, size_t c) 2765 if(isAbstractMatrix!A) 2766 in{ 2767 assert(A.inferSize(r, c).isValid); 2768 } 2769 body{ 2770 static struct Result() 2771 { 2772 @property size_t rows() const { return _rs; } 2773 @property size_t cols() const { return _cs; } 2774 2775 auto ref opIndex(size_t i, size_t j) inout 2776 in{ 2777 assert(i < rows); 2778 assert(j < cols); 2779 } 2780 body{ 2781 return _mat[i, j]; 2782 } 2783 2784 2785 static if(carbon.linear.hasAssignableElements!A) 2786 { 2787 void opIndexAssign(E)(E e, size_t i, size_t j) 2788 in{ 2789 assert(i < rows); 2790 assert(j < cols); 2791 } 2792 body{ 2793 _mat[i, j] = e; 2794 } 2795 } 2796 2797 mixin(defaultExprOps!(false)); 2798 2799 size_t _rs, _cs; 2800 A _mat; 2801 } 2802 2803 2804 return Result!()(r, c, mat); 2805 } 2806 2807 2808 /// 2809 unittest{ 2810 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2811 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2812 2813 2814 static struct I{ 2815 2816 size_t opIndex(size_t i, size_t j) inout { return i == j ? 1 : 0;} 2817 2818 static InferredResult inferSize(Msize_t r, Msize_t c) 2819 { 2820 if(r == wild && c == wild) 2821 return InferredResult(false); 2822 else if(isEqOrEitherEqX(r, c, wild)) 2823 return InferredResult(true, max(r, c), max(r, c)); 2824 else 2825 return InferredResult(false); 2826 } 2827 2828 mixin(defaultExprOps!(true)); 2829 } 2830 2831 static assert(!isNarrowMatrix!I); 2832 static assert(isAbstractMatrix!I); 2833 static assert( I.inferSize(wild, 1).isValid); 2834 static assert( I.inferSize(3, 3).isValid); 2835 static assert(!I.inferSize(1, 3).isValid); 2836 2837 I id; 2838 auto i3x3 = id.congeal(3, 3); 2839 static assert(isNarrowMatrix!(typeof(i3x3))); 2840 static assert(hasDynamicRows!(typeof(i3x3))); 2841 static assert(hasDynamicColumns!(typeof(i3x3))); 2842 assert(i3x3.rows == 3); 2843 assert(i3x3.cols == 3); 2844 assert(i3x3[0, 0] == 1);assert(i3x3[1, 0] == 0);assert(i3x3[2, 0] == 0); 2845 assert(i3x3[0, 1] == 0);assert(i3x3[1, 1] == 1);assert(i3x3[2, 1] == 0); 2846 assert(i3x3[0, 2] == 0);assert(i3x3[1, 2] == 0);assert(i3x3[2, 2] == 1); 2847 } 2848 2849 2850 /** 2851 DynamicMatrixをある大きさへ固定します。 2852 */ 2853 auto congeal(Msize_t rs, Msize_t cs, A)(auto ref A mat) 2854 //if(!isAbstractMatrix!A && (hasDynamicRows!A || rs == A.rows) || (hasDynamicColumns!A || cs == A.cols)) 2855 if(is(typeof({ 2856 static assert(!isAbstractMatrix!A); 2857 static if(hasStaticRows!A) 2858 static assert(rs == staticRows!A); 2859 static if(hasStaticCols!A) 2860 static assert(cs == staticCols!A); 2861 }))) 2862 in{ 2863 assert(mat.rows == rs); 2864 assert(mat.cols == cs); 2865 } 2866 body{ 2867 static struct Result() 2868 { 2869 enum rows = rs; 2870 enum cols = cs; 2871 2872 2873 auto ref opIndex(size_t i, size_t j) inout 2874 in{ 2875 assert(i < rows); 2876 assert(j < cols); 2877 } 2878 body{ 2879 return _mat[i, j]; 2880 } 2881 2882 2883 static if(carbon.linear.hasAssignableElements!A) 2884 { 2885 void opIndexAssign(E)(E e, size_t i, size_t j) 2886 in{ 2887 assert(i < rows); 2888 assert(j < cols); 2889 } 2890 body{ 2891 _mat[i, j] = e; 2892 } 2893 } 2894 2895 2896 mixin(defaultExprOps!(false)); 2897 2898 private: 2899 A _mat; 2900 } 2901 2902 return Result!()(mat); 2903 } 2904 2905 2906 /// ditto 2907 unittest{ 2908 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2909 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2910 2911 2912 static struct D{ 2913 size_t rows; 2914 size_t cols; 2915 2916 size_t opIndex(size_t i, size_t j) inout {return i + j;} 2917 2918 mixin(defaultExprOps!(false)); 2919 } 2920 static assert(isNarrowMatrix!D); 2921 static assert(hasDynamicRows!D); 2922 static assert(hasDynamicColumns!D); 2923 2924 D d3x3 = D(3, 3); 2925 auto s3x3 = d3x3.congeal!(3, 3)(); 2926 static assert(isNarrowMatrix!(typeof(s3x3))); 2927 static assert(s3x3.rows == 3); 2928 static assert(s3x3.cols == 3); 2929 assert(s3x3[0, 0] == 0); assert(s3x3[1, 0] == 1); assert(s3x3[2, 0] == 2); 2930 assert(s3x3[0, 1] == 1); assert(s3x3[1, 1] == 2); assert(s3x3[2, 1] == 3); 2931 assert(s3x3[0, 2] == 2); assert(s3x3[1, 2] == 3); assert(s3x3[2, 2] == 4); 2932 } 2933 2934 2935 /// 2936 auto matrix(size_t rs, size_t cs, alias f, S...)(S sizes) 2937 if(is(typeof(f(0, 0))) && S.length == ((rs == 0) + (cs == 0)) && (S.length == 0 || is(CommonType!S : size_t))) 2938 { 2939 static struct Result() 2940 { 2941 static if(rs) 2942 enum rows = rs; 2943 else{ 2944 private size_t _rs; 2945 @property size_t rows() const { return _rs; } 2946 } 2947 2948 static if(cs) 2949 enum cols = cs; 2950 else{ 2951 private size_t _cs; 2952 @property size_t cols() const { return _cs; } 2953 } 2954 2955 2956 auto ref opIndex(size_t i, size_t j) const 2957 in{ 2958 assert(i < rows); 2959 assert(j < cols); 2960 } 2961 body{ 2962 return f(i, j); 2963 } 2964 2965 2966 mixin(defaultExprOps!(false)); 2967 } 2968 2969 static if(S.length == 0) 2970 return Result!()(); 2971 else static if(S.length == 1) 2972 return Result!()(sizes[0]); 2973 else static if(S.length == 2) 2974 return Result!()(sizes[0], sizes[1]); 2975 } 2976 2977 2978 auto matrix(alias f)(size_t r, size_t c) 2979 if(is(typeof(f(0, 0)))) 2980 { 2981 return matrix!(0, 0, f)(r, c); 2982 } 2983 2984 2985 unittest{ 2986 auto m = matrix!(2, 3, (i, j) => i * 3 + j); 2987 static assert(isNarrowMatrix!(typeof(m))); 2988 static assert(hasStaticRows!(typeof(m))); 2989 static assert(hasStaticCols!(typeof(m))); 2990 static assert(m.rows == 2); 2991 static assert(m.cols == 3); 2992 assert(m[0, 0] == 0); assert(m[0, 1] == 1); assert(m[0, 2] == 2); 2993 assert(m[1, 0] == 3); assert(m[1, 1] == 4); assert(m[1, 2] == 5); 2994 2995 2996 auto md = matrix!((i, j) => i * 3 + j)(2, 3); 2997 assert(m == md); 2998 } 2999 3000 3001 auto matrix(alias f)() 3002 { 3003 static struct Result() 3004 { 3005 3006 3007 static InferredResult inferSize(Msize_t r, Msize_t c) 3008 { 3009 if(r == wild && c == wild) 3010 return InferredResult(false); 3011 else if(r == wild || c == wild) 3012 return InferredResult(true, max(r, c), max(r, c)); 3013 else 3014 return InferredResult(true, r, c); 3015 } 3016 3017 3018 auto ref opIndex(size_t i, size_t j) const { return f(i, j); } 3019 3020 3021 mixin(defaultExprOps!(true)); 3022 } 3023 3024 return Result!()(); 3025 } 3026 unittest{ 3027 auto mi = matrix!((i, j) => i * 3 + j); 3028 static assert(isAbstractMatrix!(typeof(mi))); 3029 assert(mi == matrix!((i, j) => i * 3 + j)(2, 3)); 3030 } 3031 3032 3033 /** 3034 内部で2次元配列を持つ行列です 3035 */ 3036 struct DMatrix(T, Msize_t rs = dynamic, Msize_t cs = dynamic, Major mjr = Major.row) 3037 { 3038 enum bool isColumnMajor = mjr == Major.column; 3039 enum bool isRowMajor = mjr == Major.row; 3040 enum major = mjr; 3041 3042 3043 this(M)(auto ref M m) 3044 if(!is(M == typeof(this))) 3045 { 3046 static if(isMatrix!M) 3047 this.noAlias = m; 3048 else 3049 this.opAssign(m); 3050 } 3051 3052 3053 static if(rs != dynamic) 3054 { 3055 enum size_t rows = rs; 3056 } 3057 else 3058 { 3059 @property 3060 size_t rows() const 3061 { 3062 static if(cs == 1) 3063 return _array.length; 3064 else static if(isColumnMajor) 3065 { 3066 if(this.tupleof[0].length) 3067 return this.tupleof[0][0].length; 3068 else 3069 return 0; 3070 } 3071 else 3072 return this.tupleof[0].length; 3073 } 3074 3075 3076 @property 3077 void rows(size_t newSize) 3078 { 3079 static if(rs == 1) 3080 return _array.length; 3081 else static if(isColumnMajor) 3082 { 3083 foreach(ref e; _array) 3084 e.length = newSize; 3085 } 3086 else 3087 _array.length = newSize; 3088 } 3089 } 3090 3091 static if(cs != dynamic) 3092 { 3093 enum size_t cols = cs; 3094 } 3095 else 3096 { 3097 @property 3098 size_t cols() const 3099 { 3100 static if(rs == 1) 3101 return _array.length; 3102 else static if(isRowMajor){ 3103 if(this.tupleof[0].length) 3104 return this.tupleof[0][0].length; 3105 else 3106 return 0; 3107 } 3108 else 3109 return this.tupleof[0].length; 3110 } 3111 3112 3113 @property 3114 void cols(size_t newSize) 3115 { 3116 static if(rs == 1) 3117 return _array.length = newSize; 3118 else static if(isRowMajor) 3119 { 3120 foreach(ref e; _array) 3121 e.length = newSize; 3122 } 3123 else 3124 _array.length = newSize; 3125 } 3126 } 3127 3128 3129 auto ref opIndex(size_t i, size_t j) inout 3130 in{ 3131 assert(i < rows); 3132 assert(j < cols); 3133 } 3134 body{ 3135 static if(rs == 1 || cs == 1) 3136 return _array[i+j]; 3137 else static if(isRowMajor) 3138 return _array[i][j]; 3139 else 3140 return _array[j][i]; 3141 } 3142 3143 3144 mixin(ExpressionOperators!(ETOSpec.all & ~ETOSpec.opAssign, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 3145 //mixin(defaultExprOps!(false)); 3146 3147 3148 void opAssign(M)(auto ref M mat) 3149 if(!is(typeof(this) == M) && isValidOperator!(typeof(this), "=", M)) 3150 in{ 3151 static if(rs && hasDynamicRows!M) 3152 assert(this.rows == mat.rows); 3153 3154 static if(cs && hasDynamicColumns!M) 3155 assert(this.cols == mat.cols); 3156 3157 static if(isAbstractMatrix!M) 3158 assert(mat.inferSize(this.rows, this.cols).isValid); 3159 } 3160 body 3161 { 3162 static if(isAbstractMatrix!M) 3163 { 3164 immutable rl = this.rows, 3165 cl = this.cols; 3166 } 3167 else 3168 { 3169 immutable rl = mat.rows, 3170 cl = mat.cols; 3171 } 3172 3173 immutable n = rl * cl; 3174 3175 if(_buffer.length < n) 3176 _buffer.length = n; 3177 3178 foreach(i; 0 .. rl) 3179 foreach(j; 0 .. cl) 3180 { 3181 static if(major == Major.row) 3182 _buffer[i * cl + j] = mat.at(i, j); 3183 else 3184 _buffer[j * rl + i] = mat.at(i, j); 3185 } 3186 3187 static if(rs == dynamic) 3188 this.rows = rl; 3189 3190 static if(cs == dynamic) 3191 this.cols = cl; 3192 3193 static if(rs == 1 || cs == 1) 3194 _array[] = _buffer[0 .. n][]; 3195 else{ 3196 foreach(i; 0 .. _array.length) 3197 _array[i][] = _buffer[i * _array[0].length .. (i+1) * _array[0].length][]; 3198 } 3199 } 3200 3201 3202 void opAssign(X)(X m) 3203 if(!isNarrowMatrix!X && isRandomAccessRange!X && (rs || !isInfinite!X) 3204 && isRandomAccessRange!(std.range.ElementType!X) 3205 && (cs || !isInfinite!(std.range.ElementType!X)) 3206 && isAssignable!(T, std.range.ElementType!X)) 3207 { 3208 if(m.length && m[0].length){ 3209 static if(rs == dynamic){ 3210 if(m.length != this.rows) 3211 this.rows = m.length; 3212 } 3213 3214 static if(cs == dynamic){ 3215 if(m[0].length != this.cols) 3216 this.cols = m[0].length; 3217 } 3218 3219 foreach(i; 0 .. this.rows) 3220 foreach(j; 0 .. this.cols) 3221 this[i, j] = m[i][j]; 3222 } 3223 } 3224 3225 3226 void opAssign(X)(X m) 3227 if(!isNarrowMatrix!X && isRandomAccessRange!X && isAssignable!(T, std.range.ElementType!X)) 3228 { 3229 foreach(i; 0 .. this.rows) 3230 foreach(j; 0 .. this.cols) 3231 this[i, j] = m[i * this.cols + j]; 3232 } 3233 3234 3235 static if(rs == 1 || cs == 1) 3236 { 3237 void opAssign(Array)(Array arr) 3238 if(!isNarrowMatrix!Array && isAssignable!(T, typeof(arr[size_t.init]))) 3239 { 3240 foreach(i; 0 .. this.length) 3241 this[i] = arr[i]; 3242 } 3243 } 3244 3245 3246 @property 3247 void noAlias(M)(auto ref M mat) 3248 if(isValidOperator!(typeof(this), "=", M) && ((!rs || !hasStaticRows!M) || is(typeof({static assert(this.rows == M.rows);}))) 3249 && ((!cs || !hasStaticCols!M) || is(typeof({static assert(this.cols == M.cols);})))) 3250 in{ 3251 static if(rs != dynamic) 3252 assert(this.rows == mat.rows); 3253 3254 static if(cs != dynamic) 3255 assert(this.cols == mat.cols); 3256 } 3257 body{ 3258 static if(is(typeof(this) == M)) 3259 this = mat; 3260 else 3261 { 3262 immutable rl = mat.rows, 3263 cl = mat.cols, 3264 n = rl * cl; 3265 3266 static if(is(typeof(_array) == T[])) 3267 { 3268 if(_array.length < n) 3269 _array.length = n; 3270 } 3271 else 3272 { 3273 immutable x = (major == Major.row) ? rl : cl, 3274 y = (major == Major.row) ? cl : rl; 3275 3276 if(_array.length < x) 3277 _array.length = x; 3278 3279 foreach(ref e; _array) 3280 if(e.length < y) 3281 e.length = y; 3282 } 3283 3284 static if(rs == dynamic) 3285 this.rows = rl; 3286 3287 static if(cs == dynamic) 3288 this.cols = cl; 3289 3290 foreach(i; 0 .. rl) 3291 foreach(j; 0 .. cl) 3292 { 3293 static if(rs == 1 || cs == 1) 3294 _array[i+j] = mat.at(i, j); 3295 else static if(major == Major.row) 3296 _array[i][j] = mat.at(i, j); 3297 else 3298 _array[j][i] = mat.at(i, j); 3299 } 3300 } 3301 } 3302 3303 3304 @property 3305 auto reference() inout 3306 { 3307 return this; 3308 } 3309 3310 3311 private: 3312 static if(rs == 1 || cs == 1) 3313 { 3314 T[] _array; 3315 } 3316 else 3317 { 3318 T[][] _array; 3319 } 3320 3321 static: 3322 T[] _buffer; 3323 } 3324 3325 unittest{ 3326 DMatrix!float m = 3327 [[1, 2, 3], 3328 [4, 5, 6]]; 3329 3330 assert(m.rows == 2); 3331 assert(m.cols == 3); 3332 3333 assert(m[0, 0] == 1); 3334 assert(m[0, 1] == 2); 3335 assert(m[0, 2] == 3); 3336 assert(m[1, 0] == 4); 3337 assert(m[1, 1] == 5); 3338 assert(m[1, 2] == 6); 3339 } 3340 3341 3342 DMatrix!(T, r, c, mjr) matrix(T, Msize_t r = dynamic, Msize_t c = dynamic, Major mjr = Major.row, size_t N)(size_t[N] size...) 3343 if(N == (r == dynamic) + (c == dynamic)) 3344 { 3345 typeof(return) dst; 3346 3347 static if(r != dynamic) 3348 immutable size_t rs = r; 3349 else 3350 immutable size_t rs = size[0]; 3351 3352 static if(c != dynamic) 3353 immutable size_t cs = c; 3354 else 3355 immutable size_t cs = size[$-1]; 3356 3357 immutable h = mjr == Major.row ? rs : cs, 3358 w = mjr == Major.row ? cs : rs; 3359 3360 static if(r == 1 || c == 1) 3361 dst._array = new T[h * w]; 3362 else 3363 dst._array = new T[][](h, w); 3364 3365 return dst; 3366 } 3367 3368 unittest{ 3369 foreach(major; TypeTuple!(Major.row, Major.column)) 3370 { 3371 auto mr23 = matrix!(int, 2, 4, major)(); 3372 static assert(isNarrowMatrix!(typeof(mr23))); 3373 static assert(hasStaticRows!(typeof(mr23))); 3374 static assert(hasStaticCols!(typeof(mr23))); 3375 static assert(mr23.rows == 2); 3376 static assert(mr23.cols == 4); 3377 3378 auto mr2_ = matrix!(int, 2, dynamic, major)(4); 3379 static assert(isNarrowMatrix!(typeof(mr2_))); 3380 static assert(hasStaticRows!(typeof(mr2_))); 3381 static assert(hasDynamicColumns!(typeof(mr2_))); 3382 static assert(mr2_.rows == 2); 3383 assert(mr2_.cols == 4); 3384 3385 auto mr_2 = matrix!(int, dynamic, 2, major)(4); 3386 static assert(isNarrowMatrix!(typeof(mr_2))); 3387 static assert(hasDynamicRows!(typeof(mr_2))); 3388 static assert(hasStaticCols!(typeof(mr_2))); 3389 assert(mr_2.rows == 4); 3390 static assert(mr_2.cols == 2); 3391 3392 auto mr__ = matrix!(int, dynamic, dynamic, major)(2, 4); 3393 static assert(isNarrowMatrix!(typeof(mr__))); 3394 static assert(hasDynamicRows!(typeof(mr__))); 3395 static assert(hasDynamicColumns!(typeof(mr__))); 3396 assert(mr__.rows == 2); 3397 assert(mr__.cols == 4); 3398 } 3399 } 3400 3401 3402 DMatrix!(T, r, c, mjr) matrix(size_t r, size_t c, Major mjr = Major.row, T)(T[] arr) 3403 if(r != 0 || c != 0) 3404 in{ 3405 static if(r != 0 && c != 0) 3406 assert(arr.length == r * c); 3407 else{ 3408 assert(!(arr.length % r + c)); 3409 } 3410 } 3411 body{ 3412 static if(r == 1 || c == 1) 3413 { 3414 typeof(return) dst; 3415 dst._array = arr; 3416 return dst; 3417 } 3418 else 3419 { 3420 immutable rs = r == 0 ? arr.length / c : r; 3421 immutable cs = c == 0 ? arr.length / r : c; 3422 3423 immutable h = mjr == Major.row ? rs : cs, 3424 w = mjr == Major.row ? cs : rs; 3425 3426 typeof(return) dst; 3427 dst._array.length = h; 3428 foreach(i; 0 .. h) 3429 dst._array[i] = arr[i * w .. (i+1) * w]; 3430 3431 return dst; 3432 } 3433 } 3434 3435 unittest{ 3436 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3437 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3438 3439 auto mr = matrix!(2, 3)([0, 1, 2, 3, 4, 5]); 3440 static assert(isNarrowMatrix!(typeof(mr))); 3441 static assert(hasStaticRows!(typeof(mr))); 3442 static assert(hasStaticCols!(typeof(mr))); 3443 static assert(mr.rows == 2); 3444 static assert(mr.cols == 3); 3445 assert(mr[0, 0] == 0); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3446 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3447 3448 mr[0, 0] = 10; 3449 assert(mr[0, 0] == 10); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3450 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3451 3452 mr = matrix!(2, 3, (i, j) => cast(int)((i*3+j)*2)); 3453 assert(mr[0, 0] == 0); assert(mr[0, 1] == 2); assert(mr[0, 2] == 4); 3454 assert(mr[1, 0] == 6); assert(mr[1, 1] == 8); assert(mr[1, 2] == 10); 3455 3456 auto mc = matrix!(2, 3, Major.column)([0, 1, 2, 3, 4, 5]); 3457 static assert(isNarrowMatrix!(typeof(mc))); 3458 static assert(hasStaticRows!(typeof(mc))); 3459 static assert(hasStaticCols!(typeof(mc))); 3460 static assert(mc.rows == 2); 3461 static assert(mc.cols == 3); 3462 assert(mc[0, 0] == 0); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3463 assert(mc[1, 0] == 1); assert(mc[1, 1] == 3); assert(mc[1, 2] == 5); 3464 3465 mc[0, 0] = 10; 3466 assert(mc[0, 0] == 10); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3467 assert(mc[1, 0] == 1); assert(mc[1, 1] == 3); assert(mc[1, 2] == 5); 3468 3469 mc = matrix!(2, 3, (i, j) => cast(int)((i*3+j)*2)); 3470 assert(mc[0, 0] == 0); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3471 assert(mc[1, 0] == 6); assert(mc[1, 1] == 8); assert(mc[1, 2] == 10); 3472 } 3473 3474 3475 DMatrix!(T, wild, wild, mjr) matrix(Major mjr = Major.row, T)(T[] arr, size_t r, size_t c) 3476 in{ 3477 if(r != 0 && c != 0) 3478 assert(arr.length == r * c); 3479 else if(r != 0 || c != 0) 3480 assert(!(arr.length % (r + c))); 3481 else 3482 assert(0); 3483 } 3484 body{ 3485 immutable rs = r == 0 ? arr.length / c : r; 3486 immutable cs = c == 0 ? arr.length / r : c; 3487 3488 immutable h = mjr == Major.row ? rs : cs, 3489 w = mjr == Major.row ? rs : cs; 3490 3491 typeof(return) dst; 3492 dst._array.length = h; 3493 foreach(i; 0 .. h) 3494 dst._array[i] = arr[i * w .. (i+1) * w]; 3495 3496 return dst; 3497 } 3498 3499 unittest{ 3500 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3501 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3502 3503 auto mr = matrix!(2, 3)([0, 1, 2, 3, 4, 5]); 3504 static assert(isNarrowMatrix!(typeof(mr))); 3505 static assert(hasStaticRows!(typeof(mr))); 3506 static assert(hasStaticCols!(typeof(mr))); 3507 static assert(mr.rows == 2); 3508 static assert(mr.cols == 3); 3509 assert(mr[0, 0] == 0); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3510 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3511 3512 3513 auto mc = matrix!(2, 3, Major.column)([0, 1, 2, 3, 4, 5]); 3514 static assert(isNarrowMatrix!(typeof(mc))); 3515 static assert(hasStaticRows!(typeof(mc))); 3516 static assert(hasStaticCols!(typeof(mc))); 3517 static assert(mc.rows == 2); 3518 static assert(mc.cols == 3); 3519 assert(mc[0, 0] == 0); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3520 assert(mc[1, 0] == 1); assert(mc[1, 1] == 3); assert(mc[1, 2] == 5); 3521 } 3522 3523 3524 auto matrix(Major mjr = Major.row, T, size_t N, size_t M)(ref T[M][N] arr) 3525 { 3526 static if(mjr == Major.row) 3527 DMatrix!(T, N, M, mjr) dst; 3528 else 3529 DMatrix!(T, M, N, mjr) dst; 3530 3531 dst._array.length = N; 3532 foreach(i; 0 .. N) 3533 dst._array[i] = arr[i]; 3534 return dst; 3535 } 3536 3537 unittest{ 3538 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3539 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3540 3541 int[3][2] arr = [[0, 1, 2], [3, 4, 5]]; 3542 3543 auto mr = matrix(arr); 3544 static assert(isNarrowMatrix!(typeof(mr))); 3545 static assert(hasStaticRows!(typeof(mr))); 3546 static assert(hasStaticCols!(typeof(mr))); 3547 static assert(mr.rows == 2); 3548 static assert(mr.cols == 3); 3549 assert(mr[0, 0] == 0); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3550 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3551 } 3552 3553 auto matrix(Major mjr = Major.row, A)(A mat) 3554 if(isNarrowMatrix!A && !isAbstractMatrix!A) 3555 { 3556 static if(hasStaticRows!A && hasStaticCols!A) 3557 DMatrix!(ElementType!A, A.rows, A.cols, mjr) dst; 3558 else static if(hasStaticRows!A) 3559 DMatrix!(ElementType!A, A.rows, wild, mjr) dst; 3560 else static if(hasStaticCols!A) 3561 DMatrix!(ElementType!A, wild, A.cols, mjr) dst; 3562 else 3563 DMatrix!(ElementType!A, wild, wild, mjr) dst; 3564 3565 dst.noAlias = mat; 3566 return dst; 3567 } 3568 3569 3570 auto matrix(Msize_t rs, Msize_t cs = 0, Major mjr = Major.row, A)(A mat) 3571 if(isAbstractMatrix!A && A.inferSize(rs, cs).isValid) 3572 { 3573 return mat.congeal!(rs, cs).matrix(); 3574 } 3575 3576 3577 auto matrix(Major mjr = Major.row, A)(A mat, size_t r, size_t c = 0) 3578 if(isAbstractMatrix!A) 3579 in{ 3580 assert(A.inferSize(r, c).isValid); 3581 } 3582 body{ 3583 return mat.congeal(r, c).matrix(); 3584 } 3585 3586 3587 /** 3588 要素がメモリ上に連続して存在するような行列 3589 */ 3590 struct SMatrix(T, Msize_t rs = 0, Msize_t cs = 0, Major mjr = Major.row) 3591 if(rs >= 0 && cs >= 0) 3592 { 3593 enum bool isColumnMajor = mjr == Major.column; 3594 enum bool isRowMajor = mjr == Major.row; 3595 enum major = mjr; 3596 enum size_t rows = rs; 3597 enum size_t cols = cs; 3598 3599 3600 this(M)(auto ref M mat) 3601 if(!is(M == typeof(this))) 3602 { 3603 static if(isNarrowMatrix!M) 3604 this.noAlias = mat; 3605 else 3606 this.opAssign(mat); 3607 } 3608 3609 3610 //@disable this(this); 3611 3612 3613 auto ref opIndex(size_t i, size_t j) inout 3614 in{ 3615 assert(i < rows); 3616 assert(j < cols); 3617 } 3618 body{ 3619 static if(major == Major.row) 3620 return _array[i * cols + j]; 3621 else 3622 return _array[j * rows + i]; 3623 } 3624 3625 3626 @property 3627 auto array() pure nothrow @safe inout 3628 { 3629 return _array[]; 3630 } 3631 3632 3633 void opAssign(M)(auto ref M mat) 3634 if(isValidOperator!(typeof(this), "=", M)) 3635 { 3636 auto buffer = { 3637 if(__ctfe) 3638 return new T[rs * cs]; 3639 else 3640 return SMatrix._buffer[]; 3641 }(); 3642 3643 foreach(i; 0 .. rows) 3644 foreach(j; 0 .. cols) 3645 { 3646 static if(major == Major.row) 3647 buffer[i * cols + j] = mat.at(i, j); 3648 else 3649 buffer[j * rows + i] = mat.at(i, j); 3650 } 3651 3652 _array[] = buffer[]; 3653 } 3654 3655 3656 @property 3657 void noAlias(M)(auto ref M mat) 3658 if(isValidOperator!(typeof(this), "=", M)) 3659 { 3660 foreach(i; 0 .. rows) 3661 foreach(j; 0 .. cols) 3662 { 3663 static if(major == Major.row) 3664 _array[i * cols + j] = mat[i, j]; 3665 else 3666 _array[j * rows + i] = mat[i, j]; 3667 } 3668 } 3669 3670 3671 //@property 3672 //auto stackRef() inout pure nothrow @safe 3673 //{ 3674 // //Issue: 9983 http://d.puremagic.com/issues/show_bug.cgi?id=9983 3675 // //return matrix!(rows, cols)(_array[]); 3676 // return _referenceImpl(this); 3677 //} 3678 3679 3680 //alias reference this; 3681 3682 mixin(ExpressionOperators!(ETOSpec.all & ~ETOSpec.opAssign, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 3683 //mixin(defaultExprOps!(false)); 3684 3685 3686 private: 3687 T[rs * cs] _array; 3688 3689 static: 3690 T[rs * cs] _buffer; 3691 3692 3693 //// Workaround of issue 9983 http://d.puremagic.com/issues/show_bug.cgi?id=9983 3694 //auto _referenceImpl(M)(ref M m) @trusted pure nothrow 3695 //{ 3696 // static if(is(M == immutable(M))) 3697 // return _referenceImplImmutable(cast(immutable(T)[])m._array[]); 3698 // else static if(is(M == const(M))) 3699 // return _referenceImplConst(cast(const(T)[])m._array[]); 3700 // else 3701 // return _referenceImplMutable(cast(T[])m._array[]); 3702 //} 3703 3704 3705 //auto _referenceImplMutable(E)(E[] arr) 3706 //{ 3707 // return arr.matrix!(rows, cols); 3708 //} 3709 3710 3711 //auto _referenceImplConst(E)(const E[] arr) 3712 //{ 3713 // return arr.matrix!(rows, cols); 3714 //} 3715 3716 3717 //auto _referenceImplImmutable(E)(immutable E[] arr) 3718 //{ 3719 // return arr.matrix!(rows, cols); 3720 //} 3721 } 3722 3723 unittest{ 3724 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3725 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3726 3727 3728 SMatrix!(int, 3, 3) m; 3729 m[0, 0] = 0; m[0, 1] = 1; m[0, 2] = 2; 3730 m[1, 0] = 1; m[1, 1] = 2; m[1, 2] = 3; 3731 m[2, 0] = 2; m[2, 1] = 3; m[2, 2] = 4; 3732 3733 auto mref = m.pref; 3734 3735 SMatrix!(int, 3, 3) m2 = mref * mref; 3736 mref = mref * mref; 3737 assert(mref == m2); 3738 } 3739 3740 unittest{ 3741 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3742 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3743 3744 3745 SMatrix!(int, 2, 2, Major.row) mr; // 2x2, int型, 行優先 3746 assert(mr.array.equal([0, 0, 0, 0])); // 初期化される 3747 3748 mr[0, 1] = 1; 3749 mr[1, 0] = 2; 3750 mr[1, 1] = 3; 3751 assert(mr.array.equal([0, 1, 2, 3])); // 行優先順 3752 3753 3754 SMatrix!(int, 2, 2, Major.column) mc; // 2x2, int型, 列優先 3755 assert(mc.array.equal([0, 0, 0, 0])); // 初期化される 3756 3757 mc[0, 1] = 1; 3758 mc[1, 0] = 2; 3759 mc[1, 1] = 3; 3760 assert(mc.array.equal([0, 2, 1, 3])); // 列優先順 3761 3762 3763 SMatrix!(int, 2, 2, Major.row) minit = mc; 3764 assert(minit.array.equal([0, 1, 2, 3])); // 全要素12で初期化されている 3765 } 3766 3767 unittest{ 3768 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3769 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3770 3771 3772 SMatrix!(int, 1, 3) m; 3773 m[0] = 3; 3774 assert(m[0] == 3); 3775 static assert(m.length == 3); 3776 assert(m[$-1] == 0); 3777 } 3778 3779 unittest{ 3780 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3781 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3782 3783 3784 SMatrix!(int, 2, 2) m; 3785 auto rm = m.pref; 3786 3787 assert(rm + rm == m.pref + m); 3788 assert(rm - rm == m.pref - m); 3789 assert(rm * rm == m.pref * m); 3790 3791 m = [[1, 2], [2, 3]]; 3792 3793 SMatrix!(int, 2, 2) m2 = m; 3794 m.noAlias = m2.pref + m2; 3795 assert(m[0, 0] == 2); 3796 } 3797 3798 3799 unittest{ 3800 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3801 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3802 3803 3804 alias SRVector!(int, 3) R; 3805 R rv1; 3806 static assert(rv1.rows == 1); 3807 static assert(rv1.cols == 3); 3808 static assert(rv1.length == 3); 3809 3810 rv1[0] = 3; 3811 assert(rv1[0] == 3); 3812 assert(rv1[1] == 0); 3813 assert(rv1[2] == 0); 3814 rv1 += 3; 3815 assert(rv1[0] == 6); 3816 assert(rv1[1] == 3); 3817 assert(rv1[2] == 3); 3818 rv1[0] += 3; 3819 assert(rv1[0] == 9); 3820 assert(rv1[1] == 3); 3821 assert(rv1[2] == 3); 3822 3823 SRVector!(int, 4) rv2; 3824 static assert(rv2.rows == 1); 3825 static assert(rv2.cols == 4); 3826 static assert(rv2.length == 4); 3827 3828 SCVector!(int, 3) cv1; 3829 static assert(cv1.rows == 3); 3830 static assert(cv1.cols == 1); 3831 static assert(cv1.length == 3); 3832 3833 SCVector!(int, 4) cv2; 3834 static assert(cv2.rows == 4); 3835 static assert(cv2.cols == 1); 3836 static assert(cv2.length == 4); 3837 } 3838 3839 3840 ///ditto 3841 template SRVector(T, Msize_t size) 3842 { 3843 alias SMatrix!(T, 1, size, Major.row) SRVector; 3844 } 3845 3846 ///ditto 3847 template SCVector(T, Msize_t size) 3848 { 3849 alias SMatrix!(T, size, 1, Major.column) SCVector; 3850 } 3851 3852 ///ditto 3853 template SVector(T, Msize_t size) 3854 { 3855 alias SCVector!(T, size) SVector; 3856 } 3857 3858 3859 /** 3860 転置行列を返します。 3861 */ 3862 @property 3863 auto transpose(A)(A mat) 3864 if(isNarrowMatrix!A) 3865 { 3866 static struct Transposed() 3867 { 3868 static if(isAbstractMatrix!A) 3869 { 3870 enum size_t rows = wild; 3871 enum size_t cols = wild; 3872 3873 static InferredResult inferSize(Msize_t rs, Msize_t cs) 3874 { 3875 return A.inferSize(cs, rs); 3876 } 3877 } 3878 else 3879 { 3880 static if(hasStaticCols!A) 3881 enum size_t rows = A.cols; 3882 else 3883 @property auto ref rows() const { return _mat.cols; } 3884 3885 static if(hasStaticRows!A) 3886 enum size_t cols = A.rows; 3887 else 3888 @property auto ref cols() const { return _mat.rows; } 3889 } 3890 3891 3892 auto ref opIndex(size_t i, size_t j) inout 3893 in{ 3894 assert(i < rows); 3895 assert(j < cols); 3896 } 3897 body{ 3898 return _mat[j, i]; 3899 } 3900 3901 3902 static if(carbon.linear.hasAssignableElements!A) 3903 { 3904 void opIndexAssign(E)(E e, size_t i, size_t j) 3905 in{ 3906 assert(i < rows); 3907 assert(j < cols); 3908 } 3909 body{ 3910 _mat[j, i] = e; 3911 } 3912 3913 static if(isVector!A) 3914 { 3915 void opIndexAssign(E)(E e, size_t i) 3916 in{ 3917 assert(i < this.length); 3918 } 3919 body{ 3920 static if(is(typeof({static assert(rows == 1);}))) 3921 this[0 , i] = e; 3922 else 3923 this[i, 0] = e; 3924 } 3925 } 3926 } 3927 3928 mixin(defaultExprOps!(isAbstractMatrix!A)); 3929 3930 3931 auto ref transpose() @property inout 3932 { 3933 return _mat; 3934 } 3935 3936 3937 private: 3938 A _mat; 3939 } 3940 3941 3942 return Transposed!()(mat); 3943 } 3944 unittest{ 3945 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3946 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3947 3948 3949 SMatrix!(int, 2, 2) m; 3950 m = [[0, 1], 3951 [2, 3]]; 3952 3953 auto t = m.pref.transpose; 3954 assert(t[0, 0] == 0); 3955 assert(t[0, 1] == 2); 3956 assert(t[1, 0] == 1); 3957 assert(t[1, 1] == 3); 3958 } 3959 unittest{ 3960 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3961 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3962 3963 3964 SCVector!(int, 3) v; 3965 v[0] = 1; v[1] = 2; v[2] = 3; 3966 3967 auto t = v.pref.transpose; 3968 t[0] = 1; 3969 t[1] = 2; 3970 t[2] = 3; 3971 //t = [1, 2, 3]; 3972 3973 assert(t.rows == 1); 3974 assert(t.cols == 3); 3975 3976 3977 static assert(is(typeof(v.pref) == typeof(t.transpose))); 3978 } 3979 3980 3981 /** 3982 エルミート行列を返します 3983 */ 3984 auto hermitian(A)(A mat) 3985 if(isNarrowMatrix!A) 3986 { 3987 static struct Hermitian() 3988 { 3989 static if(isAbstractMatrix!A) 3990 { 3991 enum size_t rows = wild; 3992 enum size_t cols = wild; 3993 3994 static InferredResult inferSize(Msize_t rs, Msize_t cs) 3995 { 3996 return A.inferSize(cs, rs); 3997 } 3998 } 3999 else 4000 { 4001 static if(hasStaticCols!A) 4002 enum size_t rows = A.cols; 4003 else 4004 @property auto ref rows() inout { return _mat.cols; } 4005 4006 static if(hasStaticRows!A) 4007 enum size_t cols = A.rows; 4008 else 4009 @property auto ref cols() inout { return _mat.rows; } 4010 } 4011 4012 4013 auto opIndex(size_t i, size_t j) inout 4014 in{ 4015 assert(i < rows); 4016 assert(j < cols); 4017 } 4018 body{ 4019 return _mat[j, i].conj; 4020 } 4021 4022 4023 mixin(defaultExprOps!(isAbstractMatrix!A)); 4024 4025 4026 auto ref hermitian() @property inout 4027 { 4028 return _mat; 4029 } 4030 4031 4032 private: 4033 A _mat; 4034 } 4035 4036 return Hermitian!()(mat); 4037 } 4038 unittest{ 4039 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4040 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4041 4042 4043 SMatrix!(cfloat, 2, 2) m; 4044 m = [[0+0i, 1+1i], 4045 [2+2i, 3+3i]]; 4046 4047 auto t = m.pref.hermitian; 4048 assert(t[0, 0] == 0-0i); 4049 assert(t[0, 1] == 2-2i); 4050 assert(t[1, 0] == 1-1i); 4051 assert(t[1, 1] == 3-3i); 4052 } 4053 4054 4055 4056 /** 4057 4058 */ 4059 auto toRange(A)(A mat) 4060 if(isNarrowMatrix!A) 4061 { 4062 static struct ToRange() 4063 { 4064 static struct Element() 4065 { 4066 @property auto ref front() { return _mat[_r, _cf]; } 4067 4068 @property auto ref back() { return _mat[_r, _cb]; } 4069 4070 auto ref opIndex(size_t i) { i += _cf; return _mat[_r, i]; } 4071 4072 void popFront() { ++_cf; } 4073 void popBack() { --_cb; } 4074 4075 @property bool empty() { return _cf == _cb; } 4076 @property size_t length() { return _cb - _cf; } 4077 alias opDollar = length; 4078 4079 @property auto save() { return this; } 4080 4081 auto opSlice() { return this.save; } 4082 auto opSlice(size_t i, size_t j) { return typeof(this)(_mat, _r, _cf + i, j); } 4083 4084 4085 private: 4086 A _mat; 4087 size_t _r; 4088 size_t _cf = 0; 4089 size_t _cb; 4090 } 4091 4092 4093 @property auto front() { return Element!()(this._mat, _rf, 0, this._mat.cols); } 4094 4095 @property auto back() { return Element!()(this._mat, _rb, 0, this._mat.cols); } 4096 4097 auto opIndex(size_t i) { i += _rf; return Element!()(this._mat, i, 0, this._mat.cols);} 4098 4099 void popFront() { ++_rf; } 4100 void popBack() { --_rb; } 4101 4102 @property bool empty() { return _rf == _rb; } 4103 @property size_t length() { return _rb - _rf; } 4104 alias opDollar = length; 4105 4106 @property auto save() { return this; } 4107 4108 auto opSlice() { return this.save; } 4109 auto opSlice(size_t i, size_t j) { return typeof(this)(_mat, _rf + i, j); } 4110 4111 private: 4112 A _mat; 4113 size_t _rf = 0; 4114 size_t _rb; 4115 } 4116 4117 4118 return ToRange!()(mat, 0, mat.rows); 4119 } 4120 4121 unittest{ 4122 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4123 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4124 4125 4126 SMatrix!(int, 3, 3) rm33; 4127 rm33[0, 0] = 1; rm33[0, 1] = 2; rm33[0, 2] = 3; 4128 assert(equal!"equal(a, b)"(rm33.pref.toRange, [[1, 2, 3], [0, 0, 0], [0, 0, 0]])); 4129 4130 SMatrix!(int, 1, 1) rm11; 4131 assert(equal!"equal(a, b)"(rm11.pref.toRange, [[0]])); 4132 } 4133 4134 4135 /** 4136 行列をレンジにします 4137 */ 4138 auto toFlatten(A)(A mat) 4139 if(isNarrowMatrix!A && !isAbstractMatrix!A) 4140 { 4141 alias ElementType!A E; 4142 4143 static struct ToFlatten() 4144 { 4145 @property 4146 auto ref front() 4147 { 4148 return _mat[_f / _mat.cols, _f % _mat.cols]; 4149 } 4150 4151 4152 @property 4153 auto ref back() 4154 { 4155 return _mat[_b / _mat.cols, _b % _mat.cols]; 4156 } 4157 4158 4159 auto ref opIndex(size_t i) inout 4160 in{ 4161 assert(_f + i < _b); 4162 }body{ 4163 i += _f; 4164 return _mat[i / _mat.cols, i % _mat.cols]; 4165 } 4166 4167 4168 static if(hasAssignableElements!A) 4169 { 4170 @property 4171 void front(E v) 4172 { 4173 _mat[_f / _mat.cols, _f % _mat.cols] = v; 4174 } 4175 4176 4177 @property 4178 void back(E v) 4179 { 4180 _mat[_b / _mat.cols, _b % _mat.cols] = v; 4181 } 4182 4183 4184 void opIndexAssign(E v, size_t i) 4185 in{ 4186 assert(_f + i < _b); 4187 } 4188 body{ 4189 i += _f; 4190 _mat[i / _mat.cols, i % _mat.cols] = v; 4191 } 4192 } 4193 4194 4195 @property 4196 bool empty() pure nothrow @safe const 4197 { 4198 return _f >= _b; 4199 } 4200 4201 4202 void popFront() pure nothrow @safe 4203 { 4204 _f++; 4205 } 4206 4207 4208 void popBack() pure nothrow @safe 4209 { 4210 _b--; 4211 } 4212 4213 4214 @property 4215 size_t length() pure nothrow @safe const 4216 { 4217 return _b - _f; 4218 } 4219 4220 4221 alias length opDollar; 4222 4223 4224 @property 4225 typeof(this) save() pure nothrow @safe 4226 { 4227 return this; 4228 } 4229 4230 4231 private: 4232 A _mat; 4233 size_t _f, _b; 4234 } 4235 4236 return ToFlatten!()(mat, 0, mat.rows * mat.cols); 4237 } 4238 unittest{ 4239 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4240 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4241 4242 4243 SMatrix!(int, 3, 3, Major.row) rm33; 4244 rm33[0, 0] = 1; rm33[0, 1] = 2; rm33[0, 2] = 3; 4245 //writeln(rm33.array.length;) 4246 assert(rm33.array == [1, 2, 3, 0, 0, 0, 0, 0, 0]); 4247 4248 alias Rt1 = typeof(toFlatten(rm33)); 4249 static assert(isRandomAccessRange!(Rt1)); 4250 static assert(std.range.hasLvalueElements!(Rt1)); 4251 static assert(std.range.hasAssignableElements!(Rt1)); 4252 static assert(hasLength!(Rt1)); 4253 assert(equal(rm33.toFlatten, rm33.array)); 4254 4255 SMatrix!(int, 3, 3, Major.column) cm33; 4256 cm33[0, 0] = 1; cm33[0, 1] = 2; cm33[0, 2] = 3; 4257 assert(cm33.array == [1, 0, 0, 2, 0, 0, 3, 0, 0]); 4258 assert(equal(cm33.toFlatten, [1, 2, 3, 0, 0, 0, 0, 0, 0])); 4259 } 4260 4261 4262 4263 /** 4264 レンジから行列を作ります。 4265 */ 4266 auto toMatrix(Msize_t rs, Msize_t cs, Major mjr = Major.row, R)(R range) 4267 if(isRandomAccessRange!R && isNotVectorOrMatrix!(Unqual!(std.range.ElementType!R)) && (mjr == Major.row ? (cs != wild) : (rs != wild))) 4268 { 4269 //static if(mjr == Major.column) 4270 //return range.toMatrix!(cs, rs, Major.row).transpose; 4271 //else 4272 //{ 4273 alias E = Unqual!(std.range.ElementType!R); 4274 4275 static struct ToMatrix() 4276 { 4277 static if(rs != wild) 4278 enum size_t rows = rs; 4279 else 4280 auto ref rows() const @property 4281 { 4282 return _range.length / typeof(this).cols; 4283 } 4284 4285 static if(cs != wild) 4286 enum size_t cols = cs; 4287 else 4288 auto ref cols() const @property 4289 { 4290 return _range.length / typeof(this).rows; 4291 } 4292 4293 4294 auto ref opIndex(size_t i, size_t j) inout 4295 in{ 4296 assert(i < rows || rows == 0); 4297 assert(j < cols || cols == 0); 4298 4299 static if(hasLength!R && mjr == Major.row) 4300 assert(i * cols + j < this._range.length); 4301 4302 static if(hasLength!R && mjr == Major.column) 4303 assert(j * rows + i < this._range.length); 4304 } 4305 body{ 4306 static if(mjr == Major.row) 4307 return this._range[i * cols + j]; 4308 else 4309 return this._range[j * rows + i]; 4310 } 4311 4312 4313 mixin(defaultExprOps!(false)); 4314 4315 private: 4316 R _range; 4317 } 4318 4319 return ToMatrix!()(range); 4320 //} 4321 } 4322 4323 unittest{ 4324 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4325 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4326 4327 4328 auto r = iota(4); 4329 auto mr = toMatrix!(2, 2)(r); 4330 assert(mr.toFlatten.equal([0, 1, 2, 3])); 4331 4332 auto mc = r.toMatrix!(2, 2, Major.column); 4333 assert(mc.toFlatten.equal([0, 2, 1, 3])); 4334 } 4335 unittest{ 4336 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4337 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4338 4339 4340 auto mem = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 4341 auto mr = mem.toMatrix!(3, 3); 4342 static assert(isNarrowMatrix!(typeof(mr))); 4343 static assert(hasLvalueElements!(typeof(mr))); 4344 assert(mr.toFlatten.equal(mem[0 .. 9])); 4345 4346 mem.length = 16; 4347 auto mc = mem.toMatrix!(4, 4, Major.column); 4348 static assert(isNarrowMatrix!(typeof(mr))); 4349 static assert(hasLvalueElements!(typeof(mr))); 4350 mc[3, 3] = 15; 4351 assert(mc.transpose.toFlatten.equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4352 0, 0, 0, 0, 0, 15])); 4353 } 4354 unittest{ 4355 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4356 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4357 4358 4359 auto mem = [0, 1, 2, 3]; 4360 auto r1 = mem.toMatrix!(wild, 1, Major.row); 4361 assert(equal(r1.toFlatten, mem[0 .. 4])); 4362 4363 mem ~= [4, 5]; 4364 auto r2 = mem.toMatrix!(wild, 2, Major.row); 4365 assert(r2[2, 0] == 4); 4366 assert(r2[2, 1] == 5); 4367 4368 auto c1 = mem.toMatrix!(1, wild, Major.column); 4369 assert(equal(c1.toFlatten, mem)); 4370 } 4371 4372 4373 4374 auto toMatrix(Msize_t rs, Msize_t cs, Major mjr = Major.row, R)(R range) 4375 if(isRandomAccessRange!R && isRandomAccessRange!(Unqual!(std.range.ElementType!R)) && isNotVectorOrMatrix!(Unqual!(std.range.ElementType!(Unqual!(std.range.ElementType!R))))) 4376 { 4377 static struct ToMatrix() 4378 { 4379 static if(rs != dynamic) 4380 enum size_t rows = rs; 4381 else 4382 auto ref rows() const @property 4383 { 4384 static if(mjr == Major.row) 4385 return _range.length; 4386 else 4387 return _range[0].length; 4388 } 4389 4390 4391 static if(cs != dynamic) 4392 enum size_t cols = cs; 4393 else 4394 auto ref cols() const @property 4395 { 4396 static if(mjr == Major.column) 4397 return _range.length; 4398 else 4399 return _range[0].length; 4400 } 4401 4402 4403 auto ref opIndex(size_t i, size_t j) inout 4404 in{ 4405 assert(i < rows || rows == 0); 4406 assert(j < cols || cols == 0); 4407 4408 static if(hasLength!R) 4409 assert((mjr == Major.row ? i : j) < _range.length); 4410 4411 static if(hasLength!(Unqual!(std.range.ElementType!R))) 4412 assert((mjr == Major.row ? j : i) < _range[i].length); 4413 } 4414 body{ 4415 static if(mjr == Major.row) 4416 return _range[i][j]; 4417 else 4418 return _range[j][i]; 4419 } 4420 4421 mixin(defaultExprOps!(false)); 4422 4423 private: 4424 R _range; 4425 } 4426 4427 return ToMatrix!()(range); 4428 } 4429 unittest{ 4430 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4431 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4432 4433 4434 auto arr = [[0, 1], [2, 3], [4, 5]]; 4435 auto r1 = toMatrix!(3, 2, Major.row)(arr); 4436 static assert(isNarrowMatrix!(typeof(r1))); 4437 assert(equal!"equal(a, b)"(r1.toRange, arr)); 4438 4439 auto r2 = arr.toMatrix!(1, 1, Major.row); 4440 assert(r2[0] == 0); 4441 4442 auto r3 = arr.toMatrix!(dynamic, 2, Major.row); 4443 assert(r3.congeal!(3, 2) == r1); 4444 4445 auto r4 = arr.toMatrix!(2, dynamic, Major.row); 4446 assert(equal(r4.congeal!(2, 2)().toFlatten(), [0, 1, 2, 3])); 4447 4448 auto r5 = arr.toMatrix!(dynamic, dynamic, Major.row); 4449 assert(r5 == r1); 4450 } 4451 unittest{ 4452 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4453 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4454 4455 4456 auto arr = [[0, 1], [2, 3], [4, 5]]; 4457 auto r1 = arr.toMatrix!(2, 3, Major.column); 4458 assert(equal!"equal(a, b)"(r1.transpose.toRange, arr)); 4459 assert(r1[0, 0] == 0); assert(r1[0, 1] == 2); assert(r1[0, 2] == 4); 4460 assert(r1[1, 0] == 1); assert(r1[1, 1] == 3); assert(r1[1, 2] == 5); 4461 4462 auto r2 = arr.toMatrix!(1, 1, Major.column); 4463 assert(r2[0] == 0); 4464 4465 auto r3 = arr.toMatrix!(dynamic, 3, Major.column); 4466 assert(r3 == r1); 4467 4468 auto r4 = arr.toMatrix!(2, dynamic, Major.column); 4469 assert(equal(r4.transpose.toFlatten, [0, 1, 2, 3, 4, 5])); 4470 4471 auto r5 = arr.toMatrix!(dynamic, dynamic, Major.column); 4472 assert(r5 == r1); 4473 } 4474 4475 4476 /** 4477 単位行列 4478 */ 4479 @property 4480 auto identity(E)()if(isNotVectorOrMatrix!E) 4481 { 4482 static struct Identity() 4483 { 4484 4485 4486 static InferredResult inferSize(Msize_t i, Msize_t j) 4487 { 4488 if(i < 0 && j < 0) 4489 return InferredResult(false); 4490 else if(i < 0 || j < 0) 4491 return InferredResult(true, max(i, j), max(i, j)); 4492 else if(i == j) 4493 return InferredResult(true, i, j); 4494 else 4495 return InferredResult(false); 4496 } 4497 4498 4499 E opIndex(size_t i, size_t j) inout 4500 { 4501 return (i == j) ? (cast(E)1) : (cast(E)0); 4502 } 4503 4504 mixin(defaultExprOps!(true)); 4505 } 4506 4507 return Identity!()(); 4508 } 4509 unittest{ 4510 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4511 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4512 4513 4514 auto id = identity!int; 4515 static assert(isAbstractMatrix!(typeof(id))); 4516 static assert(typeof(id).inferSize(4, 4).isValid); 4517 static assert(typeof(id).inferSize(wild, 2).isValid); 4518 static assert(!typeof(id).inferSize(1, 3).isValid); 4519 4520 auto m1 = SMatrix!(int, 2, 2).init; 4521 m1.array[] = [0, 1, 2, 3]; 4522 assert(equal((m1.pref * id).toFlatten, [0, 1, 2, 3])); 4523 4524 auto id2 = id + id; 4525 static assert(isAbstractMatrix!(typeof(id2))); 4526 static assert(typeof(id2).inferSize(4, 4).isValid); 4527 4528 4529 auto id22 = id.congeal!(wild, 2); 4530 static assert(id22.rows == 2); 4531 static assert(id22.cols == 2); 4532 assert(equal(id22.toFlatten, [1, 0, 0, 1])); 4533 4534 auto id3 = id22 * id; 4535 static assert(id3.rows == 2); 4536 static assert(id3.cols == 2); 4537 assert(equal(id3.toFlatten, [1, 0, 0, 1])); 4538 4539 auto ins = id2.congeal!(2, 2); 4540 static assert(isNarrowMatrix!(typeof(ins))); 4541 assert(equal(ins.toFlatten, [2, 0, 0, 2])); 4542 } 4543 4544 4545 /** 4546 全要素が1な行列を返します。 4547 */ 4548 @property 4549 auto ones(E)()if(isNotVectorOrMatrix!E) 4550 { 4551 static struct Ones() 4552 { 4553 4554 4555 E opIndex(size_t i, size_t j) inout 4556 { 4557 return cast(E)1; 4558 } 4559 4560 4561 static InferredResult inferSize(Msize_t i, Msize_t j) 4562 { 4563 if(i == wild && j == wild) 4564 return InferredResult(false); 4565 else if(i == wild || j == wild) 4566 return InferredResult(true, max(i, j), max(i, j)); 4567 else 4568 return InferredResult(true, i, j); 4569 } 4570 4571 4572 mixin(defaultExprOps!(true)); 4573 } 4574 4575 static assert(isAbstractMatrix!(Ones!())); 4576 4577 return Ones!().init; 4578 } 4579 unittest{ 4580 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4581 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4582 4583 4584 auto m1 = ones!float; 4585 assert(m1[0, 1] == 1); 4586 4587 auto m3 = m1 * 3; 4588 assert(m3[0, 1] == 3); 4589 4590 auto m9 = m3 * 3; 4591 assert(m9[0, 1] == 9); 4592 } 4593 4594 4595 /** 4596 部分行列を返します 4597 */ 4598 auto sub(alias rArray, alias cArray, A)(A mat) 4599 if(isArray!(typeof(rArray)) && isArray!(typeof(cArray)) && isNarrowMatrix!A && !isAbstractMatrix!A 4600 && (hasDynamicRows!A || is(typeof({static assert(rArray.find!"a>=b"(A.rows).empty);}))) 4601 && (hasDynamicColumns!A || is(typeof({static assert(cArray.find!"a>=b"(A.cols).empty);})))) 4602 in{ 4603 static if(hasDynamicRows!A) 4604 assert(rArray.find!"a>=b"(mat.rows).empty); 4605 static if(hasDynamicColumns!A) 4606 assert(cArray.find!"a>=b"(mat.cols).empty); 4607 } 4608 body{ 4609 static if(rArray.length == 0 && cArray.length == 0) 4610 return mat; 4611 else 4612 { 4613 static struct Sub() 4614 { 4615 static if(rArray.length == 0) 4616 alias rows = _mat.rows; 4617 else 4618 enum rows = rArray.length; 4619 4620 static if(cArray.length == 0) 4621 alias cols = _mat.cols; 4622 else 4623 enum cols = cArray.length; 4624 4625 4626 auto ref opIndex(size_t i, size_t j) inout 4627 in{ 4628 assert(i < rows); 4629 assert(j < cols); 4630 } 4631 body{ 4632 static if(rArray.length && cArray.length) 4633 return _mat[rArray[i], cArray[j]]; 4634 else static if(rArray.length) 4635 return _mat[rArray[i], j]; 4636 else static if(cArray.length) 4637 return _mat[i, cArray[j]]; 4638 else 4639 static assert(0); 4640 } 4641 4642 4643 mixin(defaultExprOps!(false)); 4644 4645 4646 private: 4647 A _mat; 4648 } 4649 4650 return Sub!()(mat); 4651 } 4652 } 4653 unittest{ 4654 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4655 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4656 4657 4658 auto m1 = [[0, 1], [2, 3], [4, 5]].toMatrix!(3, 2, Major.row); 4659 auto s1 = m1.sub!([1, 2], [0]); 4660 static assert(s1.rows == 2); 4661 static assert(s1.cols == 1); 4662 4663 assert(s1[0, 0] == 2); 4664 assert(s1[1, 0] == 4); 4665 4666 4667 auto m2 = [[0, 1], [2, 3], [4, 5]].toMatrix!(dynamic, dynamic, Major.row)(); 4668 auto s2 = sub!((size_t[]).init, (size_t[]).init)(m2); 4669 assert(m1 == s2.congeal!(3, 2)); 4670 4671 4672 auto m3 = identity!int.congeal!(2, 2)().sub!([0, 0, 0], [0, 0, 0])(); 4673 assert(m3 == ones!int); 4674 } 4675 4676 4677 /** 4678 swizzle : glm参照 4679 */ 4680 auto swizzle(A)(A mat) @property 4681 if(isNarrowMatrix!A && !isAbstractMatrix!A) 4682 { 4683 static struct Swizzle() 4684 { 4685 auto opDispatch(string exp)() 4686 if((isVector!A ? (isSwizzleExp(exp) && isSwizzlable!A(exp)) : false) || (isSliceExp(exp) && isSlicable!(A, exp))) 4687 { 4688 static struct SwizzleResult() 4689 { 4690 static if(isSwizzleExp(exp)) 4691 { 4692 static if(A.cols == 1) 4693 { 4694 enum size_t rows = exp.length; 4695 enum size_t cols = 1; 4696 } 4697 else 4698 { 4699 enum size_t rows = 1; 4700 enum size_t cols = exp.length; 4701 } 4702 4703 4704 auto ref opIndex(size_t i, size_t j) inout 4705 in{ 4706 assert((i+j) < (rows+cols-1)); 4707 } 4708 body{ 4709 immutable size_t s = swizzleType(exp) == 'a' ? exp[i] - 'a' : (exp[i] == 'w' ? 3 : (exp[i] - 'x')); 4710 4711 static if(cols == 1) 4712 return _mat[s, 0]; 4713 else 4714 return _mat[0, s]; 4715 } 4716 } 4717 else // isSliceExp 4718 { 4719 private enum _swizzleExpSpec = spec(exp); 4720 enum size_t rows = mixin(_swizzleExpSpec.cs); 4721 enum size_t cols = mixin(_swizzleExpSpec.rs); 4722 4723 4724 auto ref opIndex(size_t i, size_t j) inout 4725 in{ 4726 assert(i < rows); 4727 assert(j < cols); 4728 } 4729 body{ 4730 immutable size_t r = mixin(_swizzleExpSpec.rb) + i, 4731 c = mixin(_swizzleExpSpec.cb) + j; 4732 4733 return _mat[r, c]; 4734 } 4735 } 4736 4737 4738 mixin(defaultExprOps!(false)); 4739 4740 private: 4741 A _mat; 4742 } 4743 4744 4745 return SwizzleResult!()(_mat); 4746 } 4747 4748 private: 4749 A _mat; 4750 4751 static: 4752 4753 bool isSwizzleExp(string str) pure nothrow @safe 4754 { 4755 char d; 4756 foreach(c; str) 4757 if(!(('a' <= c && c <= 'h') || (c == 'x' || c == 'y' || c == 'z' || c == 'w'))) 4758 return false; 4759 else if('a' <= c && c <= 'h'){ 4760 if(d == char.init) 4761 d = 'a'; 4762 else if(d != 'a') 4763 return false; 4764 }else{ 4765 if(d == char.init) 4766 d = 'x'; 4767 else if(d != 'x') 4768 return false; 4769 } 4770 4771 return true; 4772 } 4773 unittest{ 4774 assert(isSwizzleExp("aaaaaaa")); 4775 assert(isSwizzleExp("aaaahaa")); 4776 assert(!isSwizzleExp("aaaaiaa")); 4777 assert(!isSwizzleExp("aaxa")); 4778 assert(isSwizzleExp("xxxx")); 4779 assert(isSwizzleExp("xyzw")); 4780 assert(!isSwizzleExp("xyza")); 4781 } 4782 4783 4784 // pure nothrow @safeにするため 4785 string find_(string str, char c) pure nothrow @safe 4786 { 4787 while(str.length && str[0] != c) 4788 str = str[1 .. $]; 4789 return str; 4790 } 4791 4792 4793 string until_(string str, char c) pure nothrow @safe 4794 { 4795 foreach(i; 0 .. str.length) 4796 if(str[i] == c) 4797 return str[0 .. i]; 4798 4799 return str; 4800 } 4801 4802 4803 // for matrix, format: r<bias>c<bias>m<row>x<cols> 4804 alias ExpSpec = Tuple!(string, "rb", string, "cb", string, "rs", string, "cs"); 4805 4806 4807 ExpSpec spec(string exp) pure nothrow @safe 4808 { 4809 ExpSpec spec = ExpSpec("0", "0", "", ""); 4810 { 4811 auto t = until_(until_(find_(exp, 'r'), 'c'), 'm'); 4812 if(t.length > 1) //r<Num>の形式なので、rを加えて1文字以上無いといけない 4813 spec.rb = t[1 .. $]; 4814 } 4815 4816 { 4817 auto t = until_(find_(exp, 'c'), 'm'); 4818 if(t.length > 1) //c<Num>の形式なので、rを加えて1文字以上無いといけない 4819 spec.cb = t[1 .. $]; 4820 } 4821 4822 { 4823 auto t = until_(find_(exp, 'm'), 'x'); 4824 if(t.length > 1) //m<Num>の形式なので、mを加えて1文字以上無いといけない 4825 spec.rs = t[1 .. $]; 4826 } 4827 4828 { 4829 auto t = find_(exp, 'x'); 4830 if(t.length > 1) 4831 spec.cs = t[1 .. $]; 4832 } 4833 return spec; 4834 } 4835 unittest{ 4836 assert(spec("r1c1m1x1") == tuple("1", "1", "1", "1")); 4837 assert(spec("r1_100c21m5x5") == tuple("1_100", "21", "5", "5")); 4838 assert(spec("m1x2") == tuple("0", "0", "1", "2")); 4839 } 4840 4841 4842 bool isSliceExp(string exp) pure nothrow @safe 4843 { 4844 bool isAllDigit(string str) pure nothrow @safe 4845 { 4846 if(str.length == 0) 4847 return false; 4848 4849 foreach(c; str) 4850 if(!(c.isDigit || c == '_')) 4851 return false; 4852 return true; 4853 } 4854 4855 auto sp = spec(exp); 4856 return (exp[0] == 'm' || exp[0] == 'r' || exp[0] == 'c') && isAllDigit(sp.rb) && isAllDigit(sp.cb) && isAllDigit(sp.rs) && isAllDigit(sp.cs); 4857 } 4858 unittest{ 4859 assert(isSliceExp("r1c1m1x1")); 4860 assert(isSliceExp("r1_100c21m5x5")); 4861 assert(isSliceExp("m1x2")); 4862 assert(!isSliceExp("m2m1")); 4863 assert(!isSliceExp("_2mx1")); 4864 assert(isSliceExp("c1m1x1")); 4865 } 4866 4867 4868 // for vec 4869 char swizzleType(string exp) pure nothrow @safe 4870 { 4871 if('a' <= exp[0] && exp[0] <= 'h') 4872 return 'a'; 4873 else 4874 return 'x'; 4875 } 4876 unittest{ 4877 assert(swizzleType("aaaaaaa") == 'a'); 4878 assert(swizzleType("aaaahaa") == 'a'); 4879 assert(swizzleType("xxxx") == 'x'); 4880 assert(swizzleType("xyzv") == 'x'); 4881 } 4882 4883 4884 bool isSwizzlable(T)(string exp) pure nothrow @safe 4885 { 4886 static if(isVector!T){ 4887 enum size = T.length - 1; 4888 4889 if(swizzleType(exp) == 'a'){ 4890 foreach(c; exp) 4891 if(c > 'a' + size) 4892 return false; 4893 return true; 4894 }else{ 4895 foreach(c; exp) 4896 if(c != 'x' && c != 'y' && c != 'z' && c != 'w') 4897 return false; 4898 return true; 4899 } 4900 } 4901 else 4902 return false; 4903 } 4904 unittest{ 4905 alias V3 = SMatrix!(int, 1, 3); 4906 assert(isSwizzlable!V3("aaa")); 4907 assert(isSwizzlable!V3("abc")); 4908 assert(!isSwizzlable!V3("abd")); 4909 assert(isSwizzlable!V3("xyz")); 4910 assert(!isSwizzlable!V3("xyzv")); 4911 4912 alias V4 = SMatrix!(int, 1, 4); 4913 assert(isSwizzlable!V4("xyzw")); 4914 assert(!isSwizzlable!V4("xyzv")); 4915 } 4916 4917 4918 bool isSlicable(T, string exp)() pure nothrow @safe 4919 { 4920 static if(isSliceExp(exp)){ 4921 enum sp = spec(exp); 4922 return ((mixin(sp.rs) + mixin(sp.rb)) <= T.cols) && ((mixin(sp.cs) + mixin(sp.cb)) <= T.rows); 4923 } 4924 else 4925 return false; 4926 } 4927 unittest{ 4928 alias M33 = SMatrix!(int, 3, 3); 4929 assert(isSlicable!(M33, "m3x3")); 4930 assert(isSlicable!(M33, "m1x3")); 4931 assert(isSlicable!(M33, "r1m2x3")); 4932 assert(isSlicable!(M33, "c1m2x2")); 4933 } 4934 } 4935 4936 4937 return Swizzle!()(mat); 4938 } 4939 4940 unittest{ 4941 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4942 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4943 4944 4945 auto org = matrix!((i, j) => i * 3 + j)(); 4946 SMatrix!(size_t, 4, 4) a = org; 4947 auto m = a.swizzle.m2x2; 4948 static assert(isNarrowMatrix!(typeof(m))); 4949 assert(m == org); 4950 4951 4952 auto m2 = a.swizzle.r1c1m2x2; 4953 static assert(isNarrowMatrix!(typeof(m2))); 4954 assert(m2 == matrix!((i, j) => (i+1)*3 + j + 1)()); 4955 4956 4957 assert(a.swizzle.m1x4.swizzle.xyzw == a.swizzle.m1x4); 4958 } 4959 4960 4961 /** 4962 行ベクトルのランダムアクセスレンジ 4963 */ 4964 auto rowVectors(A)(A mat) @property 4965 if(isNarrowMatrix!A && !isAbstractMatrix!A) 4966 { 4967 static struct RowVectors() 4968 { 4969 auto front() @property { return this.opIndex(0); } 4970 auto back() @property { return this.opIndex(_end-1); } 4971 void popFront() @property { ++_idx; } 4972 void popBack() @property { --_end; } 4973 bool empty() @property const { return _idx == _end; } 4974 auto save() @property { return this; } 4975 4976 4977 4978 auto opIndex(size_t i) 4979 { 4980 static struct RowVectorByIndex() 4981 { 4982 alias rows = _m.rows; 4983 enum cols = 1; 4984 4985 4986 auto ref opIndex(size_t i, size_t j) inout 4987 in{ 4988 assert(i < rows); 4989 assert(j < cols); 4990 } 4991 body{ 4992 return _m[i, _idx]; 4993 } 4994 4995 4996 mixin(defaultExprOps!(false)); 4997 4998 4999 size_t _idx; 5000 A _m; 5001 } 5002 5003 5004 return RowVectorByIndex!()(i + _idx, _m); 5005 } 5006 5007 5008 size_t length() const @property 5009 { 5010 return _end - _idx; 5011 } 5012 5013 5014 alias opDispatch = length; 5015 5016 private: 5017 size_t _idx; 5018 size_t _end; 5019 A _m; 5020 } 5021 5022 5023 return RowVectors!()(0, mat.cols, mat); 5024 } 5025 unittest 5026 { 5027 real[3][3] mStack = [[1, 2, 2], 5028 [2, 1, 1], 5029 [2, 2, 2]]; 5030 5031 auto r = matrix(mStack).rowVectors; 5032 5033 static assert(isRandomAccessRange!(typeof(r))); 5034 5035 foreach(i; 0 .. 3) 5036 foreach(j; 0 .. 3) 5037 assert(r[i][j] == mStack[j][i]); 5038 } 5039 5040 5041 /** 5042 行ベクトルのランダムアクセスレンジ 5043 */ 5044 auto columnVectors(A)(A mat) @property 5045 if(isNarrowMatrix!A && !isAbstractMatrix!A) 5046 { 5047 static struct ColumnVectors() 5048 { 5049 auto front() @property { return this.opIndex(0); } 5050 auto back() @property { return this.opIndex(_end-1); } 5051 void popFront() @property { ++_idx; } 5052 void popBack() @property { --_end; } 5053 bool empty() @property const { return _idx == _end; } 5054 auto save() @property { return this; } 5055 5056 5057 5058 auto opIndex(size_t i) 5059 { 5060 static struct ColumnVectorByIndex() 5061 { 5062 enum rows = 1; 5063 alias cols = _m.cols; 5064 5065 5066 auto ref opIndex(size_t i, size_t j) inout 5067 in{ 5068 assert(i < rows); 5069 assert(j < cols); 5070 } 5071 body{ 5072 return _m[_idx, j]; 5073 } 5074 5075 5076 mixin(defaultExprOps!(false)); 5077 5078 5079 size_t _idx; 5080 A _m; 5081 } 5082 5083 return ColumnVectorByIndex!()(i + _idx, _m); 5084 } 5085 5086 5087 size_t length() const @property 5088 { 5089 return _end - _idx; 5090 } 5091 5092 5093 alias opDispatch = length; 5094 5095 private: 5096 size_t _idx; 5097 size_t _end; 5098 A _m; 5099 } 5100 5101 5102 return ColumnVectors!()(0, mat.rows, mat); 5103 } 5104 unittest 5105 { 5106 real[3][3] mStack = [[1, 2, 2], 5107 [2, 1, 1], 5108 [2, 2, 2]]; 5109 5110 auto r = matrix(mStack).columnVectors; 5111 5112 static assert(isRandomAccessRange!(typeof(r))); 5113 5114 foreach(i; 0 .. 3) 5115 foreach(j; 0 .. 3) 5116 assert(r[i][j] == mStack[i][j]); 5117 } 5118 5119 5120 /** 5121 行列の跡(trace)を返します。正方行列についてのみ定義されます 5122 */ 5123 ElementType!A trace(A)(A mat) 5124 if(isNarrowMatrix!A && !isAbstractMatrix!A && (!(hasStaticRows!A && hasStaticCols!A) || is(typeof({static assert(A.rows == A.cols);})))) 5125 { 5126 alias ElementType!A T; 5127 T sum = cast(T)0; 5128 5129 foreach(i; 0 .. mat.rows) 5130 sum += mat[i, i]; 5131 5132 return sum; 5133 } 5134 unittest{ 5135 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5136 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5137 5138 5139 auto m = SMatrix!(int, 2, 2)(); 5140 m[0, 0] = 0; m[0, 1] = 1; 5141 m[1, 0] = 2; m[1, 1] = 3; 5142 5143 auto tr = m.trace; 5144 assert(tr == 3); 5145 } 5146 5147 5148 auto dot(V1, V2)(V1 vec1, V2 vec2) 5149 if(isVector!V1 && isVector!V2 && (!(hasStaticLength!V1 && hasStaticLength!V2) || is(typeof({static assert(V1.length == V2.length);})))) 5150 in{ 5151 static if(!(hasStaticLength!V1 && hasStaticLength!V2)) 5152 { 5153 assert(vec1.length == vec2.length); 5154 } 5155 } 5156 body{ 5157 alias Unqual!(typeof(ElementType!V1.init * ElementType!V2.init)) T; 5158 T sum = cast(T)0; 5159 5160 foreach(i; 0 .. vec1.length){ 5161 sum += vec1[i] * vec2[i]; 5162 } 5163 5164 return sum; 5165 } 5166 unittest{ 5167 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5168 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5169 5170 5171 auto rv = SRVector!(int, 3)(), 5172 cv = SCVector!(int, 3)(); 5173 5174 rv.array[] = [0, 1, 2]; 5175 cv.array[] = [1, 2, 3]; 5176 5177 assert((rv.pref * cv)[0] == 8); //8 5178 assert(rv.dot(cv) == 8); 5179 assert(cv.dot(rv) == 8); 5180 5181 assert(rv.dot(rv) == 5); 5182 assert(cv.dot(cv) == 14); 5183 5184 5185 assert(approxEqual((rv.pref * 0.5).dot(rv), 2.5)); 5186 } 5187 5188 5189 /** 5190 ベクトル同士のクロス積 5191 */ 5192 auto cross(Major mjr = Major.column, V1, V2)(V1 vec1, V2 vec2) 5193 if(isVector!V1 && isVector!V2 && (hasStaticLength!V1 && is(typeof({static assert(V1.length == 3);}))) 5194 && (hasStaticLength!V2 && is(typeof({static assert(V2.length == 3);})))) 5195 in{ 5196 assert(vec1.length == 3); 5197 assert(vec2.length == 3); 5198 } 5199 body{ 5200 static struct CrossResult 5201 { 5202 enum rows = mjr == Major.row ? 1 : 3; 5203 enum cols = mjr == Major.row ? 3 : 1; 5204 5205 5206 auto opIndex(size_t i, size_t j) const 5207 in{ 5208 assert(i < rows); 5209 assert(j < cols); 5210 } 5211 body{ 5212 switch(i + j){ 5213 case 0: return _v1[1] * _v2[2] - _v1[2] * _v2[1]; 5214 case 1: return _v1[2] * _v2[0] - _v1[0] * _v2[2]; 5215 case 2: return _v1[0] * _v2[1] - _v1[1] * _v2[0]; 5216 default: assert(0); 5217 } 5218 } 5219 5220 mixin(defaultExprOps!(false)); 5221 5222 private: 5223 V1 _v1; 5224 V2 _v2; 5225 } 5226 5227 return CrossResult(vec1, vec2); 5228 } 5229 unittest{ 5230 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5231 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5232 5233 5234 auto rv = SVector!(int, 3)(), 5235 cv = SVector!(int, 3)(); 5236 5237 rv.array[] = [0, 1, 2]; 5238 cv.array[] = [1, 2, 3]; 5239 5240 auto cp = rv.cross(cv); 5241 5242 static assert(isVector!(typeof(cp))); 5243 static assert(hasStaticLength!(typeof(cp))); 5244 static assert(hasStaticRows!(typeof(cp))); 5245 static assert(hasStaticCols!(typeof(cp))); 5246 5247 assert(cp[0] == -1); 5248 assert(cp[1] == 2); 5249 assert(cp[2] == -1); 5250 } 5251 5252 5253 /** 5254 直積 5255 */ 5256 auto cartesian(V1, V2)(V1 vec1, V2 vec2) 5257 if(isVector!V1 && isVector!V2) 5258 { 5259 static struct Cartesian() 5260 { 5261 alias rows = _vec1.length; 5262 alias cols = _vec2.length; 5263 5264 5265 auto opIndex(size_t i, size_t j) const 5266 in{ 5267 assert(i < rows); 5268 assert(j < cols); 5269 } 5270 body{ 5271 return _vec1[i] * _vec2[j]; 5272 } 5273 5274 5275 mixin(defaultExprOps!(false)); 5276 5277 5278 private: 5279 V1 _vec1; 5280 V2 _vec2; 5281 } 5282 5283 5284 return Cartesian!()(vec1, vec2); 5285 } 5286 unittest{ 5287 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5288 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5289 5290 5291 auto v1 = [0, 1, 2, 3].toMatrix!(3, 1); 5292 auto v2 = [2, 3, 4, 5].toMatrix!(1, 2); 5293 5294 assert(v1.cartesian(v2) == v1 * v2); 5295 static assert(hasStaticRows!(typeof(v1.cartesian(v2)))); 5296 static assert(hasStaticCols!(typeof(v1.cartesian(v2)))); 5297 } 5298 5299 5300 5301 /** 5302 置換行列を作ります 5303 */ 5304 auto permutation(size_t size = wild, size_t)(const size_t[] pos) pure nothrow @safe 5305 in{ 5306 foreach(e; pos) 5307 assert(e < pos.length); 5308 5309 static if(size != wild) 5310 assert(pos.length == size); 5311 } 5312 body{ 5313 static struct Permutation 5314 { 5315 static if(size != wild) 5316 enum rows = size; 5317 else 5318 size_t rows() pure nothrow @safe const @property { return _pos.length; } 5319 5320 alias cols = rows; 5321 5322 5323 auto opIndex(size_t i, size_t j) pure nothrow @safe const 5324 { 5325 return _pos[i] == j ? 1 : 0; 5326 } 5327 5328 5329 mixin(defaultExprOps!(false)); 5330 5331 5332 @property auto inverse() pure nothrow @safe const 5333 { 5334 static struct InvPermutation 5335 { 5336 static if(size != wild) 5337 enum rows = size; 5338 else 5339 size_t rows() pure nothrow @safe const @property { return _pos.length; } 5340 5341 alias cols = rows; 5342 5343 5344 auto opIndex(size_t i, size_t j) pure nothrow @safe const 5345 { 5346 return _pos[j] == i ? 1 : 0; 5347 } 5348 5349 5350 mixin(defaultExprOps!(false)); 5351 5352 5353 @property auto inverse() pure nothrow @safe const 5354 { 5355 return Permutation(_pos); 5356 } 5357 5358 5359 private: 5360 const(size_t)[] _pos; 5361 } 5362 5363 5364 return InvPermutation(_pos); 5365 } 5366 5367 5368 @property const(size_t)[] exchangeTable() pure nothrow @safe const 5369 { 5370 return _pos; 5371 } 5372 5373 5374 private: 5375 const(size_t)[] _pos; 5376 } 5377 5378 5379 return Permutation(pos); 5380 } 5381 5382 5383 template isPermutationMatrix(A) 5384 { 5385 enum isPermutationMatrix = isNarrowMatrix!A && is(Unqual!(typeof(A.init.exchangeTable)) : size_t[]); 5386 } 5387 5388 5389 5390 struct PLU(M) 5391 if(isNarrowMatrix!M && !isAbstractMatrix!M) 5392 { 5393 static if(hasStaticRows!M) 5394 alias rows = lu.rows; 5395 else 5396 @property size_t rows() pure nothrow @safe const { return piv.length; } 5397 5398 alias cols = rows; 5399 5400 size_t[] piv; 5401 bool isEvenP; 5402 M lu; 5403 5404 5405 auto p() pure nothrow @safe const 5406 { 5407 return permutation(piv); 5408 } 5409 5410 5411 5412 auto l() pure nothrow @safe 5413 { 5414 static struct L() 5415 { 5416 static if(hasStaticRows!M) 5417 enum rows = M.rows; 5418 else static if(hasStaticCols!M) 5419 enum rows = M.cols; 5420 else 5421 size_t rows() const @property { return _lu.rows; } 5422 5423 alias cols = rows; 5424 5425 5426 typeof(_lu[0, 0]) opIndex(size_t i, size_t j) const 5427 in{ 5428 assert(i < rows); 5429 assert(j < cols); 5430 } 5431 body{ 5432 if(i == j) 5433 return cast(typeof(return))1; 5434 else if(i < j) 5435 return cast(typeof(return))0; 5436 else 5437 return _lu[i, j]; 5438 } 5439 5440 5441 mixin(defaultExprOps!(false)); 5442 5443 5444 private: 5445 M _lu; 5446 } 5447 5448 5449 return L!()(lu); 5450 } 5451 5452 5453 5454 auto u() pure nothrow @safe 5455 { 5456 static struct U() 5457 { 5458 static if(hasStaticRows!M) 5459 enum rows = M.rows; 5460 else static if(hasStaticCols!M) 5461 enum rows = M.cols; 5462 else 5463 size_t rows() const @property { return _lu.rows; } 5464 5465 alias cols = rows; 5466 5467 5468 typeof(_lu[0, 0]) opIndex(size_t i, size_t j) const 5469 in{ 5470 assert(i < rows); 5471 assert(j < cols); 5472 } 5473 body{ 5474 if(i > j) 5475 return cast(typeof(return))0; 5476 else 5477 return _lu[i, j]; 5478 } 5479 5480 5481 mixin(defaultExprOps!(false)); 5482 5483 5484 private: 5485 M _lu; 5486 } 5487 5488 5489 return U!()(lu); 5490 } 5491 5492 5493 /** 5494 Ax = bとなるxを解きます 5495 */ 5496 auto solveInPlace(V)(V b) 5497 if(isVector!V /*&& isFloatingPoint!(ElementType!V) && is(typeof({b[0] = real.init;}))*/ ) 5498 in{ 5499 assert(b.length == rows); 5500 } 5501 body{ 5502 /* 5503 Ax = bの両辺にPをかけることにより 5504 PAx = Pbとなるが、LU分解によりPA = LUであるから、 5505 LUx = Pbである。 5506 5507 ここで、y=Uxとなるyベクトルを考える。 5508 Ly = Pbである。 5509 Lは下三角行列なので、簡単にyベクトルは求まる。 5510 5511 次に、Ux=yより、xベクトルを同様に求めれば良い 5512 */ 5513 5514 immutable size_t size = rows; 5515 5516 // b <- Pb 5517 b = this.p * b; 5518 5519 // Ly=Pbからyを求める 5520 foreach(i; 1 .. size) 5521 foreach(j; 0 .. i) 5522 b[i] -= lu[i, j] * b[j]; 5523 5524 // Ux=Py 5525 foreach_reverse(i; 0 .. size){ 5526 foreach_reverse(j; i+1 .. size) 5527 b[i] -= lu[i, j] * b[j]; 5528 5529 b[i] /= lu[i, i]; 5530 } 5531 } 5532 5533 5534 /** 5535 逆行列を求める 5536 */ 5537 M inverse() @property 5538 { 5539 immutable size_t size = lu.rows; 5540 5541 M m = identity!(ElementType!M)().congeal(size, size); 5542 5543 foreach(i; 0 .. lu.cols) 5544 this.solveInPlace(m.rowVectors[i]); 5545 5546 return m; 5547 } 5548 5549 5550 auto det() @property 5551 { 5552 auto dst = cast(ElementType!M)(isEvenP ? 1 : -1); 5553 foreach(i; 0 .. rows) 5554 dst *= lu[i, i]; 5555 5556 return dst; 5557 } 5558 } 5559 5560 5561 /** 5562 In-Placeで、行列をLU分解します。 5563 5564 "Numerical Recipes in C" 5565 */ 5566 PLU!(A) pluDecomposeInPlace(A)(A m) 5567 if(isNarrowMatrix!A && hasLvalueElements!A && (!hasStaticRows!A || !hasStaticCols!A || is(typeof({static assert(A.rows == A.cols);})))) 5568 in{ 5569 assert(m.rows == m.cols); 5570 } 5571 body{ 5572 immutable size = m.rows; 5573 scope vv = new real[size]; 5574 bool isEvenP; 5575 size_t[] idx = new size_t[size]; 5576 5577 foreach(i, ref e; idx) 5578 e = i; 5579 5580 foreach(i; 0 .. size){ 5581 real big = 0; 5582 5583 foreach(j; 0 .. size){ 5584 immutable temp = m[i, j].abs(); 5585 if(temp > big) 5586 big = temp; 5587 } 5588 5589 if(big == 0) enforce("Input matrix is a singular matrix"); 5590 5591 vv[i] = 1.0 / big; 5592 } 5593 5594 5595 foreach(j; 0 .. size){ 5596 foreach(i; 0 .. j){ 5597 auto sum = m[i, j]; 5598 foreach(k; 0 .. i) sum -= m[i, k] * m[k, j]; 5599 m[i, j] = sum; 5600 } 5601 5602 real big = 0; 5603 size_t imax; 5604 foreach(i; j .. size){ 5605 auto sum = m[i, j]; 5606 foreach(k; 0 .. j) sum -= m[i, k] * m[k, j]; 5607 m[i, j] = sum; 5608 5609 immutable dum = vv[i] * sum.abs(); 5610 if(dum >= big){ 5611 big = dum; 5612 imax = i; 5613 } 5614 } 5615 5616 if(j != imax){ 5617 foreach(k; 0 .. size) 5618 swap(m[imax, k], m[j, k]); 5619 5620 isEvenP = !isEvenP; 5621 vv[imax] = vv[j]; 5622 5623 swap(idx[j], idx[imax]); 5624 } 5625 5626 //idx[j] = imax; 5627 5628 //if(m[j, j] == 0) m[j, j] = 1.0E-20; 5629 5630 if(j != size-1){ 5631 immutable dum = 1 / m[j, j]; 5632 foreach(i; j+1 .. size) 5633 m[i, j] *= dum; 5634 } 5635 } 5636 5637 return PLU!A(idx, isEvenP, m); 5638 } 5639 unittest{ 5640 real[3][3] mStack = [[1, 2, 2], 5641 [2, 1, 1], 5642 [2, 2, 2]]; 5643 5644 auto m = matrix(mStack); 5645 5646 SMatrix!(real, 3, 3) org = m; 5647 auto plu = m.pluDecomposeInPlace(); 5648 5649 SMatrix!(real, 3, 3) result = plu.p.inverse * plu.l * plu.u; 5650 5651 foreach(i; 0 .. 3) foreach(j; 0 .. 3) 5652 assert(approxEqual(result[i, j], org[i, j])); // A = P^(-1)LU 5653 5654 assert(approxEqual(plu.det, 0)); 5655 } 5656 5657 unittest{ 5658 real[3][3] mStack = [[2, 4, 2], 5659 [4, 10, 3], 5660 [3, 7, 1]]; 5661 5662 auto m = mStack.matrix(); 5663 5664 SMatrix!(real, 3, 3) org = m; 5665 auto plu = m.pluDecomposeInPlace(); 5666 5667 auto v = matrix!(3, 1)(cast(real[])[8, 17, 11]); 5668 5669 plu.solveInPlace(v); 5670 foreach(i; 0 .. 3) 5671 assert(approxEqual(v[i], 1)); 5672 } 5673 5674 unittest{ 5675 real[3][3] mStack = [[2, 4, 2], 5676 [4, 10, 3], 5677 [3, 7, 1]]; 5678 5679 auto m = mStack.matrix(); 5680 5681 SMatrix!(real, 3, 3) org = m; 5682 auto plu = m.pluDecomposeInPlace(); 5683 auto iden = org.pref * plu.inverse; 5684 5685 foreach(i; 0 .. 3) 5686 foreach(j; 0 .. 3) 5687 assert(approxEqual(iden[i, j], identity!real[i, j])); 5688 } 5689 5690 5691 /** 5692 ベクトルのノルムを計算します 5693 */ 5694 auto norm(real N = 2, V)(V v) 5695 { 5696 real sum = 0; 5697 foreach(i; 0 .. v.length) 5698 sum += v[i] ^^ N; 5699 return sum ^^ (1/N); 5700 }