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