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 965 Example: 966 ---- 967 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;}} 968 alias Static1x1 = S!(int, 1, 1); 969 alias Static1x2 = S!(int, 1, 2); 970 alias Static2x1 = S!(int, 2, 1); 971 alias Static2x2 = S!(int, 2, 2); 972 973 static struct D(T){size_t rows = 1, cols = 1; T opIndex(size_t i, size_t j) const {return T.init;}} 974 alias Dynamic = D!(int); 975 976 static struct I(T){ 977 enum rows = 0, cols = 0; 978 T opIndex(size_t i, size_t j) const { return T.init; } 979 static InferredResult inferSize(size_t rs, size_t cs){ 980 if(rs == 0 && cs == 0) 981 return InferredResult(false, 0, 0); 982 else if(rs == 0 || cs == 0) 983 return InferredResult(true, max(rs, cs), max(rs, cs)); 984 else 985 return InferredResult(true, rs, cs); 986 } 987 } 988 alias Inferable = I!int; 989 static assert(Inferable.inferSize(1, 0).isValid); 990 991 alias T = Inferable; 992 static assert(T.rows == wild); 993 static assert(T.cols == wild); 994 995 996 static assert( isValidOperator!(Static1x1, "+", Static1x1)); 997 static assert(!isValidOperator!(Static1x1, "+", Static1x2)); 998 static assert( isValidOperator!(Static1x2, "+", Static1x2)); 999 static assert(!isValidOperator!(Static1x2, "+", Static1x1)); 1000 1001 static assert( isValidOperator!(Static1x1, "+", Dynamic)); 1002 static assert( isValidOperator!(Static1x2, "+", Dynamic)); 1003 static assert( isValidOperator!(Dynamic, "+", Static1x1)); 1004 static assert( isValidOperator!(Dynamic, "+", Static1x2)); 1005 1006 static assert( isValidOperator!(Static1x1, "+", Inferable)); 1007 static assert( isValidOperator!(Static1x2, "+", Inferable)); 1008 static assert( isValidOperator!(Inferable, "+", Static1x1)); 1009 static assert( isValidOperator!(Inferable, "+", Static1x2)); 1010 1011 static assert( isValidOperator!(Static1x1, "*", Static1x1)); 1012 static assert( isValidOperator!(Static1x1, "*", Static1x2)); 1013 static assert(!isValidOperator!(Static1x2, "*", Static1x2)); 1014 static assert(!isValidOperator!(Static1x2, "*", Static1x1)); 1015 1016 static assert( isValidOperator!(Static1x1, "*", Dynamic)); 1017 static assert( isValidOperator!(Static1x2, "*", Dynamic)); 1018 static assert( isValidOperator!(Dynamic, "*", Static1x1)); 1019 static assert( isValidOperator!(Dynamic, "*", Static1x2)); 1020 1021 static assert( isValidOperator!(Static1x1, "*", Inferable)); 1022 static assert( isValidOperator!(Static1x2, "*", Inferable)); 1023 static assert( isValidOperator!(Inferable, "*", Static1x1)); 1024 static assert( isValidOperator!(Inferable, "*", Static1x2)); 1025 ---- 1026 */ 1027 template isValidOperator(L, string op, R) 1028 { 1029 static if(op != "=") 1030 { 1031 static if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R)) 1032 enum isValidOperator = is(typeof(mixin("typeof(L.init.at(0, 0)).init " ~ op ~ " typeof(R.init.at(0, 0)).init"))) && isValidOperatorImpl!(L, op, R); 1033 else static if(isMatrix!L || isAbstractMatrix!L) 1034 enum isValidOperator = is(typeof(mixin("typeof(L.init.at(0, 0)).init " ~ op ~ " R.init"))) && isValidOperatorImpl!(L, op, R); 1035 else static if(isMatrix!R || isAbstractMatrix!R) 1036 enum isValidOperator = is(typeof(mixin("L.init " ~ op ~ " typeof(R.init.at(0, 0)).init"))) && isValidOperatorImpl!(L, op, R); 1037 else 1038 static assert(0); 1039 } 1040 else 1041 { 1042 static if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R)){ 1043 enum isValidOperator = isAssignable!(typeof(L.init.at(0, 0)), typeof(R.init.at(0, 0))) && isValidOperatorImpl!(L, "+", R); 1044 }else static if(isMatrix!L || isAbstractMatrix!L) 1045 enum isValidOperator = isAssignable!(typeof(L.init.at(0,0)), R) && isValidOperatorImpl!(L, "+", R); 1046 else static if(isMatrix!R || isAbstractMatrix!R) 1047 enum isValidOperator = isAssignable!(L, typeof(R.init.at(0, 0))) && isValidOperatorImpl!(L, "+", R); 1048 else 1049 static assert(0); 1050 } 1051 } 1052 1053 1054 template isValidOperatorImpl(L, string op, R) 1055 if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R) && op != "*") 1056 { 1057 struct Inferred(M, size_t r, size_t c) 1058 { 1059 enum size_t rows = M.inferSize(r, c).rows; 1060 enum size_t cols = M.inferSize(r, c).cols; 1061 1062 auto opIndex(size_t i, size_t j) const { return ElementType!M.init; } 1063 } 1064 1065 static if(op != "+" && op != "-") 1066 enum isValidOperatorImpl = false; 1067 else static if(isAbstractMatrix!L && isAbstractMatrix!R) 1068 enum isValidOperatorImpl = true; 1069 else static if(isAbstractMatrix!L) 1070 { 1071 static if(hasStaticRows!R && hasStaticCols!R) 1072 enum isValidOperatorImpl = L.inferSize(staticRows!R, staticCols!R).isValid && isValidOperatorImpl!(Inferred!(L, staticRows!R, staticCols!R), op, R); 1073 else static if(hasStaticRows!R) 1074 enum isValidOperatorImpl = L.inferSize(staticRows!R, wild).isValid && isValidOperatorImpl!(Inferred!(L, staticRows!R, wild), op, R); 1075 else static if(hasStaticCols!R) 1076 enum isValidOperatorImpl = L.inferSize(wild, staticCols!R).isValid && isValidOperatorImpl!(Inferred!(L, wild, staticCols!R), op, R); 1077 else 1078 enum isValidOperatorImpl = true; 1079 } 1080 else static if(isAbstractMatrix!R) 1081 enum isValidOperatorImpl = isValidOperatorImpl!(R, op, L); 1082 else 1083 { 1084 static if(hasStaticRows!L) 1085 { 1086 static if(hasStaticRows!R) 1087 enum _isValidR = staticRows!L == staticRows!R; 1088 else 1089 enum _isValidR = true; 1090 } 1091 else 1092 enum _isValidR = true; 1093 1094 static if(hasStaticCols!L) 1095 { 1096 static if(hasStaticCols!R) 1097 enum _isValidC = staticCols!L == staticCols!R; 1098 else 1099 enum _isValidC = true; 1100 } 1101 else 1102 enum _isValidC = true; 1103 1104 enum isValidOperatorImpl = _isValidR && _isValidC; 1105 } 1106 } 1107 1108 1109 template isValidOperatorImpl(L, string op, R) 1110 if((isMatrix!L || isAbstractMatrix!L) && (isMatrix!R || isAbstractMatrix!R) && op == "*") 1111 { 1112 struct Inferred(M, size_t r, size_t c) 1113 if(r == wild || c == wild) 1114 { 1115 enum size_t rows = M.inferSize(r, c).rows; 1116 enum size_t cols = M.inferSize(r, c).cols; 1117 1118 auto opIndex(size_t i, size_t j) const { return M.init[i, j]; } 1119 } 1120 1121 1122 static if(isAbstractMatrix!L && isAbstractMatrix!R) 1123 enum isValidOperatorImpl = false; 1124 else static if(isAbstractMatrix!L) 1125 { 1126 static if(hasStaticRows!R) 1127 enum isValidOperatorImpl = isValidOperatorImpl!(Inferred!(L, wild, staticRows!R), op, R); 1128 else 1129 enum isValidOperatorImpl = true; 1130 } 1131 else static if(isAbstractMatrix!R) 1132 { 1133 static if(hasStaticCols!L) 1134 enum isValidOperatorImpl = isValidOperatorImpl!(L, op, Inferred!(R, staticCols!L, wild)); 1135 else 1136 enum isValidOperatorImpl = true; 1137 } 1138 else 1139 { 1140 static if(hasStaticCols!L && hasStaticRows!R) 1141 enum isValidOperatorImpl = staticCols!L == staticRows!R; 1142 else 1143 enum isValidOperatorImpl = true; 1144 } 1145 } 1146 1147 1148 template isValidOperatorImpl(L, string op, R) 1149 if(((isMatrix!L || isAbstractMatrix!L) && isNotVectorOrMatrix!R) || ((isMatrix!R || isAbstractMatrix!R) && isNotVectorOrMatrix!L)) 1150 { 1151 static if(op != "+" && op != "-" && op != "*" && op != "/") 1152 enum isValidOperatorImpl = false; 1153 else 1154 enum isValidOperatorImpl = true; 1155 } 1156 1157 1158 unittest{ 1159 static struct S(T, size_t r, size_t c) 1160 { 1161 enum rows = r; 1162 enum cols = c; 1163 T at(size_t i, size_t j) const { return T.init; } 1164 alias opIndex = at; 1165 1166 static assert(isNarrowMatrix!(typeof(this))); 1167 static assert(isMatrix!(typeof(this))); 1168 static assert(hasStaticRows!(typeof(this))); 1169 static assert(hasStaticCols!(typeof(this))); 1170 } 1171 alias Static1x1 = S!(int, 1, 1); 1172 alias Static1x2 = S!(int, 1, 2); 1173 alias Static2x1 = S!(int, 2, 1); 1174 alias Static2x2 = S!(int, 2, 2); 1175 1176 static struct D(T) 1177 { 1178 size_t rows = 1, cols = 1; 1179 T at(size_t i, size_t j) const { return T.init; } 1180 alias opIndex = at; 1181 1182 static assert(isNarrowMatrix!(typeof(this))); 1183 static assert(isMatrix!(typeof(this))); 1184 static assert(!hasStaticRows!(typeof(this))); 1185 static assert(!hasStaticCols!(typeof(this))); 1186 } 1187 alias Dynamic = D!(int); 1188 1189 static struct I(T) 1190 { 1191 T at(size_t i, size_t j) const { return T.init; } 1192 alias opIndex = at; 1193 1194 static InferredResult inferSize(Msize_t rs, Msize_t cs) 1195 { 1196 if(rs == wild && cs == wild) 1197 return InferredResult(false, 0, 0); 1198 else if(rs == wild || cs == wild) 1199 return InferredResult(true, max(rs, cs), max(rs, cs)); 1200 else 1201 return InferredResult(true, rs, cs); 1202 } 1203 } 1204 alias Inferable = I!int; 1205 static assert(Inferable.inferSize(1, wild).isValid); 1206 1207 alias T = Inferable; 1208 static assert(isAbstractMatrix!(I!int)); 1209 1210 1211 static assert( isValidOperator!(Static1x1, "+", Static1x1)); 1212 static assert(!isValidOperator!(Static1x1, "+", Static1x2)); 1213 static assert( isValidOperator!(Static1x2, "+", Static1x2)); 1214 static assert(!isValidOperator!(Static1x2, "+", Static1x1)); 1215 1216 static assert( isValidOperator!(Static1x1, "+", Dynamic)); 1217 static assert( isValidOperator!(Static1x2, "+", Dynamic)); 1218 static assert( isValidOperator!(Dynamic, "+", Static1x1)); 1219 static assert( isValidOperator!(Dynamic, "+", Static1x2)); 1220 1221 static assert( isValidOperator!(Static1x1, "+", Inferable)); 1222 static assert( isValidOperator!(Static1x2, "+", Inferable)); 1223 static assert( isValidOperator!(Inferable, "+", Static1x1)); 1224 static assert( isValidOperator!(Inferable, "+", Static1x2)); 1225 1226 static assert( isValidOperator!(Static1x1, "*", Static1x1)); 1227 static assert( isValidOperator!(Static1x1, "*", Static1x2)); 1228 static assert(!isValidOperator!(Static1x2, "*", Static1x2)); 1229 static assert(!isValidOperator!(Static1x2, "*", Static1x1)); 1230 1231 static assert( isValidOperator!(Static1x1, "*", Dynamic)); 1232 static assert( isValidOperator!(Static1x2, "*", Dynamic)); 1233 static assert( isValidOperator!(Dynamic, "*", Static1x1)); 1234 static assert( isValidOperator!(Dynamic, "*", Static1x2)); 1235 1236 static assert( isValidOperator!(Static1x1, "*", Inferable)); 1237 static assert( isValidOperator!(Static1x2, "*", Inferable)); 1238 static assert( isValidOperator!(Inferable, "*", Static1x1)); 1239 static assert( isValidOperator!(Inferable, "*", Static1x2)); 1240 1241 foreach(i, E1; TypeTuple!(byte, short, int, long)) 1242 { 1243 foreach(E2; TypeTuple!(byte, short, int, long)[0 .. i+1]) 1244 { 1245 static assert( isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 1))); 1246 static assert(!isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 2))); 1247 static assert( isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 2))); 1248 static assert(!isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 1))); 1249 1250 static assert( isValidOperator!(S!(E1, 1, 1), "=", D!(E2))); 1251 static assert( isValidOperator!(S!(E1, 1, 2), "=", D!(E2))); 1252 static assert( isValidOperator!(D!(E1), "=", S!(E2, 1, 1))); 1253 static assert( isValidOperator!(D!(E1), "=", S!(E2, 1, 2))); 1254 1255 static assert( isValidOperator!(S!(E1, 1, 1), "=", I!(E2))); 1256 static assert( isValidOperator!(S!(E1, 1, 2), "=", I!(E2))); 1257 static assert( isValidOperator!(I!(E1), "=", S!(E2, 1, 1))); 1258 static assert( isValidOperator!(I!(E1), "=", S!(E2, 1, 2))); 1259 1260 static assert( isValidOperator!(S!(E1, 1, 1), "=", E2)); 1261 static assert( isValidOperator!(D!(E1), "=", E2)); 1262 static assert( isValidOperator!(I!(E1), "=", E2)); 1263 } 1264 1265 foreach(E2; TypeTuple!(byte, short, int, long)[i+1 .. $]) 1266 { 1267 static assert(!isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 1))); 1268 static assert(!isValidOperator!(S!(E1, 1, 1), "=", S!(E2, 1, 2))); 1269 static assert(!isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 2))); 1270 static assert(!isValidOperator!(S!(E1, 1, 2), "=", S!(E2, 1, 1))); 1271 1272 static assert(!isValidOperator!(S!(E1, 1, 1), "=", D!(E2))); 1273 static assert(!isValidOperator!(S!(E1, 1, 2), "=", D!(E2))); 1274 static assert(!isValidOperator!(D!(E1), "=", S!(E2, 1, 1))); 1275 static assert(!isValidOperator!(D!(E1), "=", S!(E2, 1, 2))); 1276 1277 static assert(!isValidOperator!(S!(E1, 1, 1), "=", I!(E2))); 1278 static assert(!isValidOperator!(S!(E1, 1, 2), "=", I!(E2))); 1279 static assert(!isValidOperator!(I!(E1), "=", S!(E2, 1, 1))); 1280 static assert(!isValidOperator!(I!(E1), "=", S!(E2, 1, 2))); 1281 1282 static assert(!isValidOperator!(S!(E1, 1, 1), "=", E2)); 1283 static assert(!isValidOperator!(D!(E1), "=", E2)); 1284 static assert(!isValidOperator!(I!(E1), "=", E2)); 1285 } 1286 } 1287 } 1288 1289 1290 /** 1291 式テンプレート演算子の種類 1292 */ 1293 enum ETOSpec : uint 1294 { 1295 none = 0, 1296 matrixAddMatrix = (1 << 0), 1297 matrixSubMatrix = (1 << 1), 1298 matrixMulMatrix = (1 << 2), 1299 matrixAddScalar = (1 << 3), 1300 scalarAddMatrix = (1 << 4), 1301 matrixSubScalar = (1 << 5), 1302 scalarSubMatrix = (1 << 6), 1303 matrixMulScalar = (1 << 7), 1304 scalarMulMatrix = (1 << 8), 1305 matrixDivScalar = (1 << 9), 1306 scalarDivMatrix = (1 << 10), 1307 addScalar = (1 << 11), 1308 subScalar = (1 << 12), 1309 mulScalar = (1 << 13), 1310 divScalar = (1 << 14), 1311 opEquals = (1 << 15), 1312 toString = (1 << 16), 1313 opAssign = (1 << 17), 1314 swizzle = (1 << 18), 1315 modifiedOperator = addScalar | subScalar | mulScalar | divScalar | opAssign, 1316 all = (1 << 19) -1, 1317 } 1318 1319 1320 /** 1321 式テンプレートでの演算子の種類を返します 1322 */ 1323 template ETOperatorSpec(A, string op, B) 1324 if(isValidOperator!(A, op, B)) 1325 { 1326 static if((isMatrix!A || isAbstractMatrix!A) && (isMatrix!B || isAbstractMatrix!B)) 1327 enum ETOSpec ETOperatorSpec = op == "+" ? ETOSpec.matrixAddMatrix 1328 : (op == "-" ? ETOSpec.matrixSubMatrix 1329 : ETOSpec.matrixMulMatrix); 1330 else static if(isNotVectorOrMatrix!A) 1331 enum ETOSpec ETOperatorSpec = op == "+" ? ETOSpec.scalarAddMatrix 1332 : (op == "-" ? ETOSpec.scalarSubMatrix 1333 : (op == "*" ? ETOSpec.scalarMulMatrix 1334 : ETOSpec.scalarDivMatrix)); 1335 else 1336 enum ETOSpec ETOperatorSpec = op == "+" ? ETOSpec.matrixAddScalar 1337 : (op == "-" ? ETOSpec.matrixSubScalar 1338 : (op == "*" ? ETOSpec.matrixMulScalar 1339 : ETOSpec.matrixDivScalar)); 1340 } 1341 1342 /// 1343 unittest{ 1344 static struct S(T, size_t r, size_t c) 1345 { 1346 enum rows = r; 1347 enum cols = c; 1348 T opIndex(size_t i, size_t j) const {return T.init;} 1349 alias at = opIndex; 1350 } 1351 alias Matrix2i = S!(int, 2, 2); 1352 1353 static assert(ETOperatorSpec!(Matrix2i, "+", Matrix2i) == ETOSpec.matrixAddMatrix); 1354 static assert(ETOperatorSpec!(Matrix2i, "-", Matrix2i) == ETOSpec.matrixSubMatrix); 1355 static assert(ETOperatorSpec!(Matrix2i, "*", Matrix2i) == ETOSpec.matrixMulMatrix); 1356 static assert(ETOperatorSpec!(Matrix2i, "*", int) == ETOSpec.matrixMulScalar); 1357 static assert(ETOperatorSpec!(int, "*", Matrix2i) == ETOSpec.scalarMulMatrix); 1358 } 1359 1360 1361 /** 1362 式テンプレートでの、式を表します 1363 */ 1364 struct MatrixExpression(Lhs, string s, Rhs) 1365 if(isValidOperator!(Lhs, s, Rhs) 1366 && (isAbstractMatrix!Lhs || isAbstractMatrix!Rhs) 1367 && !(isMatrix!Lhs || isMatrix!Rhs)) 1368 { 1369 enum etoSpec = ETOperatorSpec!(Lhs, s, Rhs); 1370 1371 1372 static InferredResult inferSize(Msize_t r, Msize_t c) 1373 { 1374 static if(isAbstractMatrix!Lhs && isAbstractMatrix!Rhs) 1375 { 1376 static assert(s != "*"); 1377 auto rLhs = Lhs.inferSize(r, c); 1378 auto rRhs = Rhs.inferSize(r, c); 1379 1380 bool b = rLhs.isValid && rRhs.isValid && rLhs.rows == rRhs.rows && rLhs.cols == rRhs.cols; 1381 return InferredResult(b, rLhs.rows, rLhs.cols); 1382 } 1383 else static if(isAbstractMatrix!Lhs) 1384 return Lhs.inferSize(r, c); 1385 else 1386 return Rhs.inferSize(r, c); 1387 } 1388 1389 1390 auto opIndex(size_t i, size_t j) const 1391 { 1392 static if(etoSpec == ETOSpec.matrixAddMatrix) 1393 return this.lhs[i, j] + this.rhs[i, j]; 1394 else static if(etoSpec == ETOSpec.matrixSubMatrix) 1395 return this.lhs[i, j] - this.rhs[i, j]; 1396 else static if(etoSpec == ETOSpec.matrixMulMatrix) 1397 { 1398 static assert(0); 1399 return typeof(this.lhs.at(0, 0) + this.rhs.at(0, 0)).init; 1400 } 1401 else 1402 { 1403 static if(isMatrix!Lhs || isAbstractMatrix!Lhs) 1404 return mixin("this.lhs.at(i, j) " ~ s ~ " this.rhs"); 1405 else 1406 return mixin("this.lhs " ~ s ~ " this.rhs.at(i, j)"); 1407 } 1408 } 1409 1410 1411 mixin(defaultExprOps!(true)); 1412 1413 private: 1414 Lhs lhs; 1415 Rhs rhs; 1416 } 1417 1418 1419 /// ditto 1420 struct MatrixExpression(Lhs, string s, Rhs) 1421 if(isValidOperator!(Lhs, s, Rhs) 1422 && !((isAbstractMatrix!Lhs || isAbstractMatrix!Rhs) 1423 && !(isMatrix!Lhs || isMatrix!Rhs))) 1424 { 1425 enum etoSpec = ETOperatorSpec!(Lhs, s, Rhs); 1426 1427 1428 static if((isMatrix!Lhs || isAbstractMatrix!Lhs) && (isMatrix!Rhs || isAbstractMatrix!Rhs)) 1429 { 1430 static if(s == "*") 1431 { 1432 static if(hasStaticRows!Lhs) 1433 { 1434 enum rows = Lhs.rows; 1435 private enum staticrows = rows; 1436 } 1437 else static if(isAbstractMatrix!Lhs && hasStaticRows!Rhs) 1438 { 1439 enum rows = Lhs.inferSize(wild, Rhs.rows).rows; 1440 private enum staticrows = rows; 1441 } 1442 else static if(hasDynamicRows!Lhs) 1443 { 1444 @property size_t rows() const { return this.lhs.rows; } 1445 private enum staticrows = wild; 1446 } 1447 else static if(isAbstractMatrix!Lhs && hasDynamicRows!Rhs) 1448 { 1449 @property size_t rows() const { return this.lhs.inferSize(wild, rhs.rows).rows; } 1450 private enum staticrows = wild; 1451 } 1452 else 1453 static assert(0); 1454 1455 1456 static if(hasStaticCols!Rhs) 1457 { 1458 enum cols = Rhs.cols; 1459 private enum staticcols = cols; 1460 } 1461 else static if(isAbstractMatrix!Rhs && hasStaticCols!Lhs) 1462 { 1463 enum cols = Rhs.inferSize(Lhs.cols, wild).cols; 1464 private enum staticcols = cols; 1465 } 1466 else static if(hasDynamicRows!Rhs) 1467 { 1468 @property size_t cols() const { return this.rhs.cols; } 1469 private enum staticcols = wild; 1470 } 1471 else static if(isAbstractMatrix!Rhs && hasDynamicColumns!Lhs) 1472 { 1473 @property size_t cols() const { return this.rhs.inferSize(lhs.cols, wild).cols; } 1474 private enum staticcols = wild; 1475 } 1476 else 1477 static assert(0); 1478 } 1479 else 1480 { 1481 static if(hasStaticRows!Lhs) 1482 { 1483 enum rows = Lhs.rows; 1484 private enum staticrows = rows; 1485 } 1486 else static if(hasStaticRows!Rhs) 1487 { 1488 enum rows = Rhs.rows; 1489 private enum staticrows = rows; 1490 } 1491 else static if(hasDynamicRows!Lhs) 1492 { 1493 @property size_t rows() const { return this.lhs.rows; } 1494 private enum staticrows = wild; 1495 } 1496 else 1497 { 1498 @property size_t rows() const { return this.rhs.rows; } 1499 private enum staticrows = wild; 1500 } 1501 1502 static if(hasStaticCols!Lhs) 1503 { 1504 enum cols = Lhs.cols; 1505 private enum staticcols = cols; 1506 } 1507 else static if(hasStaticCols!Rhs) 1508 { 1509 enum cols = Rhs.cols; 1510 private enum staticcols = cols; 1511 } 1512 else static if(hasDynamicColumns!Lhs) 1513 { 1514 @property size_t cols() const { return this.lhs.cols; } 1515 private enum staticcols = wild; 1516 } 1517 else 1518 { 1519 @property size_t cols() const { return this.rhs.cols; } 1520 private enum staticcols = wild; 1521 } 1522 } 1523 } 1524 else static if(isMatrix!Lhs || isAbstractMatrix!Lhs) 1525 { 1526 static assert(!isAbstractMatrix!Lhs); 1527 1528 static if(hasStaticRows!Lhs) 1529 { 1530 enum rows = Lhs.rows; 1531 private enum staticrows = rows; 1532 } 1533 else 1534 { 1535 @property size_t rows() const { return this.lhs.rows; } 1536 private enum staticrows = wild; 1537 } 1538 1539 static if(hasStaticCols!Lhs) 1540 { 1541 enum cols = Lhs.cols; 1542 private enum staticcols = cols; 1543 } 1544 else 1545 { 1546 @property size_t cols() const { return this.lhs.cols; } 1547 private enum staticcols = wild; 1548 } 1549 } 1550 else 1551 { 1552 static assert(!isAbstractMatrix!Rhs); 1553 1554 static if(hasStaticRows!Rhs) 1555 { 1556 enum rows = Rhs.rows; 1557 private enum staticrsght = rows; 1558 } 1559 else 1560 { 1561 @property size_t rows() const { return this.rhs.rows; } 1562 private enum staticrsght = wild; 1563 } 1564 1565 static if(hasStaticCols!Rhs) 1566 { 1567 enum cols = Rhs.cols; 1568 private enum staticcsght = cols; 1569 } 1570 else 1571 { 1572 @property size_t cols() const { return this.rhs.cols; } 1573 private enum staticcsght = wild; 1574 } 1575 } 1576 1577 1578 auto opIndex(size_t i, size_t j) const 1579 in{ 1580 assert(i < this.rows); 1581 assert(j < this.cols); 1582 } 1583 body{ 1584 static if(etoSpec == ETOSpec.matrixAddMatrix) 1585 return this.lhs.at(i, j) + this.rhs.at(i, j); 1586 else static if(etoSpec == ETOSpec.matrixSubMatrix) 1587 return this.lhs.at(i, j) - this.rhs.at(i, j); 1588 else static if(etoSpec == ETOSpec.matrixMulMatrix) 1589 { 1590 auto sum = cast(Unqual!(typeof(this.lhs.at(0, 0) * this.rhs.at(0, 0))))0; 1591 1592 static if(hasStaticCols!Lhs) 1593 immutable cnt = Lhs.cols; 1594 else static if(hasStaticRows!Rhs) 1595 immutable cnt = Rhs.rows; 1596 else static if(hasDynamicColumns!Lhs) 1597 immutable cnt = this.lhs.cols; 1598 else 1599 immutable cnt = this.rhs.rows; 1600 1601 foreach(k; 0 .. cnt) 1602 sum += this.lhs.at(i, k) * this.rhs.at(k, j); 1603 return sum; 1604 } 1605 else 1606 { 1607 static if(isMatrix!Lhs || isAbstractMatrix!Lhs) 1608 return mixin("this.lhs.at(i, j) " ~ s ~ " this.rhs"); 1609 else 1610 return mixin("this.lhs " ~ s ~ " this.rhs.at(i, j)"); 1611 } 1612 } 1613 1614 mixin(defaultExprOps!(false)); 1615 1616 private: 1617 Lhs lhs; 1618 Rhs rhs; 1619 } 1620 1621 1622 /// ditto 1623 auto matrixExpression(string s, A, B)(auto ref A a, auto ref B b) 1624 if(isValidOperator!(A, s, B)) 1625 { 1626 return MatrixExpression!(A, s, B)(a, b); 1627 static assert(isNarrowMatrix!(MatrixExpression!(A, s, B)) 1628 || isAbstractMatrix!(MatrixExpression!(A, s, B))); 1629 } 1630 1631 1632 /** 1633 specに示された演算子をオーバーロードします。specはETOSpecで指定する必要があります。 1634 */ 1635 template ExpressionOperators(size_t spec, size_t rs, size_t cs) 1636 { 1637 enum stringMixin = 1638 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1639 q{ 1640 static if(is(typeof({enum _unused_ = this.rows;}))) static assert(rows != 0); 1641 static if(is(typeof({enum _unused_ = this.cols;}))) static assert(cols != 0); 1642 1643 alias at = opIndex; 1644 } ~ 1645 1646 1647 ( rs == 1 ? 1648 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1649 q{ 1650 alias cols length; 1651 alias length opDollar; 1652 1653 1654 auto ref opIndex(size_t i) inout 1655 in{ 1656 assert(i < this.cols); 1657 } 1658 body{ 1659 return this[0, i]; 1660 } 1661 1662 static if(is(typeof({this[0, 0] = this[0, 0];}))) 1663 { 1664 auto ref opIndexAssign(S)(S value, size_t i) 1665 if(is(typeof(this[0, i] = value))) 1666 in{ 1667 assert(i < this.cols); 1668 } 1669 body{ 1670 return this[0, i] = value; 1671 } 1672 1673 1674 auto ref opIndexOpAssign(string op, S)(S value, size_t i) 1675 if(is(typeof(mixin("this[0, i] " ~ op ~ "= value")))) 1676 in{ 1677 assert(i < this.cols); 1678 } 1679 body{ 1680 return mixin("this[0, i] " ~ op ~ "= value"); 1681 } 1682 } 1683 } : ( cs == 1 ? 1684 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1685 q{ 1686 alias rows length; 1687 alias length opDollar; 1688 1689 1690 auto ref opIndex(size_t i) inout 1691 in{ 1692 assert(i < this.rows); 1693 } 1694 body{ 1695 return this[i, 0]; 1696 } 1697 1698 static if(is(typeof({this[0, 0] = this[0, 0];}))) 1699 { 1700 auto ref opIndexAssign(S)(S value, size_t i) 1701 if(is(typeof(this[i, 0] = value))) 1702 in{ 1703 assert(i < this.rows); 1704 } 1705 body{ 1706 return this[0, i] = value; 1707 } 1708 1709 1710 auto ref opIndexOpAssign(string op, S)(S value, size_t i) 1711 if(is(typeof(mixin("this[i, 0] " ~ op ~ "= value")))) 1712 in{ 1713 assert(i < this.rows); 1714 } 1715 body{ 1716 return mixin("this[i, 0] " ~ op ~ "= value"); 1717 } 1718 } 1719 } : "" 1720 )) ~ 1721 1722 1723 /* 1724 ((rs == 1 || cs == 1) && (spec & ETOSpec.opAssign) ? 1725 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1726 q{ 1727 void opAssign(Array)(Array arr) 1728 if(!isNarrowMatrix!Array && is(typeof(arr[size_t.init])) && isAssignable!(typeof(this[size_t.init]), typeof(arr[size_t.init]))) 1729 { 1730 foreach(i; 0 .. this.length) 1731 this[i] = arr[i]; 1732 } 1733 } : "" 1734 ) ~ 1735 */ 1736 1737 1738 (rs == 1 && cs == 1 ? 1739 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1740 q{ 1741 auto det() const @property 1742 { 1743 return this[0, 0]; 1744 } 1745 1746 alias det this; 1747 1748 1749 auto ref opAssign(S)(S value) 1750 if(is(typeof(this[0, 0] = value))) 1751 { 1752 return this[0, 0] = value; 1753 } 1754 1755 1756 auto ref opOpAssign(S)(S value) 1757 if(is(typeof(mixin("this[0, 0] " ~ op ~ "= value")))) 1758 { 1759 return mixin("this[0, 0] " ~ op ~ "= value"); 1760 } 1761 } : "" 1762 ) ~ 1763 1764 1765 (spec & ETOSpec.opEquals ? 1766 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1767 q{ 1768 bool opEquals(Rhs)(auto ref const Rhs mat) const 1769 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1770 { 1771 static assert(isValidOperator!(Unqual!(typeof(this)), "+", Rhs)); 1772 1773 static if(isAbstractMatrix!Rhs) 1774 { 1775 auto result = Rhs.inferSize(this.rows, this.cols); 1776 if(!result.isValid) 1777 return false; 1778 } 1779 else 1780 { 1781 if(this.rows != mat.rows) 1782 return false; 1783 1784 if(this.cols != mat.cols) 1785 return false; 1786 } 1787 1788 foreach(i; 0 .. this.rows) 1789 foreach(j; 0 .. this.cols) 1790 if(this[i, j] != mat.at(i, j)) 1791 return false; 1792 return true; 1793 } 1794 } : "" 1795 ) ~ 1796 1797 1798 (spec & ETOSpec.toString ? 1799 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1800 q{ 1801 @property 1802 void toString(scope void delegate(const(char)[]) sink, string formatString) const @system 1803 { 1804 sink.formattedWrite(formatString, this.toRange); 1805 } 1806 } : "" 1807 ) ~ 1808 1809 1810 (spec & ETOSpec.opAssign ? 1811 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1812 q{ 1813 void opAssign(M)(M m) 1814 if((isMatrix!M || isAbstractMatrix!M) && is(typeof(this[0, 0] = m.at(0, 0)))) 1815 in{ 1816 static if(isAbstractMatrix!M) 1817 assert(m.inferSize(this.rows, this.cols).isValid); 1818 else 1819 { 1820 assert(m.rows == this.rows); 1821 assert(m.cols == this.cols); 1822 } 1823 } 1824 body{ 1825 static assert(isValidOperator!(typeof(this), "=", M)); 1826 1827 foreach(i; 0 .. this.rows) 1828 foreach(j; 0 .. this.cols) 1829 this[i, j] = m.at(i, j); 1830 } 1831 } : "" 1832 ) ~ 1833 1834 1835 (spec & ETOSpec.matrixAddMatrix ? 1836 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1837 q{ 1838 auto opBinary(string op : "+", Rhs)(auto ref Rhs mat) const 1839 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1840 in{ 1841 static if(isAbstractMatrix!Rhs) 1842 assert(mat.inferSize(this.rows, this.cols).isValid); 1843 else 1844 { 1845 assert(mat.rows == this.rows); 1846 assert(mat.cols == this.cols); 1847 } 1848 } 1849 body{ 1850 static assert(isValidOperator!(typeof(this), op, Rhs)); 1851 return matrixExpression!"+"(this, mat); 1852 } 1853 } : "" 1854 ) ~ 1855 1856 1857 (spec & ETOSpec.matrixSubMatrix ? 1858 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1859 q{ 1860 auto opBinary(string op : "-", Rhs)(auto ref Rhs mat) const 1861 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1862 in{ 1863 static if(isAbstractMatrix!Rhs) 1864 assert(mat.inferSize(this.rows, this.cols).isValid); 1865 else 1866 { 1867 assert(mat.rows == this.rows); 1868 assert(mat.cols == this.cols); 1869 } 1870 } 1871 body{ 1872 static assert(isValidOperator!(typeof(this), op, Rhs)); 1873 return matrixExpression!"-"(this, mat); 1874 } 1875 } : "" 1876 ) ~ 1877 1878 1879 (spec & ETOSpec.matrixMulMatrix ? 1880 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1881 q{ 1882 auto opBinary(string op : "*", Rhs)(auto ref Rhs mat) const 1883 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 1884 in{ 1885 static if(isAbstractMatrix!Rhs) 1886 assert(mat.inferSize(this.cols, wild).isValid); 1887 else 1888 assert(mat.rows == this.cols); 1889 } 1890 body{ 1891 static assert(isValidOperator!(typeof(this), op, Rhs)); 1892 return matrixExpression!"*"(this, mat); 1893 } 1894 } : "" 1895 ) ~ 1896 1897 1898 (spec & ETOSpec.matrixAddScalar ? 1899 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1900 q{ 1901 auto opBinary(string op : "+", S)(S s) const 1902 if(isNotVectorOrMatrix!S) 1903 { 1904 static assert(isValidOperator!(typeof(this), op, S)); 1905 return matrixExpression!"+"(this, s); 1906 } 1907 } : "" 1908 ) ~ 1909 1910 1911 (spec & ETOSpec.scalarAddMatrix ? 1912 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1913 q{ 1914 auto opBinaryRight(string op : "+", S)(S s) const 1915 if(isNotVectorOrMatrix!S) 1916 { 1917 static assert(isValidOperator!(S, op, typeof(this))); 1918 return matrixExpression!"+"(s, this); 1919 } 1920 } : "" 1921 ) ~ 1922 1923 1924 (spec & ETOSpec.matrixSubScalar ? 1925 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1926 q{ 1927 auto opBinary(string op : "-", S)(S s) const 1928 if(isNotVectorOrMatrix!S) 1929 { 1930 static assert(isValidOperator!(typeof(this), op, S)); 1931 return matrixExpression!"-"(this, s); 1932 } 1933 } : "" 1934 ) ~ 1935 1936 1937 (spec & ETOSpec.scalarSubMatrix ? 1938 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1939 q{ 1940 auto opBinaryRight(string op : "-", S)(S s) const 1941 if(isNotVectorOrMatrix!S) 1942 { 1943 static assert(isValidOperator!(S, op, typeof(this))); 1944 return matrixExpression!"-"(s, this); 1945 } 1946 } : "" 1947 ) ~ 1948 1949 1950 (spec & ETOSpec.matrixMulScalar ? 1951 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1952 q{ 1953 auto opBinary(string op : "*", S)(S s) const 1954 if(isNotVectorOrMatrix!S) 1955 { 1956 static assert(isValidOperator!(typeof(this), op, S)); 1957 return matrixExpression!"*"(this, s); 1958 } 1959 } : "" 1960 ) ~ 1961 1962 1963 (spec & ETOSpec.scalarMulMatrix ? 1964 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1965 q{ 1966 auto opBinaryRight(string op : "*", S)(S s) const 1967 if(isNotVectorOrMatrix!S) 1968 { 1969 static assert(isValidOperator!(S, op, typeof(this))); 1970 return matrixExpression!"*"(s, this); 1971 } 1972 } : "" 1973 ) ~ 1974 1975 1976 (spec & ETOSpec.matrixDivScalar ? 1977 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1978 q{ 1979 auto opBinary(string op : "/", S)(S s) const 1980 if(isNotVectorOrMatrix!S) 1981 { 1982 static assert(isValidOperator!(typeof(this), op, S)); 1983 return matrixExpression!"/"(this, s); 1984 } 1985 } : "" 1986 ) ~ 1987 1988 1989 (spec & ETOSpec.scalarDivMatrix ? 1990 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 1991 q{ 1992 auto opBinaryRight(string op : "/", S)(S s) const 1993 if(isNotVectorOrMatrix!S) 1994 { 1995 static assert(isValidOperator!(S, op, typeof(this))); 1996 return matrixExpression!"/"(s, this); 1997 } 1998 } : "" 1999 ) ~ 2000 2001 2002 (spec & ETOSpec.addScalar ? 2003 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2004 q{ 2005 void opOpAssign(string op : "+", S)(S scalar) 2006 if(is(typeof(this[0, 0] += scalar))) 2007 { 2008 foreach(r; 0 .. rows) 2009 foreach(c; 0 .. cols) 2010 this[r, c] += scalar; 2011 } 2012 } : "" 2013 ) ~ 2014 2015 2016 (spec & ETOSpec.subScalar ? 2017 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2018 q{ 2019 void opOpAssign(string op : "-", S)(S scalar) 2020 if(is(typeof(this[0, 0] -= scalar))) 2021 { 2022 foreach(r; 0 .. rows) 2023 foreach(c; 0 .. cols) 2024 this[r, c] -= scalar; 2025 } 2026 } : "" 2027 ) ~ 2028 2029 2030 (spec & ETOSpec.mulScalar ? 2031 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2032 q{ 2033 void opOpAssign(string op : "*", S)(S scalar) 2034 if(is(typeof(this[0, 0] *= scalar))) 2035 { 2036 foreach(r; 0 .. rows) 2037 foreach(c; 0 .. cols) 2038 this[r, c] *= scalar; 2039 } 2040 } : "" 2041 ) ~ 2042 2043 2044 (spec & ETOSpec.divScalar ? 2045 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2046 q{ 2047 void opOpAssign(string op : "/", S)(S scalar) 2048 if(is(typeof(this[0, 0] /= scalar))) 2049 { 2050 foreach(r; 0 .. rows) 2051 foreach(c; 0 .. cols) 2052 this[r, c] /= scalar; 2053 } 2054 } : "" 2055 ) ~ 2056 2057 2058 ((spec & ETOSpec.swizzle) && (rs == 1 || cs == 1) ? 2059 (rs*cs >= 1 ? 2060 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2061 q{ 2062 auto ref a() inout @property { return this[0]; } 2063 alias x = a; 2064 alias r = a; 2065 alias re = a; 2066 } : "" 2067 ) ~ 2068 (rs*cs >= 2 ? 2069 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2070 q{ 2071 auto ref b() inout @property { return this[1]; } 2072 alias y = b; 2073 alias im = b; 2074 alias i = b; 2075 } : "" 2076 ) ~ 2077 (rs*cs >= 3 ? 2078 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2079 q{ 2080 auto ref c() inout @property { return this[2]; } 2081 alias z = c; 2082 alias j = c; 2083 } : "" 2084 ) ~ 2085 (rs*cs >= 4 ? 2086 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2087 q{ 2088 auto ref d() inout @property { return this[3]; } 2089 alias k = d; 2090 alias w = d; 2091 } : "" 2092 ) ~ 2093 (rs*cs >= 5 ? 2094 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2095 q{ 2096 auto ref e() inout @property { return this[4]; } 2097 } : "" 2098 ) ~ 2099 (rs*cs >= 6 ? 2100 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2101 q{ 2102 auto ref f() inout @property { return this[5]; } 2103 } : "" 2104 ) ~ 2105 (rs*cs >= 7 ? 2106 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2107 q{ 2108 auto ref g() inout @property { return this[6]; } 2109 } : "" 2110 ) ~ 2111 (rs*cs >= 8 ? 2112 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2113 q{ 2114 auto ref h() inout @property { return this[7]; } 2115 } : "" 2116 ) : "" 2117 ); 2118 2119 2120 mixin template templateMixin() 2121 { 2122 mixin(stringMixin); 2123 } 2124 } 2125 2126 2127 /// ditto 2128 template ExpressionOperatorsInferable(size_t spec) 2129 { 2130 enum stringMixin = 2131 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2132 q{ 2133 alias at = opIndex; 2134 } ~ 2135 2136 2137 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2138 q{ 2139 bool opEquals(Rhs)(auto ref in Rhs mat) const 2140 if(isMatrix!Rhs && !is(Unqual!Rhs == typeof(this)) && !isAbstractMatrix!(Rhs)) 2141 { 2142 static assert(isValidOperator!(Unqual!(typeof(this)), "+", Rhs)); 2143 2144 if(!this.inferSize(mat.rows, mat.cols).isValid) 2145 return false; 2146 2147 foreach(i; 0 .. mat.rows) 2148 foreach(j; 0 .. mat.cols) 2149 if(this[i, j] != mat.at(i, j)) 2150 return false; 2151 return true; 2152 } 2153 } ~ 2154 2155 2156 (spec & ETOSpec.matrixAddMatrix ? 2157 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2158 q{ 2159 auto opBinary(string op : "+", Rhs)(auto ref const Rhs mat) const 2160 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 2161 in{ 2162 static if(!isAbstractMatrix!Rhs) 2163 assert(this.inferSize(mat.rows, mat.cols).isValid); 2164 } 2165 body{ 2166 static assert(isValidOperator!(typeof(this), op, Rhs)); 2167 return matrixExpression!"+"(this, mat); 2168 } 2169 } : "" 2170 ) ~ 2171 2172 2173 (spec & ETOSpec.matrixSubMatrix ? 2174 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2175 q{ 2176 auto opBinary(string op : "-", Rhs)(auto ref const Rhs mat) const 2177 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 2178 in{ 2179 static if(!isAbstractMatrix!Rhs) 2180 assert(this.inferSize(mat.rows, mat.cols).isValid); 2181 } 2182 body{ 2183 static assert(isValidOperator!(typeof(this), op, Rhs)); 2184 return matrixExpression!"-"(this, mat); 2185 } 2186 } : "" 2187 ) ~ 2188 2189 2190 (spec & ETOSpec.matrixMulMatrix ? 2191 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2192 q{ 2193 auto opBinary(string op : "*", Rhs)(auto ref const Rhs mat) const 2194 if(isMatrix!Rhs || isAbstractMatrix!Rhs) 2195 in{ 2196 static if(!isAbstractMatrix!Rhs) 2197 assert(this.inferSize(wild, mat.rows).isValid); 2198 } 2199 body{ 2200 static assert(isValidOperator!(typeof(this), op, Rhs)); 2201 return matrixExpression!"*"(this, mat); 2202 } 2203 } : "" 2204 ) ~ 2205 2206 2207 (spec & ETOSpec.matrixAddScalar ? 2208 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2209 q{ 2210 auto opBinary(string op : "+", S)(const S s) const 2211 if(isNotVectorOrMatrix!S) 2212 { 2213 static assert(isValidOperator!(typeof(this), op, S)); 2214 return matrixExpression!"+"(this, s); 2215 } 2216 } : "" 2217 ) ~ 2218 2219 2220 (spec & ETOSpec.scalarAddMatrix ? 2221 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2222 q{ 2223 auto opBinaryRight(string op : "+", S)(const S s) const 2224 if(isNotVectorOrMatrix!S) 2225 { 2226 static assert(isValidOperator!(S, op, typeof(this))); 2227 return matrixExpression!"+"(s, this); 2228 } 2229 } : "" 2230 ) ~ 2231 2232 2233 (spec & ETOSpec.matrixSubScalar ? 2234 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2235 q{ 2236 auto opBinary(string op : "-", S)(const S s) const 2237 if(isNotVectorOrMatrix!S) 2238 { 2239 static assert(isValidOperator!(typeof(this), op, S)); 2240 return matrixExpression!"-"(this, s); 2241 } 2242 } : "" 2243 ) ~ 2244 2245 2246 (spec & ETOSpec.scalarSubMatrix ? 2247 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2248 q{ 2249 auto opBinaryRight(string op : "-", S)(const S s) const 2250 if(isNotVectorOrMatrix!S) 2251 { 2252 static assert(isValidOperator!(S, op, typeof(this))); 2253 return matrixExpression!"-"(s, this); 2254 } 2255 } : "" 2256 ) ~ 2257 2258 2259 (spec & ETOSpec.matrixMulScalar ? 2260 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2261 q{ 2262 auto opBinary(string op : "*", S)(const S s) const 2263 if(isNotVectorOrMatrix!S) 2264 { 2265 static assert(isValidOperator!(typeof(this), op, S)); 2266 return matrixExpression!"*"(this, s); 2267 } 2268 } : "" 2269 ) ~ 2270 2271 2272 (spec & ETOSpec.scalarMulMatrix ? 2273 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2274 q{ 2275 auto opBinaryRight(string op : "*", S)(const S s) const 2276 if(isNotVectorOrMatrix!S) 2277 { 2278 static assert(isValidOperator!(S, op, typeof(this))); 2279 return matrixExpression!"*"(s, this); 2280 } 2281 } : "" 2282 ) ~ 2283 2284 2285 (spec & ETOSpec.matrixDivScalar ? 2286 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2287 q{ 2288 auto opBinary(string op : "/", S)(const S s) const 2289 if(isNotVectorOrMatrix!S) 2290 { 2291 static assert(isValidOperator!(typeof(this), op, S)); 2292 return matrixExpression!"/"(this, s); 2293 } 2294 } : "" 2295 ) ~ 2296 2297 (spec & ETOSpec.scalarDivMatrix ? 2298 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2299 q{ 2300 auto opBinaryRight(string op : "/", S)(const S s) const 2301 if(isNotVectorOrMatrix!S) 2302 { 2303 static assert(isValidOperator!(S, op, typeof(this))); 2304 return matrixExpression!"/"(s, this); 2305 } 2306 } : "" 2307 ) ~ 2308 2309 2310 (spec & ETOSpec.addScalar ? 2311 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2312 q{ 2313 void opOpAssign(string op : "+", S)(const S scalar) 2314 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] += scalar))) 2315 { 2316 foreach(r; 0 .. rows) 2317 foreach(c; 0 .. cols) 2318 this[r, c] += scalar; 2319 } 2320 } : "" 2321 ) ~ 2322 2323 2324 (spec & ETOSpec.subScalar ? 2325 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2326 q{ 2327 void opOpAssign(string op : "-", S)(const S scalar) 2328 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] -= scalar))) 2329 { 2330 foreach(r; 0 .. rows) 2331 foreach(c; 0 .. cols) 2332 this[r, c] -= scalar; 2333 } 2334 } : "" 2335 ) ~ 2336 2337 2338 (spec & ETOSpec.mulScalar ? 2339 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2340 q{ 2341 void opOpAssign(string op : "*", S)(const S scalar) 2342 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] *= scalar))) 2343 { 2344 foreach(r; 0 .. rows) 2345 foreach(c; 0 .. cols) 2346 this[r, c] *= scalar; 2347 } 2348 } : "" 2349 ) ~ 2350 2351 2352 (spec & ETOSpec.divScalar ? 2353 format(`#line %s "%s"`, __LINE__+2, __FILE__) ~ 2354 q{ 2355 void opOpAssign(string op : "+", S)(const S scalar) 2356 if(isNotVectorOrMatrix!S && is(typeof(this[0, 0] /= scalar))) 2357 { 2358 foreach(r; 0 .. rows) 2359 foreach(c; 0 .. cols) 2360 this[r, c] /= scalar; 2361 } 2362 } : "" 2363 ); 2364 2365 2366 mixin template templateMixin() 2367 { 2368 mixin(stringMixin); 2369 } 2370 } 2371 2372 2373 /** 2374 全ての演算子をオーバーロードします。 2375 */ 2376 template defaultExprOps(bool isInferable = false) 2377 { 2378 enum defaultExprOps = 2379 isInferable ? 2380 q{ 2381 mixin(ExpressionOperatorsInferable!(ETOSpec.all).stringMixin); 2382 //mixin(ExpressionOperatorsInferable!(ETOSpec.all & ~ETOSpec.opEquals & ~ETOSpec.toString).stringMixin); 2383 //mixin(ExpressionOperatorsInferable!(ETOSpec.modifiedOperator).stringMixin); 2384 //const{mixin(ExpressionOperatorsInferable!(ETOSpec.all & ~ETOSpec.modifiedOperator).stringMixin);} 2385 //immutable{mixin(ExpressionOperatorsInferable!(ETOSpec.all & ~ETOSpec.opEquals & ~ETOSpec.toString).stringMixin);} 2386 } 2387 : 2388 q{ 2389 mixin(ExpressionOperators!(ETOSpec.all, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 2390 //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); 2391 //mixin(ExpressionOperators!(ETOSpec.modifiedOperator, mixin(is(typeof({enum _unused_ = rows;})) ? "this.rows" : "wild"), mixin(is(typeof({enum _unused_ = cols;})) ? "this.cols" : "wild")).stringMixin); 2392 //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);} 2393 //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);} 2394 }; 2395 } 2396 2397 2398 unittest{ 2399 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2400 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2401 2402 2403 static struct M(size_t rs, size_t cs) 2404 { 2405 enum rows = rs; 2406 enum cols = cs; 2407 2408 size_t opIndex(size_t i, size_t j) inout {return i + j;} 2409 2410 //inout: 2411 mixin(defaultExprOps!(false)); 2412 } 2413 2414 2415 alias S3 = M!(3, 3); 2416 alias S23 = M!(2, 3); 2417 2418 static assert(isNarrowMatrix!S3); 2419 static assert(hasStaticRows!S3); 2420 static assert(hasStaticCols!S3); 2421 static assert(isNarrowMatrix!S23); 2422 static assert(hasStaticRows!S23); 2423 static assert(hasStaticCols!S23); 2424 2425 2426 M!(1, 1) m11; 2427 static assert(isNarrowMatrix!(typeof(m11))); 2428 size_t valueM11 = m11; 2429 2430 M!(3, 1) m31; 2431 M!(1, 3) m13; 2432 valueM11 = m13 * m31; 2433 2434 2435 // swizzle 2436 M!(1, 8) m18; 2437 assert(m18[0] == m18.a); 2438 assert(m18[1] == m18.b); 2439 assert(m18[2] == m18.c); 2440 assert(m18[3] == m18.d); 2441 assert(m18[4] == m18.e); 2442 assert(m18[5] == m18.f); 2443 assert(m18[6] == m18.g); 2444 assert(m18[7] == m18.h); 2445 2446 assert(m18[0] == m18.r); 2447 assert(m18[1] == m18.i); 2448 assert(m18[2] == m18.j); 2449 assert(m18[3] == m18.k); 2450 2451 assert(m18[0] == m18.x); 2452 assert(m18[1] == m18.y); 2453 assert(m18[2] == m18.z); 2454 assert(m18[3] == m18.w); 2455 2456 assert(m18[0] == m18.re); 2457 assert(m18[1] == m18.im); 2458 2459 2460 static struct I 2461 { 2462 size_t opIndex(size_t i, size_t j) inout { return i == j ? 1 : 0;} 2463 alias at = opIndex; 2464 2465 static InferredResult inferSize(Msize_t r, Msize_t c) 2466 { 2467 if(r == wild && c == wild) 2468 return InferredResult(false); 2469 else if(r == c || r == wild || c == wild) 2470 return InferredResult(true, max(r, c), max(r, c)); 2471 else 2472 return InferredResult(false); 2473 } 2474 2475 mixin(defaultExprOps!(true)); 2476 } 2477 2478 //static assert(isNarrowMatrix!I); 2479 static assert(isAbstractMatrix!I); 2480 static assert( I.inferSize(wild, 1).isValid); 2481 static assert( I.inferSize(3, 3).isValid); 2482 static assert(!I.inferSize(1, 3).isValid); 2483 2484 2485 static struct D{ 2486 size_t rows; 2487 size_t cols; 2488 2489 size_t opIndex(size_t i, size_t j) inout {return i + j;} 2490 2491 mixin(defaultExprOps!(false)); 2492 } 2493 static assert(isNarrowMatrix!D); 2494 static assert(hasDynamicRows!D); 2495 static assert(hasDynamicColumns!D); 2496 2497 2498 S3 a; 2499 auto add = a + a; 2500 static assert(isNarrowMatrix!(typeof(add))); 2501 static assert(hasStaticRows!(typeof(add))); 2502 static assert(hasStaticCols!(typeof(add))); 2503 assert(add[0, 0] == 0); assert(add[0, 1] == 2); assert(add[0, 2] == 4); 2504 assert(add[1, 0] == 2); assert(add[1, 1] == 4); assert(add[1, 2] == 6); 2505 assert(add[2, 0] == 4); assert(add[2, 1] == 6); assert(add[2, 2] == 8); 2506 2507 auto mul = a * a; 2508 static assert(isNarrowMatrix!(typeof(mul))); 2509 static assert(hasStaticRows!(typeof(mul))); 2510 static assert(hasStaticCols!(typeof(mul))); 2511 assert(mul[0, 0] == 5); assert(mul[0, 1] == 8); assert(mul[0, 2] ==11); 2512 assert(mul[1, 0] == 8); assert(mul[1, 1] ==14); assert(mul[1, 2] ==20); 2513 assert(mul[2, 0] ==11); assert(mul[2, 1] ==20); assert(mul[2, 2] ==29); 2514 2515 auto sadd = a + 3; 2516 static assert(isNarrowMatrix!(typeof(sadd))); 2517 static assert(hasStaticRows!(typeof(sadd))); 2518 static assert(hasStaticCols!(typeof(sadd))); 2519 assert(sadd[0, 0] == 3); assert(sadd[0, 1] == 4); assert(sadd[0, 2] == 5); 2520 assert(sadd[1, 0] == 4); assert(sadd[1, 1] == 5); assert(sadd[1, 2] == 6); 2521 assert(sadd[2, 0] == 5); assert(sadd[2, 1] == 6); assert(sadd[2, 2] == 7); 2522 2523 // auto addMasS = a + m11; 2524 2525 auto add5 = a + a + cast(const)(a) * 3; 2526 static assert(isNarrowMatrix!(typeof(add5))); 2527 static assert(hasStaticRows!(typeof(add5))); 2528 static assert(hasStaticCols!(typeof(add5))); 2529 assert(add5 == a * 5); 2530 2531 I i; 2532 auto addi = a + i; 2533 static assert(isNarrowMatrix!(typeof(addi))); 2534 static assert(hasStaticRows!(typeof(addi))); static assert(typeof(addi).rows == 3); 2535 static assert(hasStaticCols!(typeof(addi))); static assert(typeof(addi).cols == 3); 2536 assert(addi[0, 0] == 1); assert(addi[0, 1] == 1); assert(addi[0, 2] == 2); 2537 assert(addi[1, 0] == 1); assert(addi[1, 1] == 3); assert(addi[1, 2] == 3); 2538 assert(addi[2, 0] == 2); assert(addi[2, 1] == 3); assert(addi[2, 2] == 5); 2539 2540 auto i2 = i * 2; 2541 static assert(!isNarrowMatrix!(typeof(i2))); 2542 static assert(isAbstractMatrix!(typeof(i2))); 2543 static assert( typeof(i2).inferSize(wild, 1).isValid); 2544 static assert( typeof(i2).inferSize(3, 3).isValid); 2545 static assert(!typeof(i2).inferSize(1, 3).isValid); 2546 2547 auto addi2 = a + i2; 2548 static assert(isNarrowMatrix!(typeof(addi2))); 2549 static assert(hasStaticRows!(typeof(addi2))); static assert(typeof(addi2).rows == 3); 2550 static assert(hasStaticCols!(typeof(addi2))); static assert(typeof(addi2).cols == 3); 2551 assert(addi2[0, 0] == 2); assert(addi2[0, 1] == 1); assert(addi2[0, 2] == 2); 2552 assert(addi2[1, 0] == 1); assert(addi2[1, 1] == 4); assert(addi2[1, 2] == 3); 2553 assert(addi2[2, 0] == 2); assert(addi2[2, 1] == 3); assert(addi2[2, 2] == 6); 2554 2555 static assert(!is(typeof(S23.init + i))); 2556 static assert(!is(typeof(i + S23.init))); 2557 assert(S23.init * i == S23.init); 2558 assert(i * S23.init == S23.init); 2559 2560 2561 import core.exception, std.exception; 2562 2563 D d33 = D(3, 3); 2564 auto addsd = a + d33; 2565 static assert(isNarrowMatrix!(typeof(addsd))); 2566 static assert(hasStaticRows!(typeof(addsd))); 2567 static assert(hasStaticCols!(typeof(addsd))); 2568 assert(addsd == a * 2); 2569 assert(addsd == d33 * 2); 2570 2571 auto addsdr = d33 + a; 2572 static assert(isNarrowMatrix!(typeof(addsdr))); 2573 static assert(hasStaticRows!(typeof(addsdr))); 2574 static assert(hasStaticCols!(typeof(addsdr))); 2575 assert(addsdr == addsd); 2576 assert(addsdr == addsd); 2577 2578 version(Release){}else assert(collectException!AssertError(D(2, 3) + a)); 2579 version(Release){}else assert(collectException!AssertError(D(2, 3) + i)); 2580 assert(D(2, 3) * i == D(2, 3)); 2581 2582 version(Release){}else assert(collectException!AssertError(D(2, 3) + D(2, 2))); 2583 version(Release){}else assert(collectException!AssertError(D(2, 3) + D(3, 3))); 2584 assert((D(2, 3) + D(2, 3)).rows == 2); 2585 assert((D(2, 3) + D(2, 3)).cols == 3); 2586 assert((D(2, 3) * D(3, 4)).rows == 2); 2587 assert((D(2, 3) * D(3, 4)).cols == 4); 2588 2589 auto mulds = d33 * 3; 2590 assert(mulds == d33 + d33 + d33); 2591 } 2592 2593 2594 /** 2595 InferableMatrixをある大きさの行列へ固定します。 2596 */ 2597 auto congeal(Msize_t rs, Msize_t cs, A)(A mat) 2598 if(isAbstractMatrix!A && A.inferSize(rs, cs).isValid) 2599 { 2600 static struct Result() 2601 { 2602 enum size_t rows = A.inferSize(rs, cs).rows; 2603 enum size_t cols = A.inferSize(rs, cs).cols; 2604 2605 auto ref opIndex(size_t i, size_t j) inout 2606 in{ 2607 assert(i < rows); 2608 assert(j < cols); 2609 } 2610 body{ 2611 return _mat[i, j]; 2612 } 2613 2614 2615 static if(carbon.linear.hasAssignableElements!A) 2616 { 2617 void opIndexAssign(E)(E e, size_t i, size_t j) 2618 in{ 2619 assert(i < rows); 2620 assert(j < cols); 2621 } 2622 body{ 2623 _mat[i, j] = e; 2624 } 2625 } 2626 2627 mixin(defaultExprOps!(false)); 2628 2629 private: 2630 A _mat; 2631 } 2632 2633 return Result!()(mat); 2634 } 2635 2636 /// 2637 unittest{ 2638 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2639 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2640 2641 2642 static struct I{ 2643 2644 size_t opIndex(size_t i, size_t j) inout { return i == j ? 1 : 0;} 2645 2646 static InferredResult inferSize(Msize_t r, Msize_t c) 2647 { 2648 if(r == wild && c == wild) 2649 return InferredResult(false); 2650 else if(isEqOrEitherEqX(r, c, wild)) 2651 return InferredResult(true, max(r, c), max(r, c)); 2652 else 2653 return InferredResult(false); 2654 } 2655 2656 mixin(defaultExprOps!(true)); 2657 } 2658 2659 static assert(!isNarrowMatrix!I); 2660 static assert(isAbstractMatrix!I); 2661 static assert( I.inferSize(wild, 1).isValid); 2662 static assert( I.inferSize(3, 3).isValid); 2663 static assert(!I.inferSize(1, 3).isValid); 2664 2665 I id; 2666 auto i3x3 = id.congeal!(3, 3)(); 2667 static assert(isNarrowMatrix!(typeof(i3x3))); 2668 static assert(hasStaticRows!(typeof(i3x3))); 2669 static assert(hasStaticCols!(typeof(i3x3))); 2670 static assert(i3x3.rows == 3); 2671 static assert(i3x3.cols == 3); 2672 assert(i3x3[0, 0] == 1);assert(i3x3[1, 0] == 0);assert(i3x3[2, 0] == 0); 2673 assert(i3x3[0, 1] == 0);assert(i3x3[1, 1] == 1);assert(i3x3[2, 1] == 0); 2674 assert(i3x3[0, 2] == 0);assert(i3x3[1, 2] == 0);assert(i3x3[2, 2] == 1); 2675 } 2676 2677 2678 /// ditto 2679 auto congeal(A)(auto ref A mat, size_t r, size_t c) 2680 if(isAbstractMatrix!A) 2681 in{ 2682 assert(A.inferSize(r, c).isValid); 2683 } 2684 body{ 2685 static struct Result() 2686 { 2687 @property size_t rows() const { return _rs; } 2688 @property size_t cols() const { return _cs; } 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 size_t _rs, _cs; 2715 A _mat; 2716 } 2717 2718 2719 return Result!()(r, c, mat); 2720 } 2721 2722 2723 /// 2724 unittest{ 2725 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2726 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2727 2728 2729 static struct I{ 2730 2731 size_t opIndex(size_t i, size_t j) inout { return i == j ? 1 : 0;} 2732 2733 static InferredResult inferSize(Msize_t r, Msize_t c) 2734 { 2735 if(r == wild && c == wild) 2736 return InferredResult(false); 2737 else if(isEqOrEitherEqX(r, c, wild)) 2738 return InferredResult(true, max(r, c), max(r, c)); 2739 else 2740 return InferredResult(false); 2741 } 2742 2743 mixin(defaultExprOps!(true)); 2744 } 2745 2746 static assert(!isNarrowMatrix!I); 2747 static assert(isAbstractMatrix!I); 2748 static assert( I.inferSize(wild, 1).isValid); 2749 static assert( I.inferSize(3, 3).isValid); 2750 static assert(!I.inferSize(1, 3).isValid); 2751 2752 I id; 2753 auto i3x3 = id.congeal(3, 3); 2754 static assert(isNarrowMatrix!(typeof(i3x3))); 2755 static assert(hasDynamicRows!(typeof(i3x3))); 2756 static assert(hasDynamicColumns!(typeof(i3x3))); 2757 assert(i3x3.rows == 3); 2758 assert(i3x3.cols == 3); 2759 assert(i3x3[0, 0] == 1);assert(i3x3[1, 0] == 0);assert(i3x3[2, 0] == 0); 2760 assert(i3x3[0, 1] == 0);assert(i3x3[1, 1] == 1);assert(i3x3[2, 1] == 0); 2761 assert(i3x3[0, 2] == 0);assert(i3x3[1, 2] == 0);assert(i3x3[2, 2] == 1); 2762 } 2763 2764 2765 /** 2766 DynamicMatrixをある大きさへ固定します。 2767 */ 2768 auto congeal(Msize_t rs, Msize_t cs, A)(auto ref A mat) 2769 //if(!isAbstractMatrix!A && (hasDynamicRows!A || rs == A.rows) || (hasDynamicColumns!A || cs == A.cols)) 2770 if(is(typeof({ 2771 static assert(!isAbstractMatrix!A); 2772 static if(hasStaticRows!A) 2773 static assert(rs == staticRows!A); 2774 static if(hasStaticCols!A) 2775 static assert(cs == staticCols!A); 2776 }))) 2777 in{ 2778 assert(mat.rows == rs); 2779 assert(mat.cols == cs); 2780 } 2781 body{ 2782 static struct Result() 2783 { 2784 enum rows = rs; 2785 enum cols = cs; 2786 2787 2788 auto ref opIndex(size_t i, size_t j) inout 2789 in{ 2790 assert(i < rows); 2791 assert(j < cols); 2792 } 2793 body{ 2794 return _mat[i, j]; 2795 } 2796 2797 2798 static if(carbon.linear.hasAssignableElements!A) 2799 { 2800 void opIndexAssign(E)(E e, size_t i, size_t j) 2801 in{ 2802 assert(i < rows); 2803 assert(j < cols); 2804 } 2805 body{ 2806 _mat[i, j] = e; 2807 } 2808 } 2809 2810 2811 mixin(defaultExprOps!(false)); 2812 2813 private: 2814 A _mat; 2815 } 2816 2817 return Result!()(mat); 2818 } 2819 2820 2821 /// ditto 2822 unittest{ 2823 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2824 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 2825 2826 2827 static struct D{ 2828 size_t rows; 2829 size_t cols; 2830 2831 size_t opIndex(size_t i, size_t j) inout {return i + j;} 2832 2833 mixin(defaultExprOps!(false)); 2834 } 2835 static assert(isNarrowMatrix!D); 2836 static assert(hasDynamicRows!D); 2837 static assert(hasDynamicColumns!D); 2838 2839 D d3x3 = D(3, 3); 2840 auto s3x3 = d3x3.congeal!(3, 3)(); 2841 static assert(isNarrowMatrix!(typeof(s3x3))); 2842 static assert(s3x3.rows == 3); 2843 static assert(s3x3.cols == 3); 2844 assert(s3x3[0, 0] == 0); assert(s3x3[1, 0] == 1); assert(s3x3[2, 0] == 2); 2845 assert(s3x3[0, 1] == 1); assert(s3x3[1, 1] == 2); assert(s3x3[2, 1] == 3); 2846 assert(s3x3[0, 2] == 2); assert(s3x3[1, 2] == 3); assert(s3x3[2, 2] == 4); 2847 } 2848 2849 2850 /// 2851 auto matrix(size_t rs, size_t cs, alias f, S...)(S sizes) 2852 if(is(typeof(f(0, 0))) && S.length == ((rs == 0) + (cs == 0)) && (S.length == 0 || is(CommonType!S : size_t))) 2853 { 2854 static struct Result() 2855 { 2856 static if(rs) 2857 enum rows = rs; 2858 else{ 2859 private size_t _rs; 2860 @property size_t rows() const { return _rs; } 2861 } 2862 2863 static if(cs) 2864 enum cols = cs; 2865 else{ 2866 private size_t _cs; 2867 @property size_t cols() const { return _cs; } 2868 } 2869 2870 2871 auto ref opIndex(size_t i, size_t j) const 2872 in{ 2873 assert(i < rows); 2874 assert(j < cols); 2875 } 2876 body{ 2877 return f(i, j); 2878 } 2879 2880 2881 mixin(defaultExprOps!(false)); 2882 } 2883 2884 static if(S.length == 0) 2885 return Result!()(); 2886 else static if(S.length == 1) 2887 return Result!()(sizes[0]); 2888 else static if(S.length == 2) 2889 return Result!()(sizes[0], sizes[1]); 2890 } 2891 2892 2893 auto matrix(alias f)(size_t r, size_t c) 2894 if(is(typeof(f(0, 0)))) 2895 { 2896 return matrix!(0, 0, f)(r, c); 2897 } 2898 2899 2900 unittest{ 2901 auto m = matrix!(2, 3, (i, j) => i * 3 + j); 2902 static assert(isNarrowMatrix!(typeof(m))); 2903 static assert(hasStaticRows!(typeof(m))); 2904 static assert(hasStaticCols!(typeof(m))); 2905 static assert(m.rows == 2); 2906 static assert(m.cols == 3); 2907 assert(m[0, 0] == 0); assert(m[0, 1] == 1); assert(m[0, 2] == 2); 2908 assert(m[1, 0] == 3); assert(m[1, 1] == 4); assert(m[1, 2] == 5); 2909 2910 2911 auto md = matrix!((i, j) => i * 3 + j)(2, 3); 2912 assert(m == md); 2913 } 2914 2915 2916 auto matrix(alias f)() 2917 { 2918 static struct Result() 2919 { 2920 2921 2922 static InferredResult inferSize(Msize_t r, Msize_t c) 2923 { 2924 if(r == wild && c == wild) 2925 return InferredResult(false); 2926 else if(r == wild || c == wild) 2927 return InferredResult(true, max(r, c), max(r, c)); 2928 else 2929 return InferredResult(true, r, c); 2930 } 2931 2932 2933 auto ref opIndex(size_t i, size_t j) const { return f(i, j); } 2934 2935 2936 mixin(defaultExprOps!(true)); 2937 } 2938 2939 return Result!()(); 2940 } 2941 unittest{ 2942 auto mi = matrix!((i, j) => i * 3 + j); 2943 static assert(isAbstractMatrix!(typeof(mi))); 2944 assert(mi == matrix!((i, j) => i * 3 + j)(2, 3)); 2945 } 2946 2947 2948 /** 2949 内部で2次元配列を持つ行列です 2950 */ 2951 struct DMatrix(T, Msize_t rs = dynamic, Msize_t cs = dynamic, Major mjr = Major.row) 2952 { 2953 enum bool isColumnMajor = mjr == Major.column; 2954 enum bool isRowMajor = mjr == Major.row; 2955 enum major = mjr; 2956 2957 2958 this(M)(auto ref M m) 2959 if(!is(M == typeof(this))) 2960 { 2961 static if(isMatrix!M) 2962 this.noAlias = m; 2963 else 2964 this.opAssign(m); 2965 } 2966 2967 2968 static if(rs != dynamic) 2969 { 2970 enum size_t rows = rs; 2971 } 2972 else 2973 { 2974 @property 2975 size_t rows() const 2976 { 2977 static if(cs == 1) 2978 return _array.length; 2979 else static if(isColumnMajor) 2980 { 2981 if(this.tupleof[0].length) 2982 return this.tupleof[0][0].length; 2983 else 2984 return 0; 2985 } 2986 else 2987 return this.tupleof[0].length; 2988 } 2989 2990 2991 @property 2992 void rows(size_t newSize) 2993 { 2994 static if(rs == 1) 2995 return _array.length; 2996 else static if(isColumnMajor) 2997 { 2998 foreach(ref e; _array) 2999 e.length = newSize; 3000 } 3001 else 3002 _array.length = newSize; 3003 } 3004 } 3005 3006 static if(cs != dynamic) 3007 { 3008 enum size_t cols = cs; 3009 } 3010 else 3011 { 3012 @property 3013 size_t cols() const 3014 { 3015 static if(rs == 1) 3016 return _array.length; 3017 else static if(isRowMajor){ 3018 if(this.tupleof[0].length) 3019 return this.tupleof[0][0].length; 3020 else 3021 return 0; 3022 } 3023 else 3024 return this.tupleof[0].length; 3025 } 3026 3027 3028 @property 3029 void cols(size_t newSize) 3030 { 3031 static if(rs == 1) 3032 return _array.length = newSize; 3033 else static if(isRowMajor) 3034 { 3035 foreach(ref e; _array) 3036 e.length = newSize; 3037 } 3038 else 3039 _array.length = newSize; 3040 } 3041 } 3042 3043 3044 auto ref opIndex(size_t i, size_t j) inout 3045 in{ 3046 assert(i < rows); 3047 assert(j < cols); 3048 } 3049 body{ 3050 static if(rs == 1 || cs == 1) 3051 return _array[i+j]; 3052 else static if(isRowMajor) 3053 return _array[i][j]; 3054 else 3055 return _array[j][i]; 3056 } 3057 3058 3059 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); 3060 //mixin(defaultExprOps!(false)); 3061 3062 3063 void opAssign(M)(auto ref M mat) 3064 if(!is(typeof(this) == M) && isValidOperator!(typeof(this), "=", M)) 3065 in{ 3066 static if(rs && hasDynamicRows!M) 3067 assert(this.rows == mat.rows); 3068 3069 static if(cs && hasDynamicColumns!M) 3070 assert(this.cols == mat.cols); 3071 3072 static if(isAbstractMatrix!M) 3073 assert(mat.inferSize(this.rows, this.cols).isValid); 3074 } 3075 body 3076 { 3077 static if(isAbstractMatrix!M) 3078 { 3079 immutable rl = this.rows, 3080 cl = this.cols; 3081 } 3082 else 3083 { 3084 immutable rl = mat.rows, 3085 cl = mat.cols; 3086 } 3087 3088 immutable n = rl * cl; 3089 3090 if(_buffer.length < n) 3091 _buffer.length = n; 3092 3093 foreach(i; 0 .. rl) 3094 foreach(j; 0 .. cl) 3095 { 3096 static if(major == Major.row) 3097 _buffer[i * cl + j] = mat.at(i, j); 3098 else 3099 _buffer[j * rl + i] = mat.at(i, j); 3100 } 3101 3102 static if(rs == dynamic) 3103 this.rows = rl; 3104 3105 static if(cs == dynamic) 3106 this.cols = cl; 3107 3108 static if(rs == 1 || cs == 1) 3109 _array[] = _buffer[0 .. n][]; 3110 else{ 3111 foreach(i; 0 .. _array.length) 3112 _array[i][] = _buffer[i * _array[0].length .. (i+1) * _array[0].length][]; 3113 } 3114 } 3115 3116 3117 void opAssign(X)(X m) 3118 if(!isNarrowMatrix!X && isRandomAccessRange!X && (rs || !isInfinite!X) 3119 && isRandomAccessRange!(std.range.ElementType!X) 3120 && (cs || !isInfinite!(std.range.ElementType!X)) 3121 && isAssignable!(T, std.range.ElementType!X)) 3122 { 3123 if(m.length && m[0].length){ 3124 static if(rs == dynamic){ 3125 if(m.length != this.rows) 3126 this.rows = m.length; 3127 } 3128 3129 static if(cs == dynamic){ 3130 if(m[0].length != this.cols) 3131 this.cols = m[0].length; 3132 } 3133 3134 foreach(i; 0 .. this.rows) 3135 foreach(j; 0 .. this.cols) 3136 this[i, j] = m[i][j]; 3137 } 3138 } 3139 3140 3141 void opAssign(X)(X m) 3142 if(!isNarrowMatrix!X && isRandomAccessRange!X && isAssignable!(T, std.range.ElementType!X)) 3143 { 3144 foreach(i; 0 .. this.rows) 3145 foreach(j; 0 .. this.cols) 3146 this[i, j] = m[i * this.cols + j]; 3147 } 3148 3149 3150 static if(rs == 1 || cs == 1) 3151 { 3152 void opAssign(Array)(Array arr) 3153 if(!isNarrowMatrix!Array && isAssignable!(T, typeof(arr[size_t.init]))) 3154 { 3155 foreach(i; 0 .. this.length) 3156 this[i] = arr[i]; 3157 } 3158 } 3159 3160 3161 @property 3162 void noAlias(M)(auto ref M mat) 3163 if(isValidOperator!(typeof(this), "=", M) && ((!rs || !hasStaticRows!M) || is(typeof({static assert(this.rows == M.rows);}))) 3164 && ((!cs || !hasStaticCols!M) || is(typeof({static assert(this.cols == M.cols);})))) 3165 in{ 3166 static if(rs != dynamic) 3167 assert(this.rows == mat.rows); 3168 3169 static if(cs != dynamic) 3170 assert(this.cols == mat.cols); 3171 } 3172 body{ 3173 static if(is(typeof(this) == M)) 3174 this = mat; 3175 else 3176 { 3177 immutable rl = mat.rows, 3178 cl = mat.cols, 3179 n = rl * cl; 3180 3181 static if(is(typeof(_array) == T[])) 3182 { 3183 if(_array.length < n) 3184 _array.length = n; 3185 } 3186 else 3187 { 3188 immutable x = (major == Major.row) ? rl : cl, 3189 y = (major == Major.row) ? cl : rl; 3190 3191 if(_array.length < x) 3192 _array.length = x; 3193 3194 foreach(ref e; _array) 3195 if(e.length < y) 3196 e.length = y; 3197 } 3198 3199 static if(rs == dynamic) 3200 this.rows = rl; 3201 3202 static if(cs == dynamic) 3203 this.cols = cl; 3204 3205 foreach(i; 0 .. rl) 3206 foreach(j; 0 .. cl) 3207 { 3208 static if(rs == 1 || cs == 1) 3209 _array[i+j] = mat.at(i, j); 3210 else static if(major == Major.row) 3211 _array[i][j] = mat.at(i, j); 3212 else 3213 _array[j][i] = mat.at(i, j); 3214 } 3215 } 3216 } 3217 3218 3219 @property 3220 auto reference() inout 3221 { 3222 return this; 3223 } 3224 3225 3226 private: 3227 static if(rs == 1 || cs == 1) 3228 { 3229 T[] _array; 3230 } 3231 else 3232 { 3233 T[][] _array; 3234 } 3235 3236 static: 3237 T[] _buffer; 3238 } 3239 3240 unittest{ 3241 DMatrix!float m = 3242 [[1, 2, 3], 3243 [4, 5, 6]]; 3244 3245 assert(m.rows == 2); 3246 assert(m.cols == 3); 3247 3248 assert(m[0, 0] == 1); 3249 assert(m[0, 1] == 2); 3250 assert(m[0, 2] == 3); 3251 assert(m[1, 0] == 4); 3252 assert(m[1, 1] == 5); 3253 assert(m[1, 2] == 6); 3254 } 3255 3256 3257 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...) 3258 if(N == (r == dynamic) + (c == dynamic)) 3259 { 3260 typeof(return) dst; 3261 3262 static if(r != dynamic) 3263 immutable size_t rs = r; 3264 else 3265 immutable size_t rs = size[0]; 3266 3267 static if(c != dynamic) 3268 immutable size_t cs = c; 3269 else 3270 immutable size_t cs = size[$-1]; 3271 3272 immutable h = mjr == Major.row ? rs : cs, 3273 w = mjr == Major.row ? cs : rs; 3274 3275 static if(r == 1 || c == 1) 3276 dst._array = new T[h * w]; 3277 else 3278 dst._array = new T[][](h, w); 3279 3280 return dst; 3281 } 3282 3283 unittest{ 3284 foreach(major; TypeTuple!(Major.row, Major.column)) 3285 { 3286 auto mr23 = matrix!(int, 2, 4, major)(); 3287 static assert(isNarrowMatrix!(typeof(mr23))); 3288 static assert(hasStaticRows!(typeof(mr23))); 3289 static assert(hasStaticCols!(typeof(mr23))); 3290 static assert(mr23.rows == 2); 3291 static assert(mr23.cols == 4); 3292 3293 auto mr2_ = matrix!(int, 2, dynamic, major)(4); 3294 static assert(isNarrowMatrix!(typeof(mr2_))); 3295 static assert(hasStaticRows!(typeof(mr2_))); 3296 static assert(hasDynamicColumns!(typeof(mr2_))); 3297 static assert(mr2_.rows == 2); 3298 assert(mr2_.cols == 4); 3299 3300 auto mr_2 = matrix!(int, dynamic, 2, major)(4); 3301 static assert(isNarrowMatrix!(typeof(mr_2))); 3302 static assert(hasDynamicRows!(typeof(mr_2))); 3303 static assert(hasStaticCols!(typeof(mr_2))); 3304 assert(mr_2.rows == 4); 3305 static assert(mr_2.cols == 2); 3306 3307 auto mr__ = matrix!(int, dynamic, dynamic, major)(2, 4); 3308 static assert(isNarrowMatrix!(typeof(mr__))); 3309 static assert(hasDynamicRows!(typeof(mr__))); 3310 static assert(hasDynamicColumns!(typeof(mr__))); 3311 assert(mr__.rows == 2); 3312 assert(mr__.cols == 4); 3313 } 3314 } 3315 3316 3317 DMatrix!(T, r, c, mjr) matrix(size_t r, size_t c, Major mjr = Major.row, T)(T[] arr) 3318 if(r != 0 || c != 0) 3319 in{ 3320 static if(r != 0 && c != 0) 3321 assert(arr.length == r * c); 3322 else{ 3323 assert(!(arr.length % r + c)); 3324 } 3325 } 3326 body{ 3327 static if(r == 1 || c == 1) 3328 { 3329 typeof(return) dst; 3330 dst._array = arr; 3331 return dst; 3332 } 3333 else 3334 { 3335 immutable rs = r == 0 ? arr.length / c : r; 3336 immutable cs = c == 0 ? arr.length / r : c; 3337 3338 immutable h = mjr == Major.row ? rs : cs, 3339 w = mjr == Major.row ? cs : rs; 3340 3341 typeof(return) dst; 3342 dst._array.length = h; 3343 foreach(i; 0 .. h) 3344 dst._array[i] = arr[i * w .. (i+1) * w]; 3345 3346 return dst; 3347 } 3348 } 3349 3350 unittest{ 3351 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3352 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3353 3354 auto mr = matrix!(2, 3)([0, 1, 2, 3, 4, 5]); 3355 static assert(isNarrowMatrix!(typeof(mr))); 3356 static assert(hasStaticRows!(typeof(mr))); 3357 static assert(hasStaticCols!(typeof(mr))); 3358 static assert(mr.rows == 2); 3359 static assert(mr.cols == 3); 3360 assert(mr[0, 0] == 0); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3361 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3362 3363 mr[0, 0] = 10; 3364 assert(mr[0, 0] == 10); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3365 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3366 3367 mr = matrix!(2, 3, (i, j) => cast(int)((i*3+j)*2)); 3368 assert(mr[0, 0] == 0); assert(mr[0, 1] == 2); assert(mr[0, 2] == 4); 3369 assert(mr[1, 0] == 6); assert(mr[1, 1] == 8); assert(mr[1, 2] == 10); 3370 3371 auto mc = matrix!(2, 3, Major.column)([0, 1, 2, 3, 4, 5]); 3372 static assert(isNarrowMatrix!(typeof(mc))); 3373 static assert(hasStaticRows!(typeof(mc))); 3374 static assert(hasStaticCols!(typeof(mc))); 3375 static assert(mc.rows == 2); 3376 static assert(mc.cols == 3); 3377 assert(mc[0, 0] == 0); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3378 assert(mc[1, 0] == 1); assert(mc[1, 1] == 3); assert(mc[1, 2] == 5); 3379 3380 mc[0, 0] = 10; 3381 assert(mc[0, 0] == 10); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3382 assert(mc[1, 0] == 1); assert(mc[1, 1] == 3); assert(mc[1, 2] == 5); 3383 3384 mc = matrix!(2, 3, (i, j) => cast(int)((i*3+j)*2)); 3385 assert(mc[0, 0] == 0); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3386 assert(mc[1, 0] == 6); assert(mc[1, 1] == 8); assert(mc[1, 2] == 10); 3387 } 3388 3389 3390 DMatrix!(T, wild, wild, mjr) matrix(Major mjr = Major.row, T)(T[] arr, size_t r, size_t c) 3391 in{ 3392 if(r != 0 && c != 0) 3393 assert(arr.length == r * c); 3394 else if(r != 0 || c != 0) 3395 assert(!(arr.length % (r + c))); 3396 else 3397 assert(0); 3398 } 3399 body{ 3400 immutable rs = r == 0 ? arr.length / c : r; 3401 immutable cs = c == 0 ? arr.length / r : c; 3402 3403 immutable h = mjr == Major.row ? rs : cs, 3404 w = mjr == Major.row ? rs : cs; 3405 3406 typeof(return) dst; 3407 dst._array.length = h; 3408 foreach(i; 0 .. h) 3409 dst._array[i] = arr[i * w .. (i+1) * w]; 3410 3411 return dst; 3412 } 3413 3414 unittest{ 3415 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3416 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3417 3418 auto mr = matrix!(2, 3)([0, 1, 2, 3, 4, 5]); 3419 static assert(isNarrowMatrix!(typeof(mr))); 3420 static assert(hasStaticRows!(typeof(mr))); 3421 static assert(hasStaticCols!(typeof(mr))); 3422 static assert(mr.rows == 2); 3423 static assert(mr.cols == 3); 3424 assert(mr[0, 0] == 0); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3425 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3426 3427 3428 auto mc = matrix!(2, 3, Major.column)([0, 1, 2, 3, 4, 5]); 3429 static assert(isNarrowMatrix!(typeof(mc))); 3430 static assert(hasStaticRows!(typeof(mc))); 3431 static assert(hasStaticCols!(typeof(mc))); 3432 static assert(mc.rows == 2); 3433 static assert(mc.cols == 3); 3434 assert(mc[0, 0] == 0); assert(mc[0, 1] == 2); assert(mc[0, 2] == 4); 3435 assert(mc[1, 0] == 1); assert(mc[1, 1] == 3); assert(mc[1, 2] == 5); 3436 } 3437 3438 3439 auto matrix(Major mjr = Major.row, T, size_t N, size_t M)(ref T[M][N] arr) 3440 { 3441 static if(mjr == Major.row) 3442 DMatrix!(T, N, M, mjr) dst; 3443 else 3444 DMatrix!(T, M, N, mjr) dst; 3445 3446 dst._array.length = N; 3447 foreach(i; 0 .. N) 3448 dst._array[i] = arr[i]; 3449 return dst; 3450 } 3451 3452 unittest{ 3453 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3454 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3455 3456 int[3][2] arr = [[0, 1, 2], [3, 4, 5]]; 3457 3458 auto mr = matrix(arr); 3459 static assert(isNarrowMatrix!(typeof(mr))); 3460 static assert(hasStaticRows!(typeof(mr))); 3461 static assert(hasStaticCols!(typeof(mr))); 3462 static assert(mr.rows == 2); 3463 static assert(mr.cols == 3); 3464 assert(mr[0, 0] == 0); assert(mr[0, 1] == 1); assert(mr[0, 2] == 2); 3465 assert(mr[1, 0] == 3); assert(mr[1, 1] == 4); assert(mr[1, 2] == 5); 3466 } 3467 3468 auto matrix(Major mjr = Major.row, A)(A mat) 3469 if(isNarrowMatrix!A && !isAbstractMatrix!A) 3470 { 3471 static if(hasStaticRows!A && hasStaticCols!A) 3472 DMatrix!(ElementType!A, A.rows, A.cols, mjr) dst; 3473 else static if(hasStaticRows!A) 3474 DMatrix!(ElementType!A, A.rows, wild, mjr) dst; 3475 else static if(hasStaticCols!A) 3476 DMatrix!(ElementType!A, wild, A.cols, mjr) dst; 3477 else 3478 DMatrix!(ElementType!A, wild, wild, mjr) dst; 3479 3480 dst.noAlias = mat; 3481 return dst; 3482 } 3483 3484 3485 auto matrix(Msize_t rs, Msize_t cs = 0, Major mjr = Major.row, A)(A mat) 3486 if(isAbstractMatrix!A && A.inferSize(rs, cs).isValid) 3487 { 3488 return mat.congeal!(rs, cs).matrix(); 3489 } 3490 3491 3492 auto matrix(Major mjr = Major.row, A)(A mat, size_t r, size_t c = 0) 3493 if(isAbstractMatrix!A) 3494 in{ 3495 assert(A.inferSize(r, c).isValid); 3496 } 3497 body{ 3498 return mat.congeal(r, c).matrix(); 3499 } 3500 3501 3502 /** 3503 要素がメモリ上に連続して存在するような行列 3504 */ 3505 struct SMatrix(T, Msize_t rs = 0, Msize_t cs = 0, Major mjr = Major.row) 3506 if(rs >= 0 && cs >= 0) 3507 { 3508 enum bool isColumnMajor = mjr == Major.column; 3509 enum bool isRowMajor = mjr == Major.row; 3510 enum major = mjr; 3511 enum size_t rows = rs; 3512 enum size_t cols = cs; 3513 3514 3515 this(M)(auto ref M mat) 3516 if(!is(M == typeof(this))) 3517 { 3518 static if(isNarrowMatrix!M) 3519 this.noAlias = mat; 3520 else 3521 this.opAssign(mat); 3522 } 3523 3524 3525 //@disable this(this); 3526 3527 3528 auto ref opIndex(size_t i, size_t j) inout 3529 in{ 3530 assert(i < rows); 3531 assert(j < cols); 3532 } 3533 body{ 3534 static if(major == Major.row) 3535 return _array[i * cols + j]; 3536 else 3537 return _array[j * rows + i]; 3538 } 3539 3540 3541 @property 3542 auto array() pure nothrow @safe inout 3543 { 3544 return _array[]; 3545 } 3546 3547 3548 void opAssign(M)(auto ref M mat) 3549 if(isValidOperator!(typeof(this), "=", M)) 3550 { 3551 auto buffer = { 3552 if(__ctfe) 3553 return new T[rs * cs]; 3554 else 3555 return SMatrix._buffer[]; 3556 }(); 3557 3558 foreach(i; 0 .. rows) 3559 foreach(j; 0 .. cols) 3560 { 3561 static if(major == Major.row) 3562 buffer[i * cols + j] = mat.at(i, j); 3563 else 3564 buffer[j * rows + i] = mat.at(i, j); 3565 } 3566 3567 _array[] = buffer[]; 3568 } 3569 3570 3571 @property 3572 void noAlias(M)(auto ref M mat) 3573 if(isValidOperator!(typeof(this), "=", M)) 3574 { 3575 foreach(i; 0 .. rows) 3576 foreach(j; 0 .. cols) 3577 { 3578 static if(major == Major.row) 3579 _array[i * cols + j] = mat[i, j]; 3580 else 3581 _array[j * rows + i] = mat[i, j]; 3582 } 3583 } 3584 3585 3586 @property 3587 auto stackRef() inout pure nothrow @safe 3588 { 3589 //Issue: 9983 http://d.puremagic.com/issues/show_bug.cgi?id=9983 3590 //return matrix!(rows, cols)(_array[]); 3591 return _referenceImpl(this); 3592 } 3593 3594 3595 //alias reference this; 3596 3597 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); 3598 //mixin(defaultExprOps!(false)); 3599 3600 3601 private: 3602 T[rs * cs] _array; 3603 3604 static: 3605 T[rs * cs] _buffer; 3606 3607 3608 // Workaround of issue 9983 http://d.puremagic.com/issues/show_bug.cgi?id=9983 3609 auto _referenceImpl(M)(ref M m) @trusted pure nothrow 3610 { 3611 static if(is(M == immutable(M))) 3612 return _referenceImplImmutable(cast(immutable(T)[])m._array[]); 3613 else static if(is(M == const(M))) 3614 return _referenceImplConst(cast(const(T)[])m._array[]); 3615 else 3616 return _referenceImplMutable(cast(T[])m._array[]); 3617 } 3618 3619 3620 auto _referenceImplMutable(E)(E[] arr) 3621 { 3622 return arr.matrix!(rows, cols); 3623 } 3624 3625 3626 auto _referenceImplConst(E)(const E[] arr) 3627 { 3628 return arr.matrix!(rows, cols); 3629 } 3630 3631 3632 auto _referenceImplImmutable(E)(immutable E[] arr) 3633 { 3634 return arr.matrix!(rows, cols); 3635 } 3636 } 3637 3638 unittest{ 3639 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3640 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3641 3642 3643 SMatrix!(int, 3, 3) m; 3644 m[0, 0] = 0; m[0, 1] = 1; m[0, 2] = 2; 3645 m[1, 0] = 1; m[1, 1] = 2; m[1, 2] = 3; 3646 m[2, 0] = 2; m[2, 1] = 3; m[2, 2] = 4; 3647 3648 auto mref = m.stackRef; 3649 3650 SMatrix!(int, 3, 3) m2 = mref * mref; 3651 mref = mref * mref; 3652 assert(mref == m2); 3653 } 3654 3655 unittest{ 3656 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3657 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3658 3659 3660 SMatrix!(int, 2, 2, Major.row) mr; // 2x2, int型, 行優先 3661 assert(mr.array.equal([0, 0, 0, 0])); // 初期化される 3662 3663 mr[0, 1] = 1; 3664 mr[1, 0] = 2; 3665 mr[1, 1] = 3; 3666 assert(mr.array.equal([0, 1, 2, 3])); // 行優先順 3667 3668 3669 SMatrix!(int, 2, 2, Major.column) mc; // 2x2, int型, 列優先 3670 assert(mc.array.equal([0, 0, 0, 0])); // 初期化される 3671 3672 mc[0, 1] = 1; 3673 mc[1, 0] = 2; 3674 mc[1, 1] = 3; 3675 assert(mc.array.equal([0, 2, 1, 3])); // 列優先順 3676 3677 3678 SMatrix!(int, 2, 2, Major.row) minit = mc; 3679 assert(minit.array.equal([0, 1, 2, 3])); // 全要素12で初期化されている 3680 } 3681 3682 unittest{ 3683 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3684 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3685 3686 3687 SMatrix!(int, 1, 3) m; 3688 m[0] = 3; 3689 assert(m[0] == 3); 3690 static assert(m.length == 3); 3691 assert(m[$-1] == 0); 3692 } 3693 3694 unittest{ 3695 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3696 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3697 3698 3699 SMatrix!(int, 2, 2) m; 3700 auto rm = m.stackRef; 3701 3702 assert(rm + rm == m + m); 3703 assert(rm - rm == m - m); 3704 assert(rm * rm == m * m); 3705 3706 m = [[1, 2], [2, 3]]; 3707 3708 SMatrix!(int, 2, 2) m2 = m; 3709 m.noAlias = m2 + m2; 3710 assert(m[0, 0] == 2); 3711 } 3712 3713 3714 unittest{ 3715 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3716 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3717 3718 3719 alias SRVector!(int, 3) R; 3720 R rv1; 3721 static assert(rv1.rows == 1); 3722 static assert(rv1.cols == 3); 3723 static assert(rv1.length == 3); 3724 3725 rv1[0] = 3; 3726 assert(rv1[0] == 3); 3727 assert(rv1[1] == 0); 3728 assert(rv1[2] == 0); 3729 rv1 += 3; 3730 assert(rv1[0] == 6); 3731 assert(rv1[1] == 3); 3732 assert(rv1[2] == 3); 3733 rv1[0] += 3; 3734 assert(rv1[0] == 9); 3735 assert(rv1[1] == 3); 3736 assert(rv1[2] == 3); 3737 3738 SRVector!(int, 4) rv2; 3739 static assert(rv2.rows == 1); 3740 static assert(rv2.cols == 4); 3741 static assert(rv2.length == 4); 3742 3743 SCVector!(int, 3) cv1; 3744 static assert(cv1.rows == 3); 3745 static assert(cv1.cols == 1); 3746 static assert(cv1.length == 3); 3747 3748 SCVector!(int, 4) cv2; 3749 static assert(cv2.rows == 4); 3750 static assert(cv2.cols == 1); 3751 static assert(cv2.length == 4); 3752 } 3753 3754 3755 ///ditto 3756 template SRVector(T, Msize_t size) 3757 { 3758 alias SMatrix!(T, 1, size, Major.row) SRVector; 3759 } 3760 3761 ///ditto 3762 template SCVector(T, Msize_t size) 3763 { 3764 alias SMatrix!(T, size, 1, Major.column) SCVector; 3765 } 3766 3767 ///ditto 3768 template SVector(T, Msize_t size) 3769 { 3770 alias SCVector!(T, size) SVector; 3771 } 3772 3773 3774 /** 3775 転置行列を返します。 3776 */ 3777 @property 3778 auto transpose(A)(A mat) 3779 if(isNarrowMatrix!A) 3780 { 3781 static struct Transposed() 3782 { 3783 static if(isAbstractMatrix!A) 3784 { 3785 enum size_t rows = wild; 3786 enum size_t cols = wild; 3787 3788 static InferredResult inferSize(Msize_t rs, Msize_t cs) 3789 { 3790 return A.inferSize(cs, rs); 3791 } 3792 } 3793 else 3794 { 3795 static if(hasStaticCols!A) 3796 enum size_t rows = A.cols; 3797 else 3798 @property auto ref rows() inout { return _mat.cols; } 3799 3800 static if(hasStaticRows!A) 3801 enum size_t cols = A.rows; 3802 else 3803 @property auto ref cols() inout { return _mat.rows; } 3804 } 3805 3806 3807 auto ref opIndex(size_t i, size_t j) inout 3808 in{ 3809 assert(i < rows); 3810 assert(j < cols); 3811 } 3812 body{ 3813 return _mat[j, i]; 3814 } 3815 3816 3817 static if(carbon.linear.hasAssignableElements!A) 3818 { 3819 void opIndexAssign(E)(E e, size_t i, size_t j) 3820 in{ 3821 assert(i < rows); 3822 assert(j < cols); 3823 } 3824 body{ 3825 _mat[j, i] = e; 3826 } 3827 3828 static if(isVector!A) 3829 { 3830 void opIndexAssign(E)(E e, size_t i) 3831 in{ 3832 assert(i < this.length); 3833 } 3834 body{ 3835 static if(is(typeof({static assert(rows == 1);}))) 3836 this[0 , i] = e; 3837 else 3838 this[i, 0] = e; 3839 } 3840 } 3841 } 3842 3843 mixin(defaultExprOps!(isAbstractMatrix!A)); 3844 3845 3846 auto ref transpose() @property inout 3847 { 3848 return _mat; 3849 } 3850 3851 3852 private: 3853 A _mat; 3854 } 3855 3856 return Transposed!()(mat); 3857 } 3858 unittest{ 3859 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3860 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3861 3862 3863 SMatrix!(int, 2, 2) m; 3864 m = [[0, 1], 3865 [2, 3]]; 3866 3867 auto t = m.transpose; 3868 assert(t[0, 0] == 0); 3869 assert(t[0, 1] == 2); 3870 assert(t[1, 0] == 1); 3871 assert(t[1, 1] == 3); 3872 } 3873 unittest{ 3874 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3875 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 3876 3877 3878 SCVector!(int, 3) v; 3879 v[0] = 1; v[1] = 2; v[2] = 3; 3880 3881 auto t = v.transpose; 3882 t[0] = 1; 3883 t[1] = 2; 3884 t[2] = 3; 3885 //t = [1, 2, 3]; 3886 3887 assert(t.rows == 1); 3888 assert(t.cols == 3); 3889 3890 3891 static assert(is(typeof(v) == typeof(t.transpose))); 3892 } 3893 3894 /** 3895 3896 */ 3897 auto toRange(A)(A mat) 3898 if(isNarrowMatrix!A) 3899 { 3900 static struct ToRange() 3901 { 3902 static struct Element() 3903 { 3904 @property auto ref front() { return _mat[_r, _cf]; } 3905 3906 @property auto ref back() { return _mat[_r, _cb]; } 3907 3908 auto ref opIndex(size_t i) { i += _cf; return _mat[_r, i]; } 3909 3910 void popFront() { ++_cf; } 3911 void popBack() { --_cb; } 3912 3913 @property bool empty() { return _cf == _cb; } 3914 @property size_t length() { return _cb - _cf; } 3915 alias opDollar = length; 3916 3917 @property auto save() { return this; } 3918 3919 auto opSlice() { return this.save; } 3920 auto opSlice(size_t i, size_t j) { return typeof(this)(_mat, _r, _cf + i, j); } 3921 3922 3923 private: 3924 A _mat; 3925 size_t _r; 3926 size_t _cf = 0; 3927 size_t _cb; 3928 } 3929 3930 3931 @property auto front() { return Element!()(this._mat, _rf, 0, this._mat.cols); } 3932 3933 @property auto back() { return Element!()(this._mat, _rb, 0, this._mat.cols); } 3934 3935 auto opIndex(size_t i) { i += _rf; return Element!()(this._mat, i, 0, this._mat.cols);} 3936 3937 void popFront() { ++_rf; } 3938 void popBack() { --_rb; } 3939 3940 @property bool empty() { return _rf == _rb; } 3941 @property size_t length() { return _rb - _rf; } 3942 alias opDollar = length; 3943 3944 @property auto save() { return this; } 3945 3946 auto opSlice() { return this.save; } 3947 auto opSlice(size_t i, size_t j) { return typeof(this)(_mat, _rf + i, j); } 3948 3949 private: 3950 A _mat; 3951 size_t _rf = 0; 3952 size_t _rb; 3953 } 3954 3955 3956 return ToRange!()(mat, 0, mat.rows); 3957 } 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 SMatrix!(int, 3, 3) rm33; 3965 rm33[0, 0] = 1; rm33[0, 1] = 2; rm33[0, 2] = 3; 3966 assert(equal!"equal(a, b)"(rm33.stackRef.toRange, [[1, 2, 3], [0, 0, 0], [0, 0, 0]])); 3967 3968 SMatrix!(int, 1, 1) rm11; 3969 assert(equal!"equal(a, b)"(rm11.stackRef.toRange, [[0]])); 3970 } 3971 3972 3973 /** 3974 行列をレンジにします 3975 */ 3976 auto toFlatten(A)(A mat) 3977 if(isNarrowMatrix!A && !isAbstractMatrix!A) 3978 { 3979 alias ElementType!A E; 3980 3981 static struct ToFlatten() 3982 { 3983 @property 3984 auto ref front() 3985 { 3986 return _mat[_f / _mat.cols, _f % _mat.cols]; 3987 } 3988 3989 3990 @property 3991 auto ref back() 3992 { 3993 return _mat[_b / _mat.cols, _b % _mat.cols]; 3994 } 3995 3996 3997 auto ref opIndex(size_t i) inout 3998 in{ 3999 assert(_f + i < _b); 4000 }body{ 4001 i += _f; 4002 return _mat[i / _mat.cols, i % _mat.cols]; 4003 } 4004 4005 4006 static if(hasAssignableElements!A) 4007 { 4008 @property 4009 void front(E v) 4010 { 4011 _mat[_f / _mat.cols, _f % _mat.cols] = v; 4012 } 4013 4014 4015 @property 4016 void back(E v) 4017 { 4018 _mat[_b / _mat.cols, _b % _mat.cols] = v; 4019 } 4020 4021 4022 void opIndexAssign(E v, size_t i) 4023 in{ 4024 assert(_f + i < _b); 4025 } 4026 body{ 4027 i += _f; 4028 _mat[i / _mat.cols, i % _mat.cols] = v; 4029 } 4030 } 4031 4032 4033 @property 4034 bool empty() pure nothrow @safe const 4035 { 4036 return _f >= _b; 4037 } 4038 4039 4040 void popFront() pure nothrow @safe 4041 { 4042 _f++; 4043 } 4044 4045 4046 void popBack() pure nothrow @safe 4047 { 4048 _b--; 4049 } 4050 4051 4052 @property 4053 size_t length() pure nothrow @safe const 4054 { 4055 return _b - _f; 4056 } 4057 4058 4059 alias length opDollar; 4060 4061 4062 @property 4063 typeof(this) save() pure nothrow @safe 4064 { 4065 return this; 4066 } 4067 4068 4069 private: 4070 A _mat; 4071 size_t _f, _b; 4072 } 4073 4074 return ToFlatten!()(mat, 0, mat.rows * mat.cols); 4075 } 4076 unittest{ 4077 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4078 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4079 4080 4081 SMatrix!(int, 3, 3, Major.row) rm33; 4082 rm33[0, 0] = 1; rm33[0, 1] = 2; rm33[0, 2] = 3; 4083 //writeln(rm33.array.length;) 4084 assert(rm33.array == [1, 2, 3, 0, 0, 0, 0, 0, 0]); 4085 4086 alias Rt1 = typeof(toFlatten(rm33)); 4087 static assert(isRandomAccessRange!(Rt1)); 4088 static assert(std.range.hasLvalueElements!(Rt1)); 4089 static assert(std.range.hasAssignableElements!(Rt1)); 4090 static assert(hasLength!(Rt1)); 4091 assert(equal(rm33.toFlatten, rm33.array)); 4092 4093 SMatrix!(int, 3, 3, Major.column) cm33; 4094 cm33[0, 0] = 1; cm33[0, 1] = 2; cm33[0, 2] = 3; 4095 assert(cm33.array == [1, 0, 0, 2, 0, 0, 3, 0, 0]); 4096 assert(equal(cm33.toFlatten, [1, 2, 3, 0, 0, 0, 0, 0, 0])); 4097 } 4098 4099 4100 4101 /** 4102 レンジから行列を作ります。 4103 */ 4104 auto toMatrix(Msize_t rs, Msize_t cs, Major mjr = Major.row, R)(R range) 4105 if(isRandomAccessRange!R && isNotVectorOrMatrix!(Unqual!(std.range.ElementType!R)) && (mjr == Major.row ? (cs != wild) : (rs != wild))) 4106 { 4107 //static if(mjr == Major.column) 4108 //return range.toMatrix!(cs, rs, Major.row).transpose; 4109 //else 4110 //{ 4111 alias E = Unqual!(std.range.ElementType!R); 4112 4113 static struct ToMatrix() 4114 { 4115 static if(rs != wild) 4116 enum size_t rows = rs; 4117 else 4118 auto ref rows() const @property 4119 { 4120 return _range.length / typeof(this).cols; 4121 } 4122 4123 static if(cs != wild) 4124 enum size_t cols = cs; 4125 else 4126 auto ref cols() const @property 4127 { 4128 return _range.length / typeof(this).rows; 4129 } 4130 4131 4132 auto ref opIndex(size_t i, size_t j) inout 4133 in{ 4134 assert(i < rows || rows == 0); 4135 assert(j < cols || cols == 0); 4136 4137 static if(hasLength!R && mjr == Major.row) 4138 assert(i * cols + j < this._range.length); 4139 4140 static if(hasLength!R && mjr == Major.column) 4141 assert(j * rows + i < this._range.length); 4142 } 4143 body{ 4144 static if(mjr == Major.row) 4145 return this._range[i * cols + j]; 4146 else 4147 return this._range[j * rows + i]; 4148 } 4149 4150 4151 mixin(defaultExprOps!(false)); 4152 4153 private: 4154 R _range; 4155 } 4156 4157 return ToMatrix!()(range); 4158 //} 4159 } 4160 4161 unittest{ 4162 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4163 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4164 4165 4166 auto r = iota(4); 4167 auto mr = toMatrix!(2, 2)(r); 4168 assert(mr.toFlatten.equal([0, 1, 2, 3])); 4169 4170 auto mc = r.toMatrix!(2, 2, Major.column); 4171 assert(mc.toFlatten.equal([0, 2, 1, 3])); 4172 } 4173 unittest{ 4174 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4175 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4176 4177 4178 auto mem = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 4179 auto mr = mem.toMatrix!(3, 3); 4180 static assert(isNarrowMatrix!(typeof(mr))); 4181 static assert(hasLvalueElements!(typeof(mr))); 4182 assert(mr.toFlatten.equal(mem[0 .. 9])); 4183 4184 mem.length = 16; 4185 auto mc = mem.toMatrix!(4, 4, Major.column); 4186 static assert(isNarrowMatrix!(typeof(mr))); 4187 static assert(hasLvalueElements!(typeof(mr))); 4188 mc[3, 3] = 15; 4189 assert(mc.transpose.toFlatten.equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4190 0, 0, 0, 0, 0, 15])); 4191 } 4192 unittest{ 4193 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4194 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4195 4196 4197 auto mem = [0, 1, 2, 3]; 4198 auto r1 = mem.toMatrix!(wild, 1, Major.row); 4199 assert(equal(r1.toFlatten, mem[0 .. 4])); 4200 4201 mem ~= [4, 5]; 4202 auto r2 = mem.toMatrix!(wild, 2, Major.row); 4203 assert(r2[2, 0] == 4); 4204 assert(r2[2, 1] == 5); 4205 4206 auto c1 = mem.toMatrix!(1, wild, Major.column); 4207 assert(equal(c1.toFlatten, mem)); 4208 } 4209 4210 4211 4212 auto toMatrix(Msize_t rs, Msize_t cs, Major mjr = Major.row, R)(R range) 4213 if(isRandomAccessRange!R && isRandomAccessRange!(Unqual!(std.range.ElementType!R)) && isNotVectorOrMatrix!(Unqual!(std.range.ElementType!(Unqual!(std.range.ElementType!R))))) 4214 { 4215 static struct ToMatrix() 4216 { 4217 static if(rs != dynamic) 4218 enum size_t rows = rs; 4219 else 4220 auto ref rows() const @property 4221 { 4222 static if(mjr == Major.row) 4223 return _range.length; 4224 else 4225 return _range[0].length; 4226 } 4227 4228 4229 static if(cs != dynamic) 4230 enum size_t cols = cs; 4231 else 4232 auto ref cols() const @property 4233 { 4234 static if(mjr == Major.column) 4235 return _range.length; 4236 else 4237 return _range[0].length; 4238 } 4239 4240 4241 auto ref opIndex(size_t i, size_t j) inout 4242 in{ 4243 assert(i < rows || rows == 0); 4244 assert(j < cols || cols == 0); 4245 4246 static if(hasLength!R) 4247 assert((mjr == Major.row ? i : j) < _range.length); 4248 4249 static if(hasLength!(Unqual!(std.range.ElementType!R))) 4250 assert((mjr == Major.row ? j : i) < _range[i].length); 4251 } 4252 body{ 4253 static if(mjr == Major.row) 4254 return _range[i][j]; 4255 else 4256 return _range[j][i]; 4257 } 4258 4259 mixin(defaultExprOps!(false)); 4260 4261 private: 4262 R _range; 4263 } 4264 4265 return ToMatrix!()(range); 4266 } 4267 unittest{ 4268 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4269 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4270 4271 4272 auto arr = [[0, 1], [2, 3], [4, 5]]; 4273 auto r1 = toMatrix!(3, 2, Major.row)(arr); 4274 static assert(isNarrowMatrix!(typeof(r1))); 4275 assert(equal!"equal(a, b)"(r1.toRange, arr)); 4276 4277 auto r2 = arr.toMatrix!(1, 1, Major.row); 4278 assert(r2[0] == 0); 4279 4280 auto r3 = arr.toMatrix!(dynamic, 2, Major.row); 4281 assert(r3.congeal!(3, 2) == r1); 4282 4283 auto r4 = arr.toMatrix!(2, dynamic, Major.row); 4284 assert(equal(r4.congeal!(2, 2)().toFlatten(), [0, 1, 2, 3])); 4285 4286 auto r5 = arr.toMatrix!(dynamic, dynamic, Major.row); 4287 assert(r5 == r1); 4288 } 4289 unittest{ 4290 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4291 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4292 4293 4294 auto arr = [[0, 1], [2, 3], [4, 5]]; 4295 auto r1 = arr.toMatrix!(2, 3, Major.column); 4296 assert(equal!"equal(a, b)"(r1.transpose.toRange, arr)); 4297 assert(r1[0, 0] == 0); assert(r1[0, 1] == 2); assert(r1[0, 2] == 4); 4298 assert(r1[1, 0] == 1); assert(r1[1, 1] == 3); assert(r1[1, 2] == 5); 4299 4300 auto r2 = arr.toMatrix!(1, 1, Major.column); 4301 assert(r2[0] == 0); 4302 4303 auto r3 = arr.toMatrix!(dynamic, 3, Major.column); 4304 assert(r3 == r1); 4305 4306 auto r4 = arr.toMatrix!(2, dynamic, Major.column); 4307 assert(equal(r4.transpose.toFlatten, [0, 1, 2, 3, 4, 5])); 4308 4309 auto r5 = arr.toMatrix!(dynamic, dynamic, Major.column); 4310 assert(r5 == r1); 4311 } 4312 4313 4314 /** 4315 単位行列 4316 */ 4317 @property 4318 auto identity(E)()if(isNotVectorOrMatrix!E) 4319 { 4320 static struct Identity() 4321 { 4322 4323 4324 static InferredResult inferSize(Msize_t i, Msize_t j) 4325 { 4326 if(i < 0 && j < 0) 4327 return InferredResult(false); 4328 else if(i < 0 || j < 0) 4329 return InferredResult(true, max(i, j), max(i, j)); 4330 else if(i == j) 4331 return InferredResult(true, i, j); 4332 else 4333 return InferredResult(false); 4334 } 4335 4336 4337 E opIndex(size_t i, size_t j) inout 4338 { 4339 return (i == j) ? (cast(E)1) : (cast(E)0); 4340 } 4341 4342 mixin(defaultExprOps!(true)); 4343 } 4344 4345 return Identity!()(); 4346 } 4347 unittest{ 4348 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4349 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4350 4351 4352 auto id = identity!int; 4353 static assert(isAbstractMatrix!(typeof(id))); 4354 static assert(typeof(id).inferSize(4, 4).isValid); 4355 static assert(typeof(id).inferSize(wild, 2).isValid); 4356 static assert(!typeof(id).inferSize(1, 3).isValid); 4357 4358 auto m1 = SMatrix!(int, 2, 2).init; 4359 m1.array[] = [0, 1, 2, 3]; 4360 assert(equal((m1 * id).toFlatten, [0, 1, 2, 3])); 4361 4362 auto id2 = id + id; 4363 static assert(isAbstractMatrix!(typeof(id2))); 4364 static assert(typeof(id2).inferSize(4, 4).isValid); 4365 4366 4367 auto id22 = id.congeal!(wild, 2); 4368 static assert(id22.rows == 2); 4369 static assert(id22.cols == 2); 4370 assert(equal(id22.toFlatten, [1, 0, 0, 1])); 4371 4372 auto id3 = id22 * id; 4373 static assert(id3.rows == 2); 4374 static assert(id3.cols == 2); 4375 assert(equal(id3.toFlatten, [1, 0, 0, 1])); 4376 4377 auto ins = id2.congeal!(2, 2); 4378 static assert(isNarrowMatrix!(typeof(ins))); 4379 assert(equal(ins.toFlatten, [2, 0, 0, 2])); 4380 } 4381 4382 4383 /** 4384 全要素が1な行列を返します。 4385 */ 4386 @property 4387 auto ones(E)()if(isNotVectorOrMatrix!E) 4388 { 4389 static struct Ones() 4390 { 4391 4392 4393 E opIndex(size_t i, size_t j) inout 4394 { 4395 return cast(E)1; 4396 } 4397 4398 4399 static InferredResult inferSize(Msize_t i, Msize_t j) 4400 { 4401 if(i == wild && j == wild) 4402 return InferredResult(false); 4403 else if(i == wild || j == wild) 4404 return InferredResult(true, max(i, j), max(i, j)); 4405 else 4406 return InferredResult(true, i, j); 4407 } 4408 4409 4410 mixin(defaultExprOps!(true)); 4411 } 4412 4413 static assert(isAbstractMatrix!(Ones!())); 4414 4415 return Ones!().init; 4416 } 4417 unittest{ 4418 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4419 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4420 4421 4422 auto m1 = ones!float; 4423 assert(m1[0, 1] == 1); 4424 4425 auto m3 = m1 * 3; 4426 assert(m3[0, 1] == 3); 4427 4428 auto m9 = m3 * 3; 4429 assert(m9[0, 1] == 9); 4430 } 4431 4432 4433 /** 4434 部分行列を返します 4435 */ 4436 auto sub(alias rArray, alias cArray, A)(A mat) 4437 if(isArray!(typeof(rArray)) && isArray!(typeof(cArray)) && isNarrowMatrix!A && !isAbstractMatrix!A 4438 && (hasDynamicRows!A || is(typeof({static assert(rArray.find!"a>=b"(A.rows).empty);}))) 4439 && (hasDynamicColumns!A || is(typeof({static assert(cArray.find!"a>=b"(A.cols).empty);})))) 4440 in{ 4441 static if(hasDynamicRows!A) 4442 assert(rArray.find!"a>=b"(mat.rows).empty); 4443 static if(hasDynamicColumns!A) 4444 assert(cArray.find!"a>=b"(mat.cols).empty); 4445 } 4446 body{ 4447 static if(rArray.length == 0 && cArray.length == 0) 4448 return mat; 4449 else 4450 { 4451 static struct Sub() 4452 { 4453 static if(rArray.length == 0) 4454 alias rows = _mat.rows; 4455 else 4456 enum rows = rArray.length; 4457 4458 static if(cArray.length == 0) 4459 alias cols = _mat.cols; 4460 else 4461 enum cols = cArray.length; 4462 4463 4464 auto ref opIndex(size_t i, size_t j) inout 4465 in{ 4466 assert(i < rows); 4467 assert(j < cols); 4468 } 4469 body{ 4470 static if(rArray.length && cArray.length) 4471 return _mat[rArray[i], cArray[j]]; 4472 else static if(rArray.length) 4473 return _mat[rArray[i], j]; 4474 else static if(cArray.length) 4475 return _mat[i, cArray[j]]; 4476 else 4477 static assert(0); 4478 } 4479 4480 4481 mixin(defaultExprOps!(false)); 4482 4483 4484 private: 4485 A _mat; 4486 } 4487 4488 return Sub!()(mat); 4489 } 4490 } 4491 unittest{ 4492 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4493 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4494 4495 4496 auto m1 = [[0, 1], [2, 3], [4, 5]].toMatrix!(3, 2, Major.row); 4497 auto s1 = m1.sub!([1, 2], [0]); 4498 static assert(s1.rows == 2); 4499 static assert(s1.cols == 1); 4500 4501 assert(s1[0, 0] == 2); 4502 assert(s1[1, 0] == 4); 4503 4504 4505 auto m2 = [[0, 1], [2, 3], [4, 5]].toMatrix!(dynamic, dynamic, Major.row)(); 4506 auto s2 = sub!((size_t[]).init, (size_t[]).init)(m2); 4507 assert(m1 == s2.congeal!(3, 2)); 4508 4509 4510 auto m3 = identity!int.congeal!(2, 2)().sub!([0, 0, 0], [0, 0, 0])(); 4511 assert(m3 == ones!int); 4512 } 4513 4514 4515 /** 4516 swizzle : glm参照 4517 */ 4518 auto swizzle(A)(A mat) @property 4519 if(isNarrowMatrix!A && !isAbstractMatrix!A) 4520 { 4521 static struct Swizzle() 4522 { 4523 auto opDispatch(string exp)() 4524 if((isVector!A ? (isSwizzleExp(exp) && isSwizzlable!A(exp)) : false) || (isSliceExp(exp) && isSlicable!(A, exp))) 4525 { 4526 static struct SwizzleResult() 4527 { 4528 static if(isSwizzleExp(exp)) 4529 { 4530 static if(A.cols == 1) 4531 { 4532 enum size_t rows = exp.length; 4533 enum size_t cols = 1; 4534 } 4535 else 4536 { 4537 enum size_t rows = 1; 4538 enum size_t cols = exp.length; 4539 } 4540 4541 4542 auto ref opIndex(size_t i, size_t j) inout 4543 in{ 4544 assert((i+j) < (rows+cols-1)); 4545 } 4546 body{ 4547 immutable size_t s = swizzleType(exp) == 'a' ? exp[i] - 'a' : (exp[i] == 'w' ? 3 : (exp[i] - 'x')); 4548 4549 static if(cols == 1) 4550 return _mat[s, 0]; 4551 else 4552 return _mat[0, s]; 4553 } 4554 } 4555 else // isSliceExp 4556 { 4557 private enum _swizzleExpSpec = spec(exp); 4558 enum size_t rows = mixin(_swizzleExpSpec.cs); 4559 enum size_t cols = mixin(_swizzleExpSpec.rs); 4560 4561 4562 auto ref opIndex(size_t i, size_t j) inout 4563 in{ 4564 assert(i < rows); 4565 assert(j < cols); 4566 } 4567 body{ 4568 immutable size_t r = mixin(_swizzleExpSpec.rb) + i, 4569 c = mixin(_swizzleExpSpec.cb) + j; 4570 4571 return _mat[r, c]; 4572 } 4573 } 4574 4575 4576 mixin(defaultExprOps!(false)); 4577 4578 private: 4579 A _mat; 4580 } 4581 4582 4583 return SwizzleResult!()(_mat); 4584 } 4585 4586 private: 4587 A _mat; 4588 4589 static: 4590 4591 bool isSwizzleExp(string str) pure nothrow @safe 4592 { 4593 char d; 4594 foreach(c; str) 4595 if(!(('a' <= c && c <= 'h') || (c == 'x' || c == 'y' || c == 'z' || c == 'w'))) 4596 return false; 4597 else if('a' <= c && c <= 'h'){ 4598 if(d == char.init) 4599 d = 'a'; 4600 else if(d != 'a') 4601 return false; 4602 }else{ 4603 if(d == char.init) 4604 d = 'x'; 4605 else if(d != 'x') 4606 return false; 4607 } 4608 4609 return true; 4610 } 4611 unittest{ 4612 assert(isSwizzleExp("aaaaaaa")); 4613 assert(isSwizzleExp("aaaahaa")); 4614 assert(!isSwizzleExp("aaaaiaa")); 4615 assert(!isSwizzleExp("aaxa")); 4616 assert(isSwizzleExp("xxxx")); 4617 assert(isSwizzleExp("xyzw")); 4618 assert(!isSwizzleExp("xyza")); 4619 } 4620 4621 4622 // pure nothrow @safeにするため 4623 string find_(string str, char c) pure nothrow @safe 4624 { 4625 while(str.length && str[0] != c) 4626 str = str[1 .. $]; 4627 return str; 4628 } 4629 4630 4631 string until_(string str, char c) pure nothrow @safe 4632 { 4633 foreach(i; 0 .. str.length) 4634 if(str[i] == c) 4635 return str[0 .. i]; 4636 4637 return str; 4638 } 4639 4640 4641 // for matrix, format: r<bias>c<bias>m<row>x<cols> 4642 alias ExpSpec = Tuple!(string, "rb", string, "cb", string, "rs", string, "cs"); 4643 4644 4645 ExpSpec spec(string exp) pure nothrow @safe 4646 { 4647 ExpSpec spec = ExpSpec("0", "0", "", ""); 4648 { 4649 auto t = until_(until_(find_(exp, 'r'), 'c'), 'm'); 4650 if(t.length > 1) //r<Num>の形式なので、rを加えて1文字以上無いといけない 4651 spec.rb = t[1 .. $]; 4652 } 4653 4654 { 4655 auto t = until_(find_(exp, 'c'), 'm'); 4656 if(t.length > 1) //c<Num>の形式なので、rを加えて1文字以上無いといけない 4657 spec.cb = t[1 .. $]; 4658 } 4659 4660 { 4661 auto t = until_(find_(exp, 'm'), 'x'); 4662 if(t.length > 1) //m<Num>の形式なので、mを加えて1文字以上無いといけない 4663 spec.rs = t[1 .. $]; 4664 } 4665 4666 { 4667 auto t = find_(exp, 'x'); 4668 if(t.length > 1) 4669 spec.cs = t[1 .. $]; 4670 } 4671 return spec; 4672 } 4673 unittest{ 4674 assert(spec("r1c1m1x1") == tuple("1", "1", "1", "1")); 4675 assert(spec("r1_100c21m5x5") == tuple("1_100", "21", "5", "5")); 4676 assert(spec("m1x2") == tuple("0", "0", "1", "2")); 4677 } 4678 4679 4680 bool isSliceExp(string exp) pure nothrow @safe 4681 { 4682 bool isAllDigit(string str) pure nothrow @safe 4683 { 4684 if(str.length == 0) 4685 return false; 4686 4687 foreach(c; str) 4688 if(!(c.isDigit || c == '_')) 4689 return false; 4690 return true; 4691 } 4692 4693 auto sp = spec(exp); 4694 return (exp[0] == 'm' || exp[0] == 'r' || exp[0] == 'c') && isAllDigit(sp.rb) && isAllDigit(sp.cb) && isAllDigit(sp.rs) && isAllDigit(sp.cs); 4695 } 4696 unittest{ 4697 assert(isSliceExp("r1c1m1x1")); 4698 assert(isSliceExp("r1_100c21m5x5")); 4699 assert(isSliceExp("m1x2")); 4700 assert(!isSliceExp("m2m1")); 4701 assert(!isSliceExp("_2mx1")); 4702 assert(isSliceExp("c1m1x1")); 4703 } 4704 4705 4706 // for vec 4707 char swizzleType(string exp) pure nothrow @safe 4708 { 4709 if('a' <= exp[0] && exp[0] <= 'h') 4710 return 'a'; 4711 else 4712 return 'x'; 4713 } 4714 unittest{ 4715 assert(swizzleType("aaaaaaa") == 'a'); 4716 assert(swizzleType("aaaahaa") == 'a'); 4717 assert(swizzleType("xxxx") == 'x'); 4718 assert(swizzleType("xyzv") == 'x'); 4719 } 4720 4721 4722 bool isSwizzlable(T)(string exp) pure nothrow @safe 4723 { 4724 static if(isVector!T){ 4725 enum size = T.length - 1; 4726 4727 if(swizzleType(exp) == 'a'){ 4728 foreach(c; exp) 4729 if(c > 'a' + size) 4730 return false; 4731 return true; 4732 }else{ 4733 foreach(c; exp) 4734 if(c != 'x' && c != 'y' && c != 'z' && c != 'w') 4735 return false; 4736 return true; 4737 } 4738 } 4739 else 4740 return false; 4741 } 4742 unittest{ 4743 alias V3 = SMatrix!(int, 1, 3); 4744 assert(isSwizzlable!V3("aaa")); 4745 assert(isSwizzlable!V3("abc")); 4746 assert(!isSwizzlable!V3("abd")); 4747 assert(isSwizzlable!V3("xyz")); 4748 assert(!isSwizzlable!V3("xyzv")); 4749 4750 alias V4 = SMatrix!(int, 1, 4); 4751 assert(isSwizzlable!V4("xyzw")); 4752 assert(!isSwizzlable!V4("xyzv")); 4753 } 4754 4755 4756 bool isSlicable(T, string exp)() pure nothrow @safe 4757 { 4758 static if(isSliceExp(exp)){ 4759 enum sp = spec(exp); 4760 return ((mixin(sp.rs) + mixin(sp.rb)) <= T.cols) && ((mixin(sp.cs) + mixin(sp.cb)) <= T.rows); 4761 } 4762 else 4763 return false; 4764 } 4765 unittest{ 4766 alias M33 = SMatrix!(int, 3, 3); 4767 assert(isSlicable!(M33, "m3x3")); 4768 assert(isSlicable!(M33, "m1x3")); 4769 assert(isSlicable!(M33, "r1m2x3")); 4770 assert(isSlicable!(M33, "c1m2x2")); 4771 } 4772 } 4773 4774 4775 return Swizzle!()(mat); 4776 } 4777 4778 unittest{ 4779 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4780 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4781 4782 4783 auto org = matrix!((i, j) => i * 3 + j)(); 4784 SMatrix!(size_t, 4, 4) a = org; 4785 auto m = a.swizzle.m2x2; 4786 static assert(isNarrowMatrix!(typeof(m))); 4787 assert(m == org); 4788 4789 4790 auto m2 = a.swizzle.r1c1m2x2; 4791 static assert(isNarrowMatrix!(typeof(m2))); 4792 assert(m2 == matrix!((i, j) => (i+1)*3 + j + 1)()); 4793 4794 4795 assert(a.swizzle.m1x4.swizzle.xyzw == a.swizzle.m1x4); 4796 } 4797 4798 4799 /** 4800 行ベクトルのランダムアクセスレンジ 4801 */ 4802 auto rowVectors(A)(A mat) @property 4803 if(isNarrowMatrix!A && !isAbstractMatrix!A) 4804 { 4805 static struct RowVectors() 4806 { 4807 auto front() @property { return this.opIndex(0); } 4808 auto back() @property { return this.opIndex(_end-1); } 4809 void popFront() @property { ++_idx; } 4810 void popBack() @property { --_end; } 4811 bool empty() @property const { return _idx == _end; } 4812 auto save() @property { return this; } 4813 4814 4815 4816 auto opIndex(size_t i) 4817 { 4818 static struct RowVectorByIndex() 4819 { 4820 alias rows = _m.rows; 4821 enum cols = 1; 4822 4823 4824 auto ref opIndex(size_t i, size_t j) inout 4825 in{ 4826 assert(i < rows); 4827 assert(j < cols); 4828 } 4829 body{ 4830 return _m[i, _idx]; 4831 } 4832 4833 4834 mixin(defaultExprOps!(false)); 4835 4836 4837 size_t _idx; 4838 A _m; 4839 } 4840 4841 4842 return RowVectorByIndex!()(i + _idx, _m); 4843 } 4844 4845 4846 size_t length() const @property 4847 { 4848 return _end - _idx; 4849 } 4850 4851 4852 alias opDispatch = length; 4853 4854 private: 4855 size_t _idx; 4856 size_t _end; 4857 A _m; 4858 } 4859 4860 4861 return RowVectors!()(0, mat.cols, mat); 4862 } 4863 unittest 4864 { 4865 real[3][3] mStack = [[1, 2, 2], 4866 [2, 1, 1], 4867 [2, 2, 2]]; 4868 4869 auto r = matrix(mStack).rowVectors; 4870 4871 static assert(isRandomAccessRange!(typeof(r))); 4872 4873 foreach(i; 0 .. 3) 4874 foreach(j; 0 .. 3) 4875 assert(r[i][j] == mStack[j][i]); 4876 } 4877 4878 4879 /** 4880 行ベクトルのランダムアクセスレンジ 4881 */ 4882 auto columnVectors(A)(A mat) @property 4883 if(isNarrowMatrix!A && !isAbstractMatrix!A) 4884 { 4885 static struct ColumnVectors() 4886 { 4887 auto front() @property { return this.opIndex(0); } 4888 auto back() @property { return this.opIndex(_end-1); } 4889 void popFront() @property { ++_idx; } 4890 void popBack() @property { --_end; } 4891 bool empty() @property const { return _idx == _end; } 4892 auto save() @property { return this; } 4893 4894 4895 4896 auto opIndex(size_t i) 4897 { 4898 static struct ColumnVectorByIndex() 4899 { 4900 enum rows = 1; 4901 alias cols = _m.cols; 4902 4903 4904 auto ref opIndex(size_t i, size_t j) inout 4905 in{ 4906 assert(i < rows); 4907 assert(j < cols); 4908 } 4909 body{ 4910 return _m[_idx, j]; 4911 } 4912 4913 4914 mixin(defaultExprOps!(false)); 4915 4916 4917 size_t _idx; 4918 A _m; 4919 } 4920 4921 return ColumnVectorByIndex!()(i + _idx, _m); 4922 } 4923 4924 4925 size_t length() const @property 4926 { 4927 return _end - _idx; 4928 } 4929 4930 4931 alias opDispatch = length; 4932 4933 private: 4934 size_t _idx; 4935 size_t _end; 4936 A _m; 4937 } 4938 4939 4940 return ColumnVectors!()(0, mat.rows, mat); 4941 } 4942 unittest 4943 { 4944 real[3][3] mStack = [[1, 2, 2], 4945 [2, 1, 1], 4946 [2, 2, 2]]; 4947 4948 auto r = matrix(mStack).columnVectors; 4949 4950 static assert(isRandomAccessRange!(typeof(r))); 4951 4952 foreach(i; 0 .. 3) 4953 foreach(j; 0 .. 3) 4954 assert(r[i][j] == mStack[i][j]); 4955 } 4956 4957 4958 /** 4959 行列の跡(trace)を返します。正方行列についてのみ定義されます 4960 */ 4961 ElementType!A trace(A)(A mat) 4962 if(isNarrowMatrix!A && !isAbstractMatrix!A && (!(hasStaticRows!A && hasStaticCols!A) || is(typeof({static assert(A.rows == A.cols);})))) 4963 { 4964 alias ElementType!A T; 4965 T sum = cast(T)0; 4966 4967 foreach(i; 0 .. mat.rows) 4968 sum += mat[i, i]; 4969 4970 return sum; 4971 } 4972 unittest{ 4973 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4974 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 4975 4976 4977 auto m = SMatrix!(int, 2, 2)(); 4978 m[0, 0] = 0; m[0, 1] = 1; 4979 m[1, 0] = 2; m[1, 1] = 3; 4980 4981 auto tr = m.trace; 4982 assert(tr == 3); 4983 } 4984 4985 4986 auto dot(V1, V2)(V1 vec1, V2 vec2) 4987 if(isVector!V1 && isVector!V2 && (!(hasStaticLength!V1 && hasStaticLength!V2) || is(typeof({static assert(V1.length == V2.length);})))) 4988 in{ 4989 static if(!(hasStaticLength!V1 && hasStaticLength!V2)) 4990 { 4991 assert(vec1.length == vec2.length); 4992 } 4993 } 4994 body{ 4995 alias typeof(ElementType!V1.init * ElementType!V2.init) T; 4996 T sum = cast(T)0; 4997 4998 foreach(i; 0 .. vec1.length){ 4999 sum += vec1[i] * vec2[i]; 5000 } 5001 5002 return sum; 5003 } 5004 unittest{ 5005 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5006 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5007 5008 5009 auto rv = SRVector!(int, 3)(), 5010 cv = SCVector!(int, 3)(); 5011 5012 rv.array[] = [0, 1, 2]; 5013 cv.array[] = [1, 2, 3]; 5014 5015 assert((rv * cv)[0] == 8); //8 5016 assert(rv.dot(cv) == 8); 5017 assert(cv.dot(rv) == 8); 5018 5019 assert(rv.dot(rv) == 5); 5020 assert(cv.dot(cv) == 14); 5021 5022 5023 assert(approxEqual((rv * 0.5).dot(rv), 2.5)); 5024 } 5025 5026 5027 /** 5028 ベクトル同士のクロス積 5029 */ 5030 auto cross(Major mjr = Major.column, V1, V2)(V1 vec1, V2 vec2) 5031 if(isVector!V1 && isVector!V2 && (hasStaticLength!V1 && is(typeof({static assert(V1.length == 3);}))) 5032 && (hasStaticLength!V2 && is(typeof({static assert(V2.length == 3);})))) 5033 in{ 5034 assert(vec1.length == 3); 5035 assert(vec2.length == 3); 5036 } 5037 body{ 5038 static struct CrossResult 5039 { 5040 enum rows = mjr == Major.row ? 1 : 3; 5041 enum cols = mjr == Major.row ? 3 : 1; 5042 5043 5044 auto opIndex(size_t i, size_t j) const 5045 in{ 5046 assert(i < rows); 5047 assert(j < cols); 5048 } 5049 body{ 5050 switch(i + j){ 5051 case 0: return _v1[1] * _v2[2] - _v1[2] * _v2[1]; 5052 case 1: return _v1[2] * _v2[0] - _v1[0] * _v2[2]; 5053 case 2: return _v1[0] * _v2[1] - _v1[1] * _v2[0]; 5054 default: assert(0); 5055 } 5056 } 5057 5058 mixin(defaultExprOps!(false)); 5059 5060 private: 5061 V1 _v1; 5062 V2 _v2; 5063 } 5064 5065 return CrossResult(vec1, vec2); 5066 } 5067 unittest{ 5068 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5069 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5070 5071 5072 auto rv = SVector!(int, 3)(), 5073 cv = SVector!(int, 3)(); 5074 5075 rv.array[] = [0, 1, 2]; 5076 cv.array[] = [1, 2, 3]; 5077 5078 auto cp = rv.cross(cv); 5079 5080 static assert(isVector!(typeof(cp))); 5081 static assert(hasStaticLength!(typeof(cp))); 5082 static assert(hasStaticRows!(typeof(cp))); 5083 static assert(hasStaticCols!(typeof(cp))); 5084 5085 assert(cp[0] == -1); 5086 assert(cp[1] == 2); 5087 assert(cp[2] == -1); 5088 } 5089 5090 5091 /** 5092 直積 5093 */ 5094 auto cartesian(V1, V2)(V1 vec1, V2 vec2) 5095 if(isVector!V1 && isVector!V2) 5096 { 5097 static struct Cartesian() 5098 { 5099 alias rows = _vec1.length; 5100 alias cols = _vec2.length; 5101 5102 5103 auto opIndex(size_t i, size_t j) const 5104 in{ 5105 assert(i < rows); 5106 assert(j < cols); 5107 } 5108 body{ 5109 return _vec1[i] * _vec2[j]; 5110 } 5111 5112 5113 mixin(defaultExprOps!(false)); 5114 5115 5116 private: 5117 V1 _vec1; 5118 V2 _vec2; 5119 } 5120 5121 5122 return Cartesian!()(vec1, vec2); 5123 } 5124 unittest{ 5125 //scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5126 //scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 5127 5128 5129 auto v1 = [0, 1, 2, 3].toMatrix!(3, 1); 5130 auto v2 = [2, 3, 4, 5].toMatrix!(1, 2); 5131 5132 assert(v1.cartesian(v2) == v1 * v2); 5133 static assert(hasStaticRows!(typeof(v1.cartesian(v2)))); 5134 static assert(hasStaticCols!(typeof(v1.cartesian(v2)))); 5135 } 5136 5137 5138 5139 /** 5140 置換行列を作ります 5141 */ 5142 auto permutation(size_t size = wild, size_t)(const size_t[] pos) pure nothrow @safe 5143 in{ 5144 foreach(e; pos) 5145 assert(e < pos.length); 5146 5147 static if(size != wild) 5148 assert(pos.length == size); 5149 } 5150 body{ 5151 static struct Permutation 5152 { 5153 static if(size != wild) 5154 enum rows = size; 5155 else 5156 size_t rows() pure nothrow @safe const @property { return _pos.length; } 5157 5158 alias cols = rows; 5159 5160 5161 auto opIndex(size_t i, size_t j) pure nothrow @safe const 5162 { 5163 return _pos[i] == j ? 1 : 0; 5164 } 5165 5166 5167 mixin(defaultExprOps!(false)); 5168 5169 5170 @property auto inverse() pure nothrow @safe const 5171 { 5172 static struct InvPermutation 5173 { 5174 static if(size != wild) 5175 enum rows = size; 5176 else 5177 size_t rows() pure nothrow @safe const @property { return _pos.length; } 5178 5179 alias cols = rows; 5180 5181 5182 auto opIndex(size_t i, size_t j) pure nothrow @safe const 5183 { 5184 return _pos[j] == i ? 1 : 0; 5185 } 5186 5187 5188 mixin(defaultExprOps!(false)); 5189 5190 5191 @property auto inverse() pure nothrow @safe const 5192 { 5193 return Permutation(_pos); 5194 } 5195 5196 5197 private: 5198 const(size_t)[] _pos; 5199 } 5200 5201 5202 return InvPermutation(_pos); 5203 } 5204 5205 5206 @property const(size_t)[] exchangeTable() pure nothrow @safe const 5207 { 5208 return _pos; 5209 } 5210 5211 5212 private: 5213 const(size_t)[] _pos; 5214 } 5215 5216 5217 return Permutation(pos); 5218 } 5219 5220 5221 template isPermutationMatrix(A) 5222 { 5223 enum isPermutationMatrix = isNarrowMatrix!A && is(Unqual!(typeof(A.init.exchangeTable)) : size_t[]); 5224 } 5225 5226 5227 5228 struct PLU(M) 5229 if(isNarrowMatrix!M && !isAbstractMatrix!M) 5230 { 5231 static if(hasStaticRows!M) 5232 alias rows = lu.rows; 5233 else 5234 @property size_t rows() pure nothrow @safe const { return piv.length; } 5235 5236 alias cols = rows; 5237 5238 size_t[] piv; 5239 bool isEvenP; 5240 M lu; 5241 5242 5243 auto p() pure nothrow @safe const 5244 { 5245 return permutation(piv); 5246 } 5247 5248 5249 5250 auto l() pure nothrow @safe 5251 { 5252 static struct L() 5253 { 5254 static if(hasStaticRows!M) 5255 enum rows = M.rows; 5256 else static if(hasStaticCols!M) 5257 enum rows = M.cols; 5258 else 5259 size_t rows() const @property { return _lu.rows; } 5260 5261 alias cols = rows; 5262 5263 5264 typeof(_lu[0, 0]) opIndex(size_t i, size_t j) const 5265 in{ 5266 assert(i < rows); 5267 assert(j < cols); 5268 } 5269 body{ 5270 if(i == j) 5271 return cast(typeof(return))1; 5272 else if(i < j) 5273 return cast(typeof(return))0; 5274 else 5275 return _lu[i, j]; 5276 } 5277 5278 5279 mixin(defaultExprOps!(false)); 5280 5281 5282 private: 5283 M _lu; 5284 } 5285 5286 5287 return L!()(lu); 5288 } 5289 5290 5291 5292 auto u() pure nothrow @safe 5293 { 5294 static struct U() 5295 { 5296 static if(hasStaticRows!M) 5297 enum rows = M.rows; 5298 else static if(hasStaticCols!M) 5299 enum rows = M.cols; 5300 else 5301 size_t rows() const @property { return _lu.rows; } 5302 5303 alias cols = rows; 5304 5305 5306 typeof(_lu[0, 0]) opIndex(size_t i, size_t j) const 5307 in{ 5308 assert(i < rows); 5309 assert(j < cols); 5310 } 5311 body{ 5312 if(i > j) 5313 return cast(typeof(return))0; 5314 else 5315 return _lu[i, j]; 5316 } 5317 5318 5319 mixin(defaultExprOps!(false)); 5320 5321 5322 private: 5323 M _lu; 5324 } 5325 5326 5327 return U!()(lu); 5328 } 5329 5330 5331 /** 5332 Ax = bとなるxを解きます 5333 */ 5334 auto solveInPlace(V)(V b) 5335 if(isVector!V /*&& isFloatingPoint!(ElementType!V) && is(typeof({b[0] = real.init;}))*/ ) 5336 in{ 5337 assert(b.length == rows); 5338 } 5339 body{ 5340 /* 5341 Ax = bの両辺にPをかけることにより 5342 PAx = Pbとなるが、LU分解によりPA = LUであるから、 5343 LUx = Pbである。 5344 5345 ここで、y=Uxとなるyベクトルを考える。 5346 Ly = Pbである。 5347 Lは下三角行列なので、簡単にyベクトルは求まる。 5348 5349 次に、Ux=yより、xベクトルを同様に求めれば良い 5350 */ 5351 5352 immutable size_t size = rows; 5353 5354 // b <- Pb 5355 b = this.p * b; 5356 5357 // Ly=Pbからyを求める 5358 foreach(i; 1 .. size) 5359 foreach(j; 0 .. i) 5360 b[i] -= lu[i, j] * b[j]; 5361 5362 // Ux=Py 5363 foreach_reverse(i; 0 .. size){ 5364 foreach_reverse(j; i+1 .. size) 5365 b[i] -= lu[i, j] * b[j]; 5366 5367 b[i] /= lu[i, i]; 5368 } 5369 } 5370 5371 5372 /** 5373 逆行列を求める 5374 */ 5375 M inverse() @property 5376 { 5377 immutable size_t size = lu.rows; 5378 5379 M m = identity!(ElementType!M)().congeal(size, size); 5380 5381 foreach(i; 0 .. lu.cols) 5382 this.solveInPlace(m.rowVectors[i]); 5383 5384 return m; 5385 } 5386 5387 5388 auto det() @property 5389 { 5390 auto dst = cast(ElementType!M)(isEvenP ? 1 : -1); 5391 foreach(i; 0 .. rows) 5392 dst *= lu[i, i]; 5393 5394 return dst; 5395 } 5396 } 5397 5398 5399 /** 5400 In-Placeで、行列をLU分解します。 5401 5402 "Numerical Recipes in C" 5403 */ 5404 PLU!(A) pluDecomposeInPlace(A)(A m) 5405 if(isNarrowMatrix!A && hasLvalueElements!A && (!hasStaticRows!A || !hasStaticCols!A || is(typeof({static assert(A.rows == A.cols);})))) 5406 in{ 5407 assert(m.rows == m.cols); 5408 } 5409 body{ 5410 immutable size = m.rows; 5411 scope vv = new real[size]; 5412 bool isEvenP; 5413 size_t[] idx = new size_t[size]; 5414 5415 foreach(i, ref e; idx) 5416 e = i; 5417 5418 foreach(i; 0 .. size){ 5419 real big = 0; 5420 5421 foreach(j; 0 .. size){ 5422 immutable temp = m[i, j].abs(); 5423 if(temp > big) 5424 big = temp; 5425 } 5426 5427 if(big == 0) enforce("Input matrix is a singular matrix"); 5428 5429 vv[i] = 1.0 / big; 5430 } 5431 5432 5433 foreach(j; 0 .. size){ 5434 foreach(i; 0 .. j){ 5435 auto sum = m[i, j]; 5436 foreach(k; 0 .. i) sum -= m[i, k] * m[k, j]; 5437 m[i, j] = sum; 5438 } 5439 5440 real big = 0; 5441 size_t imax; 5442 foreach(i; j .. size){ 5443 auto sum = m[i, j]; 5444 foreach(k; 0 .. j) sum -= m[i, k] * m[k, j]; 5445 m[i, j] = sum; 5446 5447 immutable dum = vv[i] * sum.abs(); 5448 if(dum >= big){ 5449 big = dum; 5450 imax = i; 5451 } 5452 } 5453 5454 if(j != imax){ 5455 foreach(k; 0 .. size) 5456 swap(m[imax, k], m[j, k]); 5457 5458 isEvenP = !isEvenP; 5459 vv[imax] = vv[j]; 5460 5461 swap(idx[j], idx[imax]); 5462 } 5463 5464 //idx[j] = imax; 5465 5466 //if(m[j, j] == 0) m[j, j] = 1.0E-20; 5467 5468 if(j != size-1){ 5469 immutable dum = 1 / m[j, j]; 5470 foreach(i; j+1 .. size) 5471 m[i, j] *= dum; 5472 } 5473 } 5474 5475 return PLU!A(idx, isEvenP, m); 5476 } 5477 unittest{ 5478 real[3][3] mStack = [[1, 2, 2], 5479 [2, 1, 1], 5480 [2, 2, 2]]; 5481 5482 auto m = matrix(mStack); 5483 5484 SMatrix!(real, 3, 3) org = m; 5485 auto plu = m.pluDecomposeInPlace(); 5486 5487 SMatrix!(real, 3, 3) result = plu.p.inverse * plu.l * plu.u; 5488 5489 foreach(i; 0 .. 3) foreach(j; 0 .. 3) 5490 assert(approxEqual(result[i, j], org[i, j])); // A = P^(-1)LU 5491 5492 assert(approxEqual(plu.det, 0)); 5493 } 5494 5495 unittest{ 5496 real[3][3] mStack = [[2, 4, 2], 5497 [4, 10, 3], 5498 [3, 7, 1]]; 5499 5500 auto m = mStack.matrix(); 5501 5502 SMatrix!(real, 3, 3) org = m; 5503 auto plu = m.pluDecomposeInPlace(); 5504 5505 auto v = matrix!(3, 1)(cast(real[])[8, 17, 11]); 5506 5507 plu.solveInPlace(v); 5508 foreach(i; 0 .. 3) 5509 assert(approxEqual(v[i], 1)); 5510 } 5511 5512 unittest{ 5513 real[3][3] mStack = [[2, 4, 2], 5514 [4, 10, 3], 5515 [3, 7, 1]]; 5516 5517 auto m = mStack.matrix(); 5518 5519 SMatrix!(real, 3, 3) org = m; 5520 auto plu = m.pluDecomposeInPlace(); 5521 auto iden = org * plu.inverse; 5522 5523 foreach(i; 0 .. 3) 5524 foreach(j; 0 .. 3) 5525 assert(approxEqual(iden[i, j], identity!real[i, j])); 5526 } 5527 5528 5529 /** 5530 ベクトルのノルムを計算します 5531 */ 5532 auto norm(real N = 2, V)(V v) 5533 { 5534 real sum = 0; 5535 foreach(i; 0 .. v.length) 5536 sum += v[i] ^^ N; 5537 return sum ^^ (1/N); 5538 }