If you define the coordinates and directions clockwise, starting on the left side,
#define LEFT 0 #define TOP 1 #define RIGHT 2 #define BOTTOM 3
you can use
void edge_line(int line[4], const int rect[4], const int edge) { line[0] = rect[ edge & 2 ]; line[1] = rect[ ((edge + 3) & 2) + 1 ]; line[2] = rect[ ((edge + 1) & 2) ]; line[3] = rect[ (edge & 2) + 1 ]; }
to copy the coordinates of the edge line (each line segment is clockwise). It looks suboptimal, but using -O2 , GCC-4.8, you get substantially
edge_line: pushl %esi pushl %ebx movl 20(%esp), %ecx movl 16(%esp), %edx movl 12(%esp), %eax movl %ecx, %esi andl $2, %esi movl (%edx,%esi,4), %ebx movl %ebx, (%eax) leal 3(%ecx), %ebx addl $1, %ecx andl $2, %ebx andl $2, %ecx addl $1, %ebx movl (%edx,%ebx,4), %ebx movl %ebx, 4(%eax) movl (%edx,%ecx,4), %ecx movl %ecx, 8(%eax) movl 4(%edx,%esi,4), %edx movl %edx, 12(%eax) popl %ebx popl %esi ret
but on 64-bit even better
edge_line: movl %edx, %ecx andl $2, %ecx movslq %ecx, %rcx movl (%rsi,%rcx,4), %eax movl %eax, (%rdi) leal 3(%rdx), %eax addl $1, %edx andl $2, %edx andl $2, %eax movslq %edx, %rdx cltq movl 4(%rsi,%rax,4), %eax movl %eax, 4(%rdi) movl (%rsi,%rdx,4), %eax movl %eax, 8(%rdi) movl 4(%rsi,%rcx,4), %eax movl %eax, 12(%rdi) ret
As you can see, there are no conditional expressions, and binary operators combine and optimize to very few instructions.
Edited to add:
If we define the getIndex(i, edge) function using three binary getIndex(i, edge) , one shift bit (right by 1), three additions and one subtraction,
int getIndex(const int i, const int edge) { return (i & 1) + ((edge + 4 - (i & 1) + (i >> 1)) & 2); }
with which edge_line() can be implemented as
void edge_line(int line[4], const int rect[4], const int edge) { line[0] = rect[ getIndex(0, edge) ]; line[1] = rect[ getIndex(1, edge) ]; line[2] = rect[ getIndex(2, edge) ]; line[3] = rect[ getIndex(3, edge) ]; }
we get the same results as before. Using GCC-4.8.4 and -O2 for AMD64 / x86-64 compiles in
getIndex: movl %edi, %edx sarl %edi andl $1, %edx subl %edx, %esi leal 4(%rsi,%rdi), %eax andl $2, %eax addl %edx, %eax ret
and
getIndex: movl 4(%esp), %eax movl 8(%esp), %edx movl %eax, %ecx andl $1, %ecx subl %ecx, %edx sarl %eax leal 4(%edx,%eax), %eax andl $2, %eax addl %ecx, %eax ret
on i686. Note that I came to the above form using a four by four result table; there are other, more rigorous ways to create it, and there may be even a more optimal form. Because of this, I seriously recommend adding a great huge comment above the function, explaining the intention, and it is preferable to also show a table of results. Sort of
/* This function returns an array index: * 0 for left * 1 for top * 2 for right * 3 for bottom * given edge: * 0 for left * 1 for top * 2 for right * 3 for bottom * and i: * 0 for initial x * 1 for initial y * 2 for final x * 3 for final y * * The result table is * | edge * | 0 1 2 3 * ----+------- * i=0 | 0 0 2 2 * i=1 | 3 1 1 3 * i=2 | 0 2 2 0 * i=3 | 1 1 3 3 * * Apologies for the write-only code. */
Or something similar.