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