


1. Language

	The main language is C17 with commonly used GNU extensions (gnu17)
	as supported by the GCC and clang compilers.


1.1. Standard types:

	The complex types are the standard types introduced with ISO C99.

	#include <complex.h>

	complex float
	complex double

	Similarly, we use the standard boolean type.

	#include <stdbool.h>

	bool x = true;

	In headers that have to be compatible with C++, we have to use _Complex.

1.2. Arrays

	Large multi-dimensional arrays should use our md_* functions.

	Small arrays should use (variable-length) arrays to increase
	type-safety. Pointer arithmetic should be avoided.

	float kernel[N][3];

	complex float* image = md_alloc(N, dims, CFL_SIZE);

	In headers, we use the __VLA(x) macro for compatibility with C++
	when this is necessary.


1.2. GNU Extensions:

	Some extensions a commonly supported by compilers and useful. In
	addition to those that are part of the ISO C23 standard, we
	use the following extensions.

	a. statement expressions: ({  })
	b. imaginary constants: 1.i
	c. conditional operator without middle operand: a?:b
	b. zero-length arrays: int a[0];
	d. nested functions using macro wrappers. { NESTED(int, f, ()) { }; }


1.3. Type safety

	void* and casts should be used only when necessary. Functions must
	have a prototype. Variable-length arrays are preferred over basic
	pointers. Macros can often be made type-safe, e.g. using
	the TYPE_CHECK macro.

	structs should be defined in the source (.c) file whenever possible
	to enforce modularization ("opaque pointers").


2. Coding Style

	Coding style are meant as guidelines. It is OK to deviate from the
	guidelines in situations, if it helps to make the code easier
	to understand.


2.1. Indentation

	Indentation using a single tab. A tab is considered 8 characters.

	For alignment spaces should be used.

	White space errors (white space after the last visible
        character of a line) should be avoided.

	Labels should be indented one tab less than the actual code.  This
	includes case labels!

	pragmas should start at the beginning of a line.
	The "omp" in OpenMP pragmas should be aligned to the affected statement
	by use of tabs after the initial "pragma".

	Lines should not exceed 80 to 100 characters.

2.2. Expressions

	There should be no space after the opening or before the closing
	bracket. There should be a single space before and after any
	operator except for prefix and postfix operators. Subexpressions
	should be enclosed in brackets and not rely on operator precedence
	for correct evaluation order.

	int i = (3 + x++) * 2;



	If there is a constant involved in a comparison the constant
	should be on the left side.

	if (0. == x)
		return false;



	The type of the controlling expression used in if statements or loops
	should be boolean. Pointers and integers should not implicitly
	compared to NULL or zero.

	if (NULL == foo_ptr)
		foo_ptr = init_foo_ptr();



	The conditional operator ?: should be used only where it does not
	obscure the logic.  In particular, multiple ?: should not be nested.


2.3. Statement Groups.

	Opening curly brace is on the next line for functions and on the
	same line for if, for, while, and switch statements. In the latter
	case there should be an empty line afterwards. In case only a
	single statement follows an if, for, or while statement, the
	statement block can be omitted - but for if-else pairs only if
	it can be omitted on both sides. There should be no empty line
	before the closing bracket.

	if (0. == x) {

		...
	}

	if (0. == x)
		y = 3;


	There should be empty lines before and after "} else {".

	if (0. == x) {

		foo();

	} else {

		bar();
	}


	When nesting loops and curly braces are used for the innermost
	loop, curly braces should also be used for all enclosing loops.


	for (int i = 0; i < 10; i++)
		for (int j = 0; j < 10; j++)
			a[i][j]++;

	for (int i = 0; i < 10; i++) {

		for (int j = 0; j < 10; j++) {

			a[i][j]++;
		}
	}


	Statements should be grouped in logical blocks by a single empty
	line. In particular, declarations, definitions (with initialization)
	should be separated from other statements. Memory allocation and
	deallocation should usually be separated.
	Multiple declarators in a single declaration should be avoided.


	void compute_bar(complex float* bar)
	{
		complex float* foo = md_alloc();

		compute_foo(foo);
		compute_bar_from_foo(bar, foo);

		md_free(foo);
	}


2.4. Comments:

	Comments should be used to document the API, explain complicated
	code and algorithms, and give required background information.
	For single line comments use // for blocks /* */.

	Comments are not meant to explain things a competent programmer
	should know by reading the code.


	Good:

	// gram-schmidt algorithm
	for (...) {

		for (..) {

			...
		}
	}


	Bad:

	// initialize foo
	int foo = 3;


	// config struct
	struct foo_conf_s conf = ...


	// allocate memory
	complex float* x = md_alloc(...);



2.5. Variable and Function Names

	Functions and variables names should be lower case and
	use '_' has separator of components. Names should be
	meaningful but not unnecessary long. If possible, use
	self-explanatory variable names. Except for loop
	indices, where one-letter variables names can be used.



	float norm = calc_frobenius_norm(image)


	This is preferable to adding comments:

	// calculate frobenous norm
	float n = clc_frbn(i);


	On the other hand, for often used functions a short
	name is appropriate. For example, we use

	md_fmac()

	instead of

	multidim_fused_multiply_accumulate()


	Locally used loop indices can be single character
	letters: i, j, k


2.6. Includes

	System headers should be included first, followed by headers from other modules,
	headers from the same module and finally the header belonging to the current file.

	Include guards should use the following style

	  _NAME_H

	where NAME is the basename of the header file.
