ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

math.c (26113B)


      1 /* See LICENSE for license details. */
      2 #include "external/cephes.c"
      3 
      4 function void
      5 fill_kronecker_sub_matrix_f16(f16 *out, i32 out_stride, f16 scale, const f16 *b, iv2 b_dim)
      6 {
      7 	for (i32 i = 0; i < b_dim.y; i++) {
      8 		for (i32 j = 0; j < b_dim.x; j += 4, b += 4) {
      9 			out[j + 0] = scale * b[0];
     10 			out[j + 1] = scale * b[1];
     11 			out[j + 2] = scale * b[2];
     12 			out[j + 3] = scale * b[3];
     13 		}
     14 		out += out_stride;
     15 	}
     16 }
     17 
     18 /* NOTE: this won't check for valid space/etc and assumes row major order */
     19 function void
     20 kronecker_product_f16(f16 *out, const f16 *a, iv2 a_dim, const f16 *b, iv2 b_dim)
     21 {
     22 	iv2 out_dim = {{a_dim.x * b_dim.x, a_dim.y * b_dim.y}};
     23 	assert(out_dim.y % 4 == 0);
     24 	for (i32 i = 0; i < a_dim.y; i++) {
     25 		f16 *vout = out;
     26 		for (i32 j = 0; j < a_dim.x; j++, a++) {
     27 			fill_kronecker_sub_matrix_f16(vout, out_dim.y, *a, b, b_dim);
     28 			vout += b_dim.y;
     29 		}
     30 		out += out_dim.y * b_dim.x;
     31 	}
     32 }
     33 
     34 /* NOTE/TODO: to support even more hadamard sizes use the Paley construction */
     35 function f16 *
     36 make_hadamard_transpose(Arena *arena, i32 dim, b32 row_major)
     37 {
     38 	read_only f16 hadamard_12_12_transpose[] = {
     39 		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
     40 		1, -1, -1,  1, -1, -1, -1,  1,  1,  1, -1,  1,
     41 		1,  1, -1, -1,  1, -1, -1, -1,  1,  1,  1, -1,
     42 		1, -1,  1, -1, -1,  1, -1, -1, -1,  1,  1,  1,
     43 		1,  1, -1,  1, -1, -1,  1, -1, -1, -1,  1,  1,
     44 		1,  1,  1, -1,  1, -1, -1,  1, -1, -1, -1,  1,
     45 		1,  1,  1,  1, -1,  1, -1, -1,  1, -1, -1, -1,
     46 		1, -1,  1,  1,  1, -1,  1, -1, -1,  1, -1, -1,
     47 		1, -1, -1,  1,  1,  1, -1,  1, -1, -1,  1, -1,
     48 		1, -1, -1, -1,  1,  1,  1, -1,  1, -1, -1,  1,
     49 		1,  1, -1, -1, -1,  1,  1,  1, -1,  1, -1, -1,
     50 		1, -1,  1, -1, -1, -1,  1,  1,  1, -1,  1, -1,
     51 	};
     52 
     53 	read_only f16 hadamard_20_20_transpose[] = {
     54 		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
     55 		1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1,
     56 		1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1,
     57 		1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,
     58 		1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,
     59 		1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1,
     60 		1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1,
     61 		1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1,
     62 		1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1,
     63 		1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,
     64 		1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1,
     65 		1,  1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,
     66 		1, -1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1,
     67 		1,  1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,
     68 		1,  1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,
     69 		1,  1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,
     70 		1,  1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,
     71 		1, -1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1,
     72 		1, -1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1,
     73 		1,  1, -1, -1,  1,  1, -1, -1, -1, -1,  1, -1,  1, -1,  1,  1,  1,  1, -1, -1,
     74 	};
     75 
     76 	f16 *result = 0;
     77 
     78 	i32 order          = dim;
     79 	b32 power_of_2     = IsPowerOfTwo(dim);
     80 	b32 multiple_of_12 = dim % 12 == 0;
     81 	b32 multiple_of_20 = dim % 20 == 0;
     82 	i64 elements       = dim * dim;
     83 
     84 	i32 base_dim = 0;
     85 	if (power_of_2) {
     86 		base_dim  = dim;
     87 	} else if (multiple_of_20 && IsPowerOfTwo(dim / 20)) {
     88 		base_dim  = 20;
     89 		dim      /= 20;
     90 	} else if (multiple_of_12 && IsPowerOfTwo(dim / 12)) {
     91 		base_dim  = 12;
     92 		dim      /= 12;
     93 	}
     94 
     95 	if (power_of_2 && base_dim) {
     96 		result = push_array(arena, f16, elements);
     97 
     98 		Temp scratch = temp_begin(arena);
     99 		f16 *m = dim == base_dim ? result : push_array(arena, f16, elements);
    100 
    101 		#define IND(i, j) ((i) * dim + (j))
    102 		m[0] = 1;
    103 		for (i32 k = 1; k < dim; k *= 2) {
    104 			for (i32 i = 0; i < k; i++) {
    105 				for (i32 j = 0; j < k; j++) {
    106 					f16 val = m[IND(i, j)];
    107 					m[IND(i + k, j)]     =  val;
    108 					m[IND(i, j + k)]     =  val;
    109 					m[IND(i + k, j + k)] = -val;
    110 				}
    111 			}
    112 		}
    113 		#undef IND
    114 
    115 		const f16 *m2 = 0;
    116 		iv2 m2_dim;
    117 		switch (base_dim) {
    118 		case 12:{ m2 = hadamard_12_12_transpose; m2_dim = (iv2){{12, 12}}; }break;
    119 		case 20:{ m2 = hadamard_20_20_transpose; m2_dim = (iv2){{20, 20}}; }break;
    120 		}
    121 		if (m2) kronecker_product_f16(result, m, (iv2){{dim, dim}}, m2, m2_dim);
    122 
    123 		temp_end(scratch);
    124 	}
    125 
    126 	if (result && row_major) {
    127 		for (i32 r = 0; r < order; r++)
    128 			for (i32 c = 0; c < order; c++)
    129 				swap(result[r * order + c], result[c * order + r]);
    130 	}
    131 
    132 	return result;
    133 }
    134 
    135 function u32
    136 ceil_div_u32(u32 a, u32 b)
    137 {
    138 	u32 result = ((u64)a + b - 1) / b;
    139 	return result;
    140 }
    141 
    142 function b32
    143 u128_equal(u128 a, u128 b)
    144 {
    145 	b32 result = a.U64[0] == b.U64[0] && a.U64[1] == b.U64[1];
    146 	return result;
    147 }
    148 
    149 function RangeU64
    150 subrange_n_from_n_m_count(u64 n, u64 n_count, u64 m)
    151 {
    152 	assert(n < n_count);
    153 
    154 	u64 per_lane            = m / n_count;
    155 	u64 leftover            = m - per_lane * n_count;
    156 	u64 leftovers_before_n  = Min(leftover, n);
    157 	u64 base_index          = n * per_lane + leftovers_before_n;
    158 	u64 one_past_last_index = base_index + per_lane + ((n < leftover) ? 1 : 0);
    159 
    160 	RangeU64 result = {base_index, one_past_last_index};
    161 	return result;
    162 }
    163 
    164 function i32
    165 iv3_dimension(iv3 points)
    166 {
    167 	i32 result = (points.x > 1) + (points.y > 1) + (points.z > 1);
    168 	return result;
    169 }
    170 
    171 function bv3
    172 iv3_equal(iv3 a, iv3 b)
    173 {
    174 	bv3 result;
    175 	result.x = a.x == b.x;
    176 	result.y = a.y == b.y;
    177 	result.z = a.z == b.z;
    178 	return result;
    179 }
    180 
    181 function b32
    182 bv3_all(bv3 a)
    183 {
    184 	b32 result = a.x != 0 && a.y != 0 && a.z != 0;
    185 	return result;
    186 }
    187 
    188 function b32
    189 bv3_any(bv3 a)
    190 {
    191 	b32 result = a.x != 0 || a.y != 0 || a.z != 0;
    192 	return result;
    193 }
    194 
    195 function v2
    196 clamp_v2_rect(v2 v, Rect r)
    197 {
    198 	v2 result = v;
    199 	result.x = Clamp(v.x, r.pos.x, r.pos.x + r.size.x);
    200 	result.y = Clamp(v.y, r.pos.y, r.pos.y + r.size.y);
    201 	return result;
    202 }
    203 
    204 function v2
    205 v2_from_iv2(iv2 v)
    206 {
    207 	v2 result;
    208 	result.E[0] = (f32)v.E[0];
    209 	result.E[1] = (f32)v.E[1];
    210 	return result;
    211 }
    212 
    213 function v2
    214 v2_abs(v2 a)
    215 {
    216 	v2 result;
    217 	result.x = Abs(a.x);
    218 	result.y = Abs(a.y);
    219 	return result;
    220 }
    221 
    222 function v2
    223 v2_scale(v2 a, f32 scale)
    224 {
    225 	v2 result;
    226 	result.x = a.x * scale;
    227 	result.y = a.y * scale;
    228 	return result;
    229 }
    230 
    231 function v2
    232 v2_add(v2 a, v2 b)
    233 {
    234 	v2 result;
    235 	result.x = a.x + b.x;
    236 	result.y = a.y + b.y;
    237 	return result;
    238 }
    239 
    240 function v2
    241 v2_sub(v2 a, v2 b)
    242 {
    243 	v2 result = v2_add(a, v2_scale(b, -1.0f));
    244 	return result;
    245 }
    246 
    247 function v2
    248 v2_mul(v2 a, v2 b)
    249 {
    250 	v2 result;
    251 	result.x = a.x * b.x;
    252 	result.y = a.y * b.y;
    253 	return result;
    254 }
    255 
    256 function v2
    257 v2_div(v2 a, v2 b)
    258 {
    259 	v2 result;
    260 	result.x = a.x / b.x;
    261 	result.y = a.y / b.y;
    262 	return result;
    263 }
    264 
    265 function v2
    266 v2_floor(v2 a)
    267 {
    268 	v2 result;
    269 	result.x = (f32)((i32)a.x);
    270 	result.y = (f32)((i32)a.y);
    271 	return result;
    272 }
    273 
    274 function f32
    275 v2_magnitude_squared(v2 a)
    276 {
    277 	f32 result = a.x * a.x + a.y * a.y;
    278 	return result;
    279 }
    280 
    281 function f32
    282 v2_magnitude(v2 a)
    283 {
    284 	f32 result = sqrt_f32(a.x * a.x + a.y * a.y);
    285 	return result;
    286 }
    287 
    288 function v3
    289 cross(v3 a, v3 b)
    290 {
    291 	v3 result;
    292 	result.x = a.y * b.z - a.z * b.y;
    293 	result.y = a.z * b.x - a.x * b.z;
    294 	result.z = a.x * b.y - a.y * b.x;
    295 	return result;
    296 }
    297 
    298 function v3
    299 v3_from_iv3(iv3 v)
    300 {
    301 	v3 result;
    302 	result.E[0] = (f32)v.E[0];
    303 	result.E[1] = (f32)v.E[1];
    304 	result.E[2] = (f32)v.E[2];
    305 	return result;
    306 }
    307 
    308 function v3
    309 v3_abs(v3 a)
    310 {
    311 	v3 result;
    312 	result.x = Abs(a.x);
    313 	result.y = Abs(a.y);
    314 	result.z = Abs(a.z);
    315 	return result;
    316 }
    317 
    318 function v3
    319 v3_scale(v3 a, f32 scale)
    320 {
    321 	v3 result;
    322 	result.x = scale * a.x;
    323 	result.y = scale * a.y;
    324 	result.z = scale * a.z;
    325 	return result;
    326 }
    327 
    328 function v3
    329 v3_add(v3 a, v3 b)
    330 {
    331 	v3 result;
    332 	result.x = a.x + b.x;
    333 	result.y = a.y + b.y;
    334 	result.z = a.z + b.z;
    335 	return result;
    336 }
    337 
    338 function v3
    339 v3_sub(v3 a, v3 b)
    340 {
    341 	v3 result = v3_add(a, v3_scale(b, -1.0f));
    342 	return result;
    343 }
    344 
    345 function v3
    346 v3_div(v3 a, v3 b)
    347 {
    348 	v3 result;
    349 	result.x = a.x / b.x;
    350 	result.y = a.y / b.y;
    351 	result.z = a.z / b.z;
    352 	return result;
    353 }
    354 
    355 function f32
    356 v3_dot(v3 a, v3 b)
    357 {
    358 	f32 result = a.x * b.x + a.y * b.y + a.z * b.z;
    359 	return result;
    360 }
    361 
    362 function f32
    363 v3_magnitude_squared(v3 a)
    364 {
    365 	f32 result = v3_dot(a, a);
    366 	return result;
    367 }
    368 
    369 function f32
    370 v3_magnitude(v3 a)
    371 {
    372 	f32 result = sqrt_f32(v3_dot(a, a));
    373 	return result;
    374 }
    375 
    376 function v3
    377 v3_normalize(v3 a)
    378 {
    379 	v3 result = v3_scale(a, 1.0f / v3_magnitude(a));
    380 	return result;
    381 }
    382 
    383 function v4
    384 v4_scale(v4 a, f32 scale)
    385 {
    386 	v4 result;
    387 	result.x = scale * a.x;
    388 	result.y = scale * a.y;
    389 	result.z = scale * a.z;
    390 	result.w = scale * a.w;
    391 	return result;
    392 }
    393 
    394 function v4
    395 v4_add(v4 a, v4 b)
    396 {
    397 	v4 result;
    398 	result.x = a.x + b.x;
    399 	result.y = a.y + b.y;
    400 	result.z = a.z + b.z;
    401 	result.w = a.w + b.w;
    402 	return result;
    403 }
    404 
    405 function v4
    406 v4_sub(v4 a, v4 b)
    407 {
    408 	v4 result = v4_add(a, v4_scale(b, -1));
    409 	return result;
    410 }
    411 
    412 function f32
    413 v4_dot(v4 a, v4 b)
    414 {
    415 	f32 result = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
    416 	return result;
    417 }
    418 
    419 function v4
    420 v4_lerp(v4 a, v4 b, f32 t)
    421 {
    422 	v4 result = v4_add(a, v4_scale(v4_sub(b, a), t));
    423 	return result;
    424 }
    425 
    426 function b32
    427 m4_equal(m4 a, m4 b)
    428 {
    429 	b32 result = 1;
    430 	#ifdef __AVX512F__
    431 	__m512 one = _mm512_set1_ps(1.f);
    432 	__m512 eps = _mm512_set1_ps(F32_EPSILON);
    433 	__m512 av  = _mm512_loadu_ps(&a);
    434 	__m512 bv  = _mm512_loadu_ps(&b);
    435 	__m512 delta = _mm512_abs_ps(_mm512_sub_ps(av, bv));
    436 	__m512 test  = _mm512_mul_ps(eps, _mm512_max_ps(one, _mm512_max_ps(_mm512_abs_ps(av), _mm512_abs_ps(bv))));
    437 	result = _mm512_cmp_ps_mask(delta, test, _CMP_GT_OS) == 0;
    438 	#else
    439 	for EachElement(a.E, it)
    440 		result &= f32_equal(a.E[it], b.E[it]);
    441 	#endif
    442 	return result;
    443 }
    444 
    445 #define m4_identity() \
    446 	(m4){.E = { \
    447 		1, 0, 0, 0, \
    448 		0, 1, 0, 0, \
    449 		0, 0, 1, 0, \
    450 		0, 0, 0, 1, \
    451 	}}
    452 
    453 function v4
    454 m4_row(m4 a, u32 row)
    455 {
    456 	v4 result;
    457 	result.E[0] = a.c[0].E[row];
    458 	result.E[1] = a.c[1].E[row];
    459 	result.E[2] = a.c[2].E[row];
    460 	result.E[3] = a.c[3].E[row];
    461 	return result;
    462 }
    463 
    464 function m4
    465 m4_mul(m4 a, m4 b)
    466 {
    467 	m4 result;
    468 	for (u32 i = 0; i < 4; i++) {
    469 		for (u32 j = 0; j < 4; j++) {
    470 			result.c[i].E[j] = v4_dot(m4_row(a, j), b.c[i]);
    471 		}
    472 	}
    473 	return result;
    474 }
    475 
    476 /* NOTE(rnp): based on:
    477  * https://web.archive.org/web/20131215123403/ftp://download.intel.com/design/PentiumIII/sml/24504301.pdf
    478  * TODO(rnp): redo with SIMD as given in the link (but need to rewrite for column-major)
    479  */
    480 function m4
    481 m4_inverse(m4 m)
    482 {
    483 	m4 result;
    484 	result.E[ 0] =  m.E[5] * m.E[10] * m.E[15] - m.E[5] * m.E[11] * m.E[14] - m.E[9] * m.E[6] * m.E[15] + m.E[9] * m.E[7] * m.E[14] + m.E[13] * m.E[6] * m.E[11] - m.E[13] * m.E[7] * m.E[10];
    485 	result.E[ 4] = -m.E[4] * m.E[10] * m.E[15] + m.E[4] * m.E[11] * m.E[14] + m.E[8] * m.E[6] * m.E[15] - m.E[8] * m.E[7] * m.E[14] - m.E[12] * m.E[6] * m.E[11] + m.E[12] * m.E[7] * m.E[10];
    486 	result.E[ 8] =  m.E[4] * m.E[ 9] * m.E[15] - m.E[4] * m.E[11] * m.E[13] - m.E[8] * m.E[5] * m.E[15] + m.E[8] * m.E[7] * m.E[13] + m.E[12] * m.E[5] * m.E[11] - m.E[12] * m.E[7] * m.E[ 9];
    487 	result.E[12] = -m.E[4] * m.E[ 9] * m.E[14] + m.E[4] * m.E[10] * m.E[13] + m.E[8] * m.E[5] * m.E[14] - m.E[8] * m.E[6] * m.E[13] - m.E[12] * m.E[5] * m.E[10] + m.E[12] * m.E[6] * m.E[ 9];
    488 	result.E[ 1] = -m.E[1] * m.E[10] * m.E[15] + m.E[1] * m.E[11] * m.E[14] + m.E[9] * m.E[2] * m.E[15] - m.E[9] * m.E[3] * m.E[14] - m.E[13] * m.E[2] * m.E[11] + m.E[13] * m.E[3] * m.E[10];
    489 	result.E[ 5] =  m.E[0] * m.E[10] * m.E[15] - m.E[0] * m.E[11] * m.E[14] - m.E[8] * m.E[2] * m.E[15] + m.E[8] * m.E[3] * m.E[14] + m.E[12] * m.E[2] * m.E[11] - m.E[12] * m.E[3] * m.E[10];
    490 	result.E[ 9] = -m.E[0] * m.E[ 9] * m.E[15] + m.E[0] * m.E[11] * m.E[13] + m.E[8] * m.E[1] * m.E[15] - m.E[8] * m.E[3] * m.E[13] - m.E[12] * m.E[1] * m.E[11] + m.E[12] * m.E[3] * m.E[ 9];
    491 	result.E[13] =  m.E[0] * m.E[ 9] * m.E[14] - m.E[0] * m.E[10] * m.E[13] - m.E[8] * m.E[1] * m.E[14] + m.E[8] * m.E[2] * m.E[13] + m.E[12] * m.E[1] * m.E[10] - m.E[12] * m.E[2] * m.E[ 9];
    492 	result.E[ 2] =  m.E[1] * m.E[ 6] * m.E[15] - m.E[1] * m.E[ 7] * m.E[14] - m.E[5] * m.E[2] * m.E[15] + m.E[5] * m.E[3] * m.E[14] + m.E[13] * m.E[2] * m.E[ 7] - m.E[13] * m.E[3] * m.E[ 6];
    493 	result.E[ 6] = -m.E[0] * m.E[ 6] * m.E[15] + m.E[0] * m.E[ 7] * m.E[14] + m.E[4] * m.E[2] * m.E[15] - m.E[4] * m.E[3] * m.E[14] - m.E[12] * m.E[2] * m.E[ 7] + m.E[12] * m.E[3] * m.E[ 6];
    494 	result.E[10] =  m.E[0] * m.E[ 5] * m.E[15] - m.E[0] * m.E[ 7] * m.E[13] - m.E[4] * m.E[1] * m.E[15] + m.E[4] * m.E[3] * m.E[13] + m.E[12] * m.E[1] * m.E[ 7] - m.E[12] * m.E[3] * m.E[ 5];
    495 	result.E[14] = -m.E[0] * m.E[ 5] * m.E[14] + m.E[0] * m.E[ 6] * m.E[13] + m.E[4] * m.E[1] * m.E[14] - m.E[4] * m.E[2] * m.E[13] - m.E[12] * m.E[1] * m.E[ 6] + m.E[12] * m.E[2] * m.E[ 5];
    496 	result.E[ 3] = -m.E[1] * m.E[ 6] * m.E[11] + m.E[1] * m.E[ 7] * m.E[10] + m.E[5] * m.E[2] * m.E[11] - m.E[5] * m.E[3] * m.E[10] - m.E[ 9] * m.E[2] * m.E[ 7] + m.E[ 9] * m.E[3] * m.E[ 6];
    497 	result.E[ 7] =  m.E[0] * m.E[ 6] * m.E[11] - m.E[0] * m.E[ 7] * m.E[10] - m.E[4] * m.E[2] * m.E[11] + m.E[4] * m.E[3] * m.E[10] + m.E[ 8] * m.E[2] * m.E[ 7] - m.E[ 8] * m.E[3] * m.E[ 6];
    498 	result.E[11] = -m.E[0] * m.E[ 5] * m.E[11] + m.E[0] * m.E[ 7] * m.E[ 9] + m.E[4] * m.E[1] * m.E[11] - m.E[4] * m.E[3] * m.E[ 9] - m.E[ 8] * m.E[1] * m.E[ 7] + m.E[ 8] * m.E[3] * m.E[ 5];
    499 	result.E[15] =  m.E[0] * m.E[ 5] * m.E[10] - m.E[0] * m.E[ 6] * m.E[ 9] - m.E[4] * m.E[1] * m.E[10] + m.E[4] * m.E[2] * m.E[ 9] + m.E[ 8] * m.E[1] * m.E[ 6] - m.E[ 8] * m.E[2] * m.E[ 5];
    500 
    501 	f32 determinant = m.E[0] * result.E[0] + m.E[1] * result.E[4] + m.E[2] * result.E[8] + m.E[3] * result.E[12];
    502 	determinant = 1.0f / determinant;
    503 	for(i32 i = 0; i < 16; i++)
    504 		result.E[i] *= determinant;
    505 	return result;
    506 }
    507 
    508 function m4
    509 m4_translation(v3 delta)
    510 {
    511 	m4 result;
    512 	result.c[0] = (v4){{1, 0, 0, 0}};
    513 	result.c[1] = (v4){{0, 1, 0, 0}};
    514 	result.c[2] = (v4){{0, 0, 1, 0}};
    515 	result.c[3] = (v4){{delta.x, delta.y, delta.z, 1}};
    516 	return result;
    517 }
    518 
    519 function m4
    520 m4_scale(v3 scale)
    521 {
    522 	m4 result;
    523 	result.c[0] = (v4){{scale.x, 0,       0,       0}};
    524 	result.c[1] = (v4){{0,       scale.y, 0,       0}};
    525 	result.c[2] = (v4){{0,       0,       scale.z, 0}};
    526 	result.c[3] = (v4){{0,       0,       0,       1}};
    527 	return result;
    528 }
    529 
    530 function m4
    531 m4_rotation_about_axis(v3 axis, f32 turns)
    532 {
    533 	assert(f32_equal(v3_magnitude_squared(axis), 1.0f));
    534 	f32 sa  = sin_f32(turns * 2 * PI);
    535 	f32 ca  = cos_f32(turns * 2 * PI);
    536 	f32 mca = 1.0f - ca;
    537 
    538 	f32 x = axis.x, x2 = x * x;
    539 	f32 y = axis.y, y2 = y * y;
    540 	f32 z = axis.z, z2 = z * z;
    541 
    542 	m4 result;
    543 	result.c[0] = (v4){{ca + mca * x2,        mca * x * y - sa * z, mca * x * z + sa * y, 0}};
    544 	result.c[1] = (v4){{mca * x * y + sa * z, ca + mca * y2,        mca * y * z - sa * x, 0}};
    545 	result.c[2] = (v4){{mca * x * z - sa * y, mca * y * z + sa * x, ca + mca * z2,        0}};
    546 	result.c[3] = (v4){{0, 0, 0, 1}};
    547 	return result;
    548 }
    549 
    550 function m4
    551 m4_rotation_about_y(f32 turns)
    552 {
    553 	m4 result = m4_rotation_about_axis((v3){.y = 1.0f}, turns);
    554 	return result;
    555 }
    556 
    557 function m4
    558 y_aligned_volume_transform(v3 extent, v3 translation, f32 rotation_turns)
    559 {
    560 	m4 T = m4_translation(translation);
    561 	m4 R = m4_rotation_about_axis((v3){.y = 1.0f}, rotation_turns);
    562 	m4 S = m4_scale(extent);
    563 	m4 result = m4_mul(T, m4_mul(R, S));
    564 	return result;
    565 }
    566 
    567 function v4
    568 m4_mul_v4(m4 a, v4 v)
    569 {
    570 	v4 result;
    571 	result.x = v4_dot(m4_row(a, 0), v);
    572 	result.y = v4_dot(m4_row(a, 1), v);
    573 	result.z = v4_dot(m4_row(a, 2), v);
    574 	result.w = v4_dot(m4_row(a, 3), v);
    575 	return result;
    576 }
    577 
    578 function v3
    579 m4_mul_v3(m4 a, v3 v)
    580 {
    581 	v3 result = m4_mul_v4(a, (v4){{v.x, v.y, v.z, 1.0f}}).xyz;
    582 	return result;
    583 }
    584 
    585 function v2
    586 rect_uv(v2 p, Rect r)
    587 {
    588 	v2 result = v2_div(v2_sub(p, r.pos), r.size);
    589 	return result;
    590 }
    591 
    592 function v2
    593 rect_uv_ndc(v2 p, Rect r)
    594 {
    595 	v2 uv     = rect_uv(p, r);
    596 	v2 result = v2_sub(v2_scale(uv, 2.f), (v2){{1.f, 1.f}});
    597 	return result;
    598 }
    599 
    600 function Rect
    601 rect_intersect(Rect a, Rect b)
    602 {
    603 	v2 ae = v2_add(a.pos, a.size);
    604 	v2 be = v2_add(b.pos, b.size);
    605 
    606 	Rect result   = {0};
    607 	result.pos.x  = Max(a.pos.x, b.pos.x);
    608 	result.pos.y  = Max(a.pos.y, b.pos.y);
    609 	result.size.x = Min(ae.x, be.x) - result.pos.x;
    610 	result.size.y = Min(ae.y, be.y) - result.pos.y;
    611 	return result;
    612 }
    613 
    614 function Rect
    615 rect_squish_centered(Rect a, v2 pct)
    616 {
    617 	v2 delta_size = v2_mul(a.size, pct);
    618 	Rect result;
    619 	result.pos  = v2_add(a.pos,  v2_scale(delta_size, 0.5f));
    620 	result.size = v2_add(a.size, v2_scale(delta_size, -1.f));
    621 	return result;
    622 }
    623 
    624 function Rect
    625 rect_shrink_centered(Rect a, v2 px)
    626 {
    627 	Rect result;
    628 	result.pos  = v2_add(a.pos,  v2_scale(px, 0.5f));
    629 	result.size = v2_add(a.size, v2_scale(px, -1.f));
    630 	return result;
    631 }
    632 
    633 function m4
    634 orthographic_projection(f32 n, f32 f, f32 t, f32 r)
    635 {
    636 	m4 result;
    637 	f32 a = -2 / (f - n);
    638 	f32 b = - (f + n) / (f - n);
    639 	result.c[0] = (v4){{1 / r, 0,     0,  0}};
    640 	result.c[1] = (v4){{0,     1 / t, 0,  0}};
    641 	result.c[2] = (v4){{0,     0,     a,  0}};
    642 	result.c[3] = (v4){{0,     0,     b,  1}};
    643 	return result;
    644 }
    645 
    646 function m4
    647 perspective_projection(f32 n, f32 f, f32 fov, f32 aspect)
    648 {
    649 	m4 result;
    650 	f32 t = n * tan_f32(fov / 2.0f);
    651 	f32 r = t * aspect;
    652 	f32 a = -(f + n) / (f - n);
    653 	f32 b = -2 * f * n / (f - n);
    654 	result.c[0] = (v4){{n / r, 0,     0,  0}};
    655 	result.c[1] = (v4){{0,     n / t, 0,  0}};
    656 	result.c[2] = (v4){{0,     0,     a, -1}};
    657 	result.c[3] = (v4){{0,     0,     b,  0}};
    658 	return result;
    659 }
    660 
    661 function m4
    662 camera_look_at(v3 camera, v3 point)
    663 {
    664 	v3 orthogonal = {{0, 1.0f, 0}};
    665 	v3 normal     = v3_normalize(v3_sub(camera, point));
    666 	v3 right      = cross(orthogonal, normal);
    667 	v3 up         = cross(normal,     right);
    668 
    669 	v3 translate;
    670 	camera      = v3_sub((v3){0}, camera);
    671 	translate.x = v3_dot(camera, right);
    672 	translate.y = v3_dot(camera, up);
    673 	translate.z = v3_dot(camera, normal);
    674 
    675 	m4 result;
    676 	result.c[0] = (v4){{right.x,     up.x,        normal.x,    0}};
    677 	result.c[1] = (v4){{right.y,     up.y,        normal.y,    0}};
    678 	result.c[2] = (v4){{right.z,     up.z,        normal.z,    0}};
    679 	result.c[3] = (v4){{translate.x, translate.y, translate.z, 1}};
    680 	return result;
    681 }
    682 
    683 /* NOTE(rnp): adapted from "Essential Mathematics for Games and Interactive Applications" (Verth, Bishop) */
    684 function f32
    685 obb_raycast(m4 obb_orientation, v3 obb_size, v3 obb_center, ray r)
    686 {
    687 	v3 p = v3_sub(obb_center, r.origin);
    688 	v3 X = obb_orientation.c[0].xyz;
    689 	v3 Y = obb_orientation.c[1].xyz;
    690 	v3 Z = obb_orientation.c[2].xyz;
    691 
    692 	/* NOTE(rnp): projects direction vector onto OBB axis */
    693 	v3 f;
    694 	f.x = v3_dot(X, r.direction);
    695 	f.y = v3_dot(Y, r.direction);
    696 	f.z = v3_dot(Z, r.direction);
    697 
    698 	/* NOTE(rnp): projects relative vector onto OBB axis */
    699 	v3 e;
    700 	e.x = v3_dot(X, p);
    701 	e.y = v3_dot(Y, p);
    702 	e.z = v3_dot(Z, p);
    703 
    704 	f32 result = 0;
    705 	f32 t[6] = {0};
    706 	for (i32 i = 0; i < 3; i++) {
    707 		if (f32_equal(f.E[i], 0)) {
    708 			if (-e.E[i] - obb_size.E[i] > 0 || -e.E[i] + obb_size.E[i] < 0)
    709 				result = -1.0f;
    710 			f.E[i] = F32_EPSILON;
    711 		}
    712 		t[i * 2 + 0] = (e.E[i] + obb_size.E[i]) / f.E[i];
    713 		t[i * 2 + 1] = (e.E[i] - obb_size.E[i]) / f.E[i];
    714 	}
    715 
    716 	if (result != -1) {
    717 		f32 tmin = Max(Max(Min(t[0], t[1]), Min(t[2], t[3])), Min(t[4], t[5]));
    718 		f32 tmax = Min(Min(Max(t[0], t[1]), Max(t[2], t[3])), Max(t[4], t[5]));
    719 		if (tmax >= 0 && tmin <= tmax) {
    720 			result = tmin > 0 ? tmin : tmax;
    721 		} else {
    722 			result = -1;
    723 		}
    724 	}
    725 
    726 	return result;
    727 }
    728 
    729 function f32
    730 complex_filter_first_moment(v2 *filter, i32 length, f32 sampling_frequency)
    731 {
    732 	f32 n = 0, d = 0;
    733 	for (i32 i = 0; i < length; i++) {
    734 		f32 t = v2_magnitude_squared(filter[i]);
    735 		n += (f32)i * t;
    736 		d += t;
    737 	}
    738 	f32 result = n / d / sampling_frequency;
    739 	return result;
    740 }
    741 
    742 function f32
    743 real_filter_first_moment(f32 *filter, i32 length, f32 sampling_frequency)
    744 {
    745 	f32 n = 0, d = 0;
    746 	for (i32 i = 0; i < length; i++) {
    747 		f32 t = filter[i] * filter[i];
    748 		n += (f32)i * t;
    749 		d += t;
    750 	}
    751 	f32 result = n / d / sampling_frequency;
    752 	return result;
    753 }
    754 
    755 function f32
    756 tukey_window(f32 t, f32 tapering)
    757 {
    758 	f32 r = tapering;
    759 	f32 result = 1;
    760 	if (t < r / 2)      result = 0.5f * (1 + cos_f32(2 * PI * (t - r / 2)     / r));
    761 	if (t >= 1 - r / 2) result = 0.5f * (1 + cos_f32(2 * PI * (t - 1 + r / 2) / r));
    762 	return result;
    763 }
    764 
    765 /* NOTE(rnp): adapted from "Discrete Time Signal Processing" (Oppenheim) */
    766 function f32 *
    767 kaiser_low_pass_filter(Arena *arena, f32 cutoff_frequency, f32 sampling_frequency, f32 beta, i32 length)
    768 {
    769 	f32 *result = push_array(arena, f32, length);
    770 	f32 wc      = 2 * PI * cutoff_frequency / sampling_frequency;
    771 	f32 a       = (f32)length / 2.0f;
    772 	f32 pi_i0_b = PI * (f32)cephes_i0(beta);
    773 
    774 	for (i32 n = 0; n < length; n++) {
    775 		f32 t       = (f32)n - a;
    776 		f32 impulse = !f32_equal(t, 0) ? sin_f32(wc * t) / t : wc;
    777 		t           = t / a;
    778 		f32 window  = (f32)cephes_i0(beta * sqrt_f32(1 - t * t)) / pi_i0_b;
    779 		result[n]   = impulse * window;
    780 	}
    781 
    782 	return result;
    783 }
    784 
    785 function f32 *
    786 rf_chirp(Arena *arena, f32 min_frequency, f32 max_frequency, f32 sampling_frequency,
    787          i32 length, b32 reverse)
    788 {
    789 	f32 *result = push_array(arena, f32, length);
    790 	for (i32 i = 0; i < length; i++) {
    791 		i32 index = reverse? length - 1 - i : i;
    792 		f32 fc    = min_frequency + (f32)i * (max_frequency - min_frequency) / (2 * (f32)length);
    793 		f32 arg   = 2 * PI * fc * (f32)i / sampling_frequency;
    794 		result[index] = sin_f32(arg) * tukey_window((f32)i / (f32)length, 0.2f);
    795 	}
    796 	return result;
    797 }
    798 
    799 function v2 *
    800 baseband_chirp(Arena *arena, f32 min_frequency, f32 max_frequency, f32 sampling_frequency,
    801                i32 length, b32 reverse, f32 scale)
    802 {
    803 	v2 *result    = push_array(arena, v2, length);
    804 	f32 conjugate = reverse ? -1 : 1;
    805 	for (i32 i = 0; i < length; i++) {
    806 		i32 index = reverse? length - 1 - i : i;
    807 		f32 fc    = min_frequency + (f32)i * (max_frequency - min_frequency) / (2 * (f32)length);
    808 		f32 arg   = 2 * PI * fc * (f32)i / sampling_frequency;
    809 		v2 sample = {{scale * cos_f32(arg), conjugate * scale * sin_f32(arg)}};
    810 		result[index] = v2_scale(sample, tukey_window((f32)i / (f32)length, 0.2f));
    811 	}
    812 	return result;
    813 }
    814 
    815 function m4
    816 das_transform_1d(v3 p1, v3 p2)
    817 {
    818 	v3 extent = v3_sub(p2, p1);
    819 	m4 result = {
    820 		.c[0] = (v4){{extent.x, extent.y, extent.z, 0.0f}},
    821 		.c[1] = (v4){{0.0f, 0.0f, 0.0f, 0.0f}},
    822 		.c[2] = (v4){{0.0f, 0.0f, 0.0f, 0.0f}},
    823 		.c[3] = (v4){{p1.x, p1.y, p1.z, 1.0f}},
    824 	};
    825 	return result;
    826 }
    827 
    828 function m4
    829 das_transform_2d_with_normal(v3 normal, v2 min_coordinate, v2 max_coordinate, f32 offset)
    830 {
    831 	v3 U = {{0, 1.0f, 0}};
    832 	if (f32_equal(v3_dot(U, normal), 1.0f))
    833 		U = (v3){{1.0f, 0, 0}};
    834 
    835 	v3 N = normal;
    836 	v3 V = cross(U, N);
    837 
    838 	v3 min = v3_add(v3_scale(U, min_coordinate.x), v3_scale(V, min_coordinate.y));
    839 	v3 max = v3_add(v3_scale(U, max_coordinate.x), v3_scale(V, max_coordinate.y));
    840 
    841 	v3 extent = v3_sub(max, min);
    842 	U = v3_scale(U, v3_dot(U, extent));
    843 	V = v3_scale(V, v3_dot(V, extent));
    844 
    845 	v3 t = v3_add(v3_scale(N, offset), min);
    846 
    847 	m4 result;
    848 	result.c[0] = (v4){{U.x,  U.y,  U.z,  0.0f}};
    849 	result.c[1] = (v4){{V.x,  V.y,  V.z,  0.0f}};
    850 	result.c[2] = (v4){{N.x,  N.y,  N.z,  0.0f}};
    851 	result.c[3] = (v4){{t.x,  t.y,  t.z,  1.0f}};
    852 
    853 	return result;
    854 }
    855 
    856 function m4
    857 das_transform_2d_xz(v2 min_coordinate, v2 max_coordinate, f32 y_off)
    858 {
    859 	m4 result = das_transform_2d_with_normal((v3){.y = 1.0f}, min_coordinate, max_coordinate, y_off);
    860 	return result;
    861 }
    862 
    863 function m4
    864 das_transform_2d_yz(v2 min_coordinate, v2 max_coordinate, f32 x_off)
    865 {
    866 	// NOTE(rnp): flip so that region extends in correct direction
    867 	m4 result = das_transform_2d_with_normal((v3){.x = -1.0f}, min_coordinate, max_coordinate, x_off);
    868 	return result;
    869 }
    870 
    871 function m4
    872 das_transform_2d_xy(v2 min_coordinate, v2 max_coordinate, f32 z_off)
    873 {
    874 	m4 result = das_transform_2d_with_normal((v3){.z = 1.0f}, min_coordinate, max_coordinate, z_off);
    875 	return result;
    876 }
    877 
    878 function m4
    879 das_transform_3d(v3 min_coordinate, v3 max_coordinate)
    880 {
    881 	v3 extent = v3_sub(max_coordinate, min_coordinate);
    882 	m4 result;
    883 	result.c[0] = (v4){{extent.x,         0.0f,             0.0f,             0.0f}};
    884 	result.c[1] = (v4){{0.0f,             extent.y,         0.0f,             0.0f}};
    885 	result.c[2] = (v4){{0.0f,             0.0f,             extent.z,         0.0f}};
    886 	result.c[3] = (v4){{min_coordinate.x, min_coordinate.y, min_coordinate.z, 1.0f}};
    887 	return result;
    888 }
    889 
    890 function v3
    891 plane_normal_from_transform(m4 transform)
    892 {
    893 	v3 U = v3_normalize(transform.c[0].xyz);
    894 	v3 V = v3_normalize(transform.c[1].xyz);
    895 	v3 result  = cross(V, U);
    896 	return result;
    897 }
    898 
    899 function f32
    900 plane_offset_from_transform(m4 transform)
    901 {
    902 	f32 result = v3_dot(plane_normal_from_transform(transform), transform.c[3].xyz);
    903 	return result;
    904 }
    905 
    906 function void
    907 plane_corners_from_transform(m4 transform, v2 *min, v2 *max)
    908 {
    909 	v3 U = v3_normalize(transform.c[0].xyz);
    910 	v3 V = v3_normalize(transform.c[1].xyz);
    911 
    912 	v3 min_3d = m4_mul_v3(transform, (v3){{0.f, 0.f, 0.f}});
    913 	v3 max_3d = m4_mul_v3(transform, (v3){{1.f, 1.f, 1.f}});
    914 
    915 	if (min) *min = (v2){{v3_dot(U, min_3d), v3_dot(V, min_3d)}};
    916 	if (max) *max = (v2){{v3_dot(U, max_3d), v3_dot(V, max_3d)}};
    917 }
    918 
    919 function v2
    920 plane_uv(v3 point, v3 U, v3 V)
    921 {
    922 	v2 result;
    923 	result.x = v3_dot(U, point) / v3_dot(U, U);
    924 	result.y = v3_dot(V, point) / v3_dot(V, V);
    925 	return result;
    926 }
    927 
    928 function v4
    929 hsv_to_rgb(v4 hsv)
    930 {
    931 	/* f(k(n))   = V - V*S*max(0, min(k, min(4 - k, 1)))
    932 	 * k(n)      = fmod((n + H * 6), 6)
    933 	 * (R, G, B) = (f(n = 5), f(n = 3), f(n = 1))
    934 	 */
    935 	alignas(16) f32 nval[4] = {5.0f, 3.0f, 1.0f, 0.0f};
    936 	f32x4 n   = load_f32x4(nval);
    937 	f32x4 H   = dup_f32x4(hsv.x);
    938 	f32x4 S   = dup_f32x4(hsv.y);
    939 	f32x4 V   = dup_f32x4(hsv.z);
    940 	f32x4 six = dup_f32x4(6);
    941 
    942 	f32x4 t   = add_f32x4(n, mul_f32x4(six, H));
    943 	f32x4 rem = floor_f32x4(div_f32x4(t, six));
    944 	f32x4 k   = sub_f32x4(t, mul_f32x4(rem, six));
    945 
    946 	t = min_f32x4(sub_f32x4(dup_f32x4(4), k), dup_f32x4(1));
    947 	t = max_f32x4(dup_f32x4(0), min_f32x4(k, t));
    948 	t = mul_f32x4(t, mul_f32x4(S, V));
    949 
    950 	v4 rgba;
    951 	store_f32x4(rgba.E, sub_f32x4(V, t));
    952 	rgba.a = hsv.a;
    953 	return rgba;
    954 }
    955 
    956 function f32
    957 ease_in_out_cubic(f32 t)
    958 {
    959 	f32 result;
    960 	if (t < 0.5f) {
    961 		result = 4.0f * t * t * t;
    962 	} else {
    963 		t      = -2.0f * t + 2.0f;
    964 		result =  1.0f - t * t * t / 2.0f;
    965 	}
    966 	return result;
    967 }
    968 
    969 function f32
    970 ease_in_out_quartic(f32 t)
    971 {
    972 	f32 result;
    973 	if (t < 0.5f) {
    974 		result = 8.0f * t * t * t * t;
    975 	} else {
    976 		t      = -2.0f * t + 2.0f;
    977 		result =  1.0f - t * t * t * t / 2.0f;
    978 	}
    979 	return result;
    980 }