1 /** 2 Vector module. 3 */ 4 module karasux.linear_algebra.vector; 5 6 import std.math : isClose; 7 import std.traits : isNumeric; 8 9 import karasux.linear_algebra.matrix : Matrix; 10 11 @safe: 12 13 /** 14 Vector structure. 15 16 Params: 17 D = dimensions. 18 E = element type. 19 */ 20 struct Vector(size_t D, E = float) 21 { 22 static assert(D > 0); 23 static assert(isNumeric!E); 24 25 /** 26 Get an element. 27 28 Params: 29 i = index. 30 Returns: 31 element value. 32 */ 33 ref const(E) opIndex(size_t i) const return scope 34 in (i < D) 35 { 36 return elements_[i]; 37 } 38 39 /** 40 Set an element. 41 42 Params: 43 value = element value. 44 i = index. 45 Returns: 46 assigned element value. 47 */ 48 ref const(E) opIndexAssign()(auto ref const(E) value, size_t i) return scope 49 in (i < D) 50 { 51 return elements_[i] = value; 52 } 53 54 /** 55 operation and assign an element. 56 57 Params: 58 op = operator. 59 value = element value. 60 i = index. 61 Returns: 62 assigned element value. 63 */ 64 ref const(E) opIndexOpAssign(string op)(auto ref const(E) value, size_t i) return scope 65 in (i < D) 66 { 67 return mixin("elements_[i] " ~ op ~ "= value"); 68 } 69 70 /** 71 Operation and assign other vector. 72 73 Params: 74 value = other vetor value. 75 Returns: 76 this vector. 77 */ 78 ref typeof(this) opOpAssign(string op)(auto ref const(typeof(this)) value) return scope 79 { 80 foreach (i, ref v; elements_) 81 { 82 mixin("v " ~ op ~ "= value[i];"); 83 } 84 return this; 85 } 86 87 /** 88 Returns: 89 elements slice. 90 */ 91 const(E)[] opSlice() const return scope 92 { 93 return elements_[]; 94 } 95 96 /** 97 Fill elements. 98 99 Params: 100 value = filler value. 101 */ 102 ref typeof(this) fill()(auto ref const(E) value) return scope 103 { 104 elements_[] = value; 105 return this; 106 } 107 108 /** 109 Vector pointer. 110 111 Returns: 112 vector pointer. 113 */ 114 @property const(E)* ptr() const return scope 115 out (r; r != null) 116 { 117 return &elements_[0]; 118 } 119 120 /** 121 Matrix multiply for vector. 122 123 Params: 124 m = argument matrix. 125 v = argument vector. 126 Returns: 127 result vector. 128 */ 129 ref typeof(this) mul()( 130 auto scope ref const(Matrix!(D, D, E)) m, 131 auto scope ref const(Vector!(D, E)) v) @nogc nothrow pure return scope 132 { 133 foreach (row; 0 .. D) 134 { 135 E value = E(0); 136 foreach (column; 0 .. D) 137 { 138 value += m[row, column] * v[column]; 139 } 140 elements_[row] = value; 141 } 142 return this; 143 } 144 145 private: 146 E[D] elements_; 147 } 148 149 /// 150 @nogc nothrow pure unittest 151 { 152 immutable v = Vector!3([1, 2, 3]); 153 assert(v[0].isClose(1.0)); 154 assert(v[1].isClose(2.0)); 155 assert(v[2].isClose(3.0)); 156 } 157 158 /// 159 @nogc nothrow pure unittest 160 { 161 auto v = Vector!3([1, 2, 3]); 162 v[0] = 2.0f; 163 v[1] = 3.0f; 164 v[2] = 4.0f; 165 166 assert(v[0].isClose(2.0)); 167 assert(v[1].isClose(3.0)); 168 assert(v[2].isClose(4.0)); 169 170 v[0] += 1.0f; 171 v[1] += 1.0f; 172 v[2] += 1.0f; 173 174 assert(v[0].isClose(3.0)); 175 assert(v[1].isClose(4.0)); 176 assert(v[2].isClose(5.0)); 177 } 178 179 /// 180 @nogc nothrow pure unittest 181 { 182 auto v = Vector!3([1, 2, 3]); 183 immutable u = Vector!3([2, 3, 4]); 184 v += u; 185 186 assert(v[0].isClose(3.0)); 187 assert(v[1].isClose(5.0)); 188 assert(v[2].isClose(7.0)); 189 } 190 191 /// 192 @nogc nothrow pure unittest 193 { 194 import std.math : isNaN; 195 196 Vector!3 v; 197 assert(v[0].isNaN); 198 assert(v[1].isNaN); 199 assert(v[2].isNaN); 200 201 immutable u = Vector!3([2, 3, 4]); 202 foreach (i, e; u[]) 203 { 204 v[i] = e; 205 } 206 207 assert(v[0].isClose(2.0)); 208 assert(v[1].isClose(3.0)); 209 assert(v[2].isClose(4.0)); 210 } 211 212 /// 213 @nogc nothrow pure unittest 214 { 215 import std.math : isNaN; 216 217 Vector!3 v; 218 v.fill(1.0); 219 foreach (e; v[]) 220 { 221 assert(e.isClose(1.0)); 222 } 223 } 224 225 /// 226 @nogc nothrow pure unittest 227 { 228 immutable v = Vector!3([1, 2, 3]); 229 assert(isClose(*(v.ptr), 1.0)); 230 } 231 232 /// 233 @nogc nothrow pure unittest 234 { 235 import std.math : isClose; 236 237 immutable m = Matrix!(4, 4).unit; 238 immutable v = Vector!4([1, 2, 3, 0]); 239 auto result = Vector!4(); 240 result.mul(m, v); 241 assert(result == v); 242 } 243 244 /// 245 @nogc nothrow pure unittest 246 { 247 import karasux.linear_algebra.vector : isClose; 248 249 immutable m = Matrix!(4, 4).scale(2.0, 3.0, 4.0); 250 immutable v = Vector!4([1, 2, 3, 0]); 251 auto result = Vector!4(); 252 result.mul(m, v); 253 254 assert(result.isClose(Vector!4([2, 6, 12, 0]))); 255 } 256 257 /** 258 isClose for vector. 259 260 Params: 261 a = vector. 262 b = other vector. 263 Returns: 264 true if both vector are close. 265 */ 266 bool isClose(size_t D, E)( 267 auto scope ref const(Vector!(D, E)) a, 268 auto scope ref const(Vector!(D, E)) b) @nogc nothrow pure 269 { 270 import std.math : mathIsClose = isClose; 271 272 foreach (i; 0 .. D) 273 { 274 if (!mathIsClose(a[i], b[i])) 275 { 276 return false; 277 } 278 } 279 280 return true; 281 } 282 283 /// 284 @nogc nothrow pure unittest 285 { 286 immutable v = Vector!3([1, 2, 3]); 287 assert(v.isClose(Vector!3([1, 2, 3]))); 288 assert(!v.isClose(Vector!3([0.9, 2, 3]))); 289 assert(!v.isClose(Vector!3([1, 1.9, 3]))); 290 assert(!v.isClose(Vector!3([1, 2, 2.9]))); 291 } 292