C++11 - nearly working :)
=

After reading [this article](http://www.yumpu.com/en/document/view/16913964/avoiding-yourself-bit-player), I collected bits of wisdom from that guy who apparently worked for 25 years on the less complicated problem of counting self-avoiding paths on a square lattice.

<!-- language: lang-cpp -->

    #include <cassert>
    #include <ctime>
    #include <sstream>
    #include <vector>
    #include <algorithm> // sort
    
    using namespace std;
    
    // theroretical max snake lenght (the code would need a few decades to process that value)
    #define MAX_LENGTH ((int)(1+8*sizeof(unsigned)))
    
    #ifndef _MSC_VER
    #ifndef QT_DEBUG // using Qt IDE for g++ builds
    #define NDEBUG
    #endif
    #endif
    
    #ifdef NDEBUG
    inline void tprintf(const char *, ...){}
    #else
    #define tprintf printf
    #endif
    
    void panic(const char * msg)
    {
    	printf("PANIC: %s\n", msg);
    	exit(-1);
    }
    
    // ============================================================================
    // fast bit reversal
    // ============================================================================
    unsigned bit_reverse(register unsigned x, unsigned len)
    {
    	x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
    	x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
    	x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
    	x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
    	return((x >> 16) | (x << 16)) >> (32-len);
    }
    
    // ============================================================================
    // 2D geometry (restricted to integer coordinates and right angle rotations)
    // ============================================================================
    
    // points using integer- or float-valued coordinates
    template<typename T>struct tTypedPoint;
    
    typedef int    tCoord;
    typedef double tFloatCoord;
    
    typedef tTypedPoint<tCoord> tPoint;
    typedef tTypedPoint<tFloatCoord>  tFloatPoint;
    
    template <typename T>
    struct tTypedPoint {
    	T x, y;
    
    	template<typename U> tTypedPoint(const tTypedPoint<U>& from) : x((T)from.x), y((T)from.y) {} // conversion constructor
    
    	tTypedPoint() {}
    	tTypedPoint(T x, T y) : x(x), y(y) {}
        tTypedPoint(const tTypedPoint& p) { *this = p; }
    	tTypedPoint operator+ (const tTypedPoint & p) const { return{ x + p.x, y + p.y }; }
    	tTypedPoint operator- (const tTypedPoint & p) const { return{ x - p.x, y - p.y }; }
    	tTypedPoint operator* (T scalar) const { return{ x * scalar, y * scalar }; }
    	tTypedPoint operator/ (T scalar) const { return{ x / scalar, y / scalar }; }
    	bool operator== (const tTypedPoint & p) const { return x == p.x && y == p.y; }
    	bool operator!= (const tTypedPoint & p) const { return !operator==(p); }
    	T dot(const tTypedPoint &p) const { return x*p.x + y * p.y; } // dot product  
    	int cross(const tTypedPoint &p) const { return x*p.y - y * p.x; } // z component of cross product
    	T norm2(void) const { return dot(*this); }
    
    	// works only with direction = 1 (90° right) or -1 (90° left)
    	tTypedPoint rotate(int direction) const { return{ direction * y, -direction * x }; }
    	tTypedPoint rotate(int direction, const tTypedPoint & center) const { return (*this - center).rotate(direction) + center; }
    
        // used to compute length of a ragdoll snake segment
    	unsigned manhattan_distance(const tPoint & p) const { return abs(x-p.x) + abs(y-p.y); }
    };
    
    
    struct tArc {
    	tPoint c;                        // circle center
    	tFloatPoint middle_vector;       // vector splitting the arc in half
    	tCoord      middle_vector_norm2; // precomputed for speed
        tFloatCoord dp_limit;
    
    	tArc() {}
    	tArc(tPoint c, tPoint p, int direction) : c(c)
    	{
    		tPoint r = p - c;
    		tPoint end = r.rotate(direction);
            middle_vector = ((tFloatPoint)(r+end)) / sqrt(2); // works only for +-90° rotations. The vector should be normalized to circle radius in the general case
            middle_vector_norm2 = r.norm2();
            dp_limit = ((tFloatPoint)r).dot(middle_vector);
            assert (middle_vector == tPoint(0, 0) || dp_limit != 0);
    	}
    
    	bool contains(tFloatPoint p) // p must be a point on the circle
    	{
            if ((p-c).dot(middle_vector) >= dp_limit)
    		{
    			return true;
    		}
    		else return false;
    	}
    };
    
    // returns the point of line (p1 p2) that is closest to c
    // handles degenerate case p1 = p2
    tPoint line_closest_point(tPoint p1, tPoint p2, tPoint c)
    {
    	if (p1 == p2) return{ p1.x, p1.y };
    	tPoint p1p2 = p2 - p1;
    	tPoint p1c =  c  - p1;
    	tPoint disp = (p1p2 * p1c.dot(p1p2)) / p1p2.norm2();
    	return p1 + disp;
    }
    
    // variant of closest point computation that checks if the projection falls within the segment
    bool closest_point_within(tPoint p1, tPoint p2, tPoint c, tPoint & res)
    {
    	tPoint p1p2 = p2 - p1;
    	tPoint p1c = c - p1;
    	tCoord nk = p1c.dot(p1p2);
    	if (nk <= 0) return false;
    	tCoord n = p1p2.norm2();
    	if (nk >= n) return false;
    	res = p1 + p1p2 * (nk / n);
    	return true;
    }
    
    // tests intersection of line (p1 p2) with an arc
    bool inter_seg_arc(tPoint p1, tPoint p2, tArc arc)
    {
    	tPoint m = line_closest_point(p1, p2, arc.c);
    	tCoord r2 = arc.middle_vector_norm2;
    	tPoint cm = m - arc.c;
    	tCoord h2 = cm.norm2();
    	if (r2 < h2) return false; // no circle intersection
    
    	tPoint p1p2 = p2 - p1;
    	tCoord n2p1p2 = p1p2.norm2();
    
    	// works because by construction p is on (p1 p2)
    	auto in_segment = [&](const tFloatPoint & p) -> bool
    	{
    		tFloatCoord nk = p1p2.dot(p - p1);
            return nk >= 0 && nk <= n2p1p2;
    	};
    
    	if (r2 == h2) return arc.contains(m) && in_segment(m); // tangent intersection
    
    	//if (p1 == p2) return false; // degenerate segment located inside circle
    	assert(p1 != p2);
    
    	tFloatPoint u = (tFloatPoint)p1p2 * sqrt((r2-h2)/n2p1p2); // displacement on (p1 p2) from m to one intersection point
    
    	tFloatPoint i1 = m + u;
    	if    (arc.contains(i1) && in_segment(i1)) return true;
    	tFloatPoint i2 = m - u;
    	return arc.contains(i2) && in_segment(i2);
    }
    
    // ============================================================================
    // compact storage of a configuration (64 bits)
    // ============================================================================
    struct sConfiguration {
    	unsigned partition;
    	unsigned folding;
    
    	explicit sConfiguration() {}
    	sConfiguration(unsigned partition, unsigned folding) : partition(partition), folding(folding) {}
    
    	// add a bend
    	sConfiguration bend(unsigned joint, int rotation) const
    	{
    		sConfiguration res;
    		unsigned joint_mask = 1 << joint;
    		res.partition = partition | joint_mask;
    		res.folding = folding;
    		if (rotation == -1) res.folding |= joint_mask;
    		return res;
    	}
    
    	// textual representation
    	string text(unsigned length) const
    	{
    		ostringstream res;
    
    		unsigned f = folding;
    		unsigned p = partition;
    
    		int segment_len = 1;
    		int direction = 1;
            for (size_t i = 1; i != length; i++)
    		{
    			if (p & 1)
    			{
    				res << segment_len * direction << ',';
    				direction = (f & 1) ? -1 : 1;
    				segment_len = 1;
    			}
    			else segment_len++;
    
    			p >>= 1;
    			f >>= 1;
    		}
    		res << segment_len * direction;
    		return res.str();
    	}
    
    	// for final sorting
    	bool operator< (const sConfiguration& c) const
    	{
    		return (partition == c.partition) ? folding < c.folding : partition < c.partition;
    	}
    };
    
    // ============================================================================
    // static snake geometry checking grid
    // ============================================================================
    typedef unsigned tConfId;
    
    class tGrid {
    	vector<tConfId>point;
    	tConfId current;
    	size_t snake_len;
    	int min_x, max_x, min_y, max_y;
    	size_t x_size, y_size;
    
    	size_t raw_index(const tPoint& p) { bound_check(p);  return (p.x - min_x) + (p.y - min_y) * x_size; }
    	void bound_check(const tPoint& p) const { assert(p.x >= min_x && p.x <= max_x && p.y >= min_y && p.y <= max_y); }
    
    	void set(const tPoint& p)
    	{
    		point[raw_index(p)] = current;
    	}
    	bool check(const tPoint& p)
    	{
    		if (point[raw_index(p)] == current) return false;
    		set(p);
    		return true;
    	}
    
    public:
    	tGrid(int len) : current(-1), snake_len(len)
    	{
    		min_x = -max(len - 3, 0);
    		max_x = max(len - 0, 0);
    		min_y = -max(len - 1, 0);
    		max_y = max(len - 4, 0);
    		x_size = max_x - min_x + 1;
    		y_size = max_y - min_y + 1;
    		point.assign(x_size * y_size, current);
    	}
    
    	bool check(sConfiguration c)
    	{
    		current++;
    		tPoint d(1, 0);
    		tPoint p(0, 0);
    		set(p);
    		for (size_t i = 1; i != snake_len; i++)
    		{
    			p = p + d;
    			if (!check(p)) return false;
    			if (c.partition & 1) d = d.rotate((c.folding & 1) ? -1 : 1);
    			c.folding >>= 1;
    			c.partition >>= 1;
    		}
    		return check(p + d);
    	}
    
    };
    
    // ============================================================================
    // snake ragdoll 
    // ============================================================================
    class tSnakeDoll {
    	vector<tPoint>point; // snake geometry. Head at (0,0) pointing right
    
    	// allows to check for collision with the area swept by a rotating segment
    	struct rotatedSegment {
    		struct segment { tPoint a, b; };
    		tPoint  org;
    		segment end;
    		tArc    arc[3];
    		bool extra_arc; // see if third arc is needed
    
    		// empty constructor to avoid wasting time in vector initializations
    		rotatedSegment(){}
    		// copy constructor is mandatory for vectors *but* shall never be used, since we carefully pre-allocate vector memory
            rotatedSegment(const rotatedSegment &){ assert(!"rotatedSegment should never have been copy-constructed"); }
    
    		// rotate a segment
    		rotatedSegment(tPoint pivot, int rotation, tPoint o1, tPoint o2)
    		{
    			arc[0] = tArc(pivot, o1, rotation);
    			arc[1] = tArc(pivot, o2, rotation);
    			tPoint middle;
    			extra_arc = closest_point_within(o1, o2, pivot, middle);
    			if (extra_arc) arc[2] = tArc(pivot, middle, rotation);
    			org = o1;
    			end = { o1.rotate(rotation, pivot), o2.rotate(rotation, pivot) };
    		}
    
    		// check if a segment intersects the area swept during rotation
    		bool intersects(tPoint p1, tPoint p2) const
    		{
    			auto print_arc = [&](int a) { tprintf("(%d,%d)(%d,%d) -> %d (%d,%d)[%f,%f]", p1.x, p1.y, p2.x, p2.y, a, arc[a].c.x, arc[a].c.y, arc[a].middle_vector.x, arc[a].middle_vector.y); };
    
    			if (p1 == org) return false; // pivot is the only point allowed to intersect
    			if (inter_seg_arc(p1, p2, arc[0])) 
    			{ 
    				print_arc(0);  
    				return true;
    			}
    			if (inter_seg_arc(p1, p2, arc[1]))
    			{ 
    				print_arc(1); 
    				return true;
    			}
    			if (extra_arc && inter_seg_arc(p1, p2, arc[2])) 
    			{ 
    				print_arc(2);
    				return true;
    			}
    			return false;
    		}
    	};
    
    public:
    	sConfiguration configuration;
    	bool valid;
    
    	// holds results of a folding attempt
    	class snakeFolding {
    		friend class tSnakeDoll;
    		vector<rotatedSegment>segment; // rotated segments
    		unsigned joint;
    		int direction;
    		size_t i_rotate;
    
            // pre-allocate rotated segments
            void reserve(size_t length)
            {
                segment.clear(); // this supposedly does not release vector storage memory
                segment.reserve(length);
            }
    
    		// handle one segment rotation
    		void rotate(tPoint pivot, int rotation, tPoint o1, tPoint o2)
    		{
    			segment.emplace_back(pivot, rotation, o1, o2);
    		}
    	public:
    		// nothing done during construction
            snakeFolding(unsigned size)
            {
                segment.reserve (size);
            }
    	};
    
    	// empty default constructor to avoid wasting time in array/vector inits
    	tSnakeDoll() {}
    
    	// constructs ragdoll from compressed configuration
    	tSnakeDoll(unsigned size, unsigned generator, unsigned folding) : point(size), configuration(generator,folding)
    	{
    		tPoint direction(1, 0);
    		tPoint current = { 0, 0 };
    		size_t p = 0;
    		point[p++] = current;
            for (size_t i = 1; i != size; i++)
    		{
    			current = current + direction;
    			if (generator & 1)
    			{
    				direction.rotate((folding & 1) ? -1 : 1);
    				point[p++] = current;
    			}
    			folding >>= 1;
    			generator >>= 1;
    		}
    		point[p++] = current;
    		point.resize(p);
    	}
    
    	// constructs the initial flat snake
        tSnakeDoll(int size) : point(2), configuration(0,0), valid(true)
    	{
    		point[0] = { 0, 0 };
    		point[1] = { size, 0 };
    	}
    
    	// constructs a new folding with one added rotation
    	tSnakeDoll(const tSnakeDoll & parent, unsigned joint, int rotation, tGrid& grid)
    	{
            // update configuration
    		configuration = parent.configuration.bend(joint, rotation);
    
    		// locate folding point
    		unsigned p_joint = joint+1;
    		tPoint pivot;
    		size_t i_rotate = 0;
    		for (size_t i = 1; i != parent.point.size(); i++)
    		{
    			unsigned len = parent.point[i].manhattan_distance(parent.point[i - 1]);
    			if (len > p_joint)
    			{
    				pivot = parent.point[i - 1] + ((parent.point[i] - parent.point[i - 1]) / len) * p_joint;
    				i_rotate = i;
    				break;
    			}
    			else p_joint -= len;
    		}
    
    		// rotate around joint
            snakeFolding fold (parent.point.size() - i_rotate);
    		fold.rotate(pivot, rotation, pivot, parent.point[i_rotate]);
    		for (size_t i = i_rotate + 1; i != parent.point.size(); i++) fold.rotate(pivot, rotation, parent.point[i - 1], parent.point[i]);
    
    		// copy unmoved points
            point.resize(parent.point.size()+1);
            size_t i;
    		for (i = 0; i != i_rotate; i++) point[i] = parent.point[i];
    
    		// copy rotated points
    		for (; i != parent.point.size(); i++) point[i] = fold.segment[i - i_rotate].end.a;
    		point[i] = fold.segment[i - 1 - i_rotate].end.b;
    
            // static configuration check
            valid = grid.check (configuration);
    
            // check collisions with swept arcs
            if (valid && parent.valid) // ;!; parent.valid test is temporary
            {
                for (const rotatedSegment & s : fold.segment)
                for (size_t i = 0; i != i_rotate; i++)
                {
                    if (s.intersects(point[i+1], point[i]))
                    {
                        //printf("! %s => %s\n", parent.trace().c_str(), trace().c_str());//;!;
                        valid = false;
                        break;
                    }
                }
            }
        }
    
    	// trace
    	string trace(void) const
    	{
    		size_t len = 0;
            for (size_t i = 1; i != point.size(); i++) len += point[i - 1].manhattan_distance(point[i]);
    		return configuration.text(len);
    	}
    };
    
    // ============================================================================
    // snake twisting engine
    // ============================================================================
    class cSnakeFolder {
    	int length;
    	unsigned num_joints;
        tGrid grid;
    
        // filter redundant configurations
        bool is_unique (sConfiguration c)
        {
            unsigned reverse_p = bit_reverse(c.partition, num_joints);
    		if (reverse_p < c.partition)
            {
    			tprintf("P cut %s\n", c.text(length).c_str());
                return false;
            }
    		else if (reverse_p == c.partition) // filter redundant foldings
            {
    			unsigned first_joint_mask = c.partition & (-c.partition); // insulates leftmost bit
                unsigned reverse_f = bit_reverse(c.folding, num_joints);
    			if (reverse_f & first_joint_mask) reverse_f = ~reverse_f & c.partition;
    
                if (reverse_f > c.folding)
                {
    				tprintf("F cut %s\n", c.text(length).c_str());
                    return false;
                }
            }
            return true;
        }
    
        // recursive folding
        void fold(tSnakeDoll snake, unsigned first_joint)
    	{
            // count unique configurations
            if (snake.valid && is_unique(snake.configuration)) num_configurations++;
    
    		// try to bend remaining joints
            for (size_t joint = first_joint; joint != num_joints; joint++)
    		{
    			// right bend
    			tprintf("%s -> %s\n", snake.configuration.text(length).c_str(), snake.configuration.bend(joint,1).text(length).c_str());
                fold(tSnakeDoll(snake, joint, 1, grid), joint + 1);
    
    			// left bend, except for the first joint
    			if (snake.configuration.partition != 0)
    			{
    				tprintf("%s -> %s\n", snake.configuration.text(length).c_str(), snake.configuration.bend(joint, -1).text(length).c_str());
                    fold(tSnakeDoll(snake, joint, -1, grid), joint + 1);
    			}
    		}
    	}
    
    public:
        // count of found configurations
        unsigned num_configurations;
    
        // constructor does all the work :)
        cSnakeFolder(int n) : length(n), grid(n), num_configurations(0)
    	{
    		num_joints = length - 1;
    
    		// launch recursive folding
            fold(tSnakeDoll(length), 0);
    	}
    };
    
    // ============================================================================
    // here we go
    // ============================================================================
    int main(int argc, char * argv[])
    {
    #ifdef NDEBUG
    	if (argc != 2) panic("give me a snake length or else");
    	int length = atoi(argv[1]);
    #else
        (void)argc; (void)argv;
    	int length = 12;
    #endif // NDEBUG
    
        if (length <= 0 || length >= MAX_LENGTH) panic("a snake of that length is hardly foldable");
    
    	time_t start = time(NULL);
    	cSnakeFolder snakes(length);
    	time_t duration = time(NULL) - start;
    
        printf ("Found %d configuration%c of length %d in %lds\n", snakes.num_configurations, (snakes.num_configurations == 1) ? '\0' : 's', length, duration);
    	return 0;
    }

Building the executable
-
Compile with **`g++ -O3 -std=c++11`**  
I use MinGW under Win7 with g++4.8 for "linux" builds, so portability is not 100% guaranteed.

It also works (sort of) with a standard MSVC2013 project

By undefining **`NDEBUG`**, you get traces of algorithm execution and a summary of found configurations.

Performances
-
with or without hash tables, Microsoft compiler performs miserably: g++ build is **3 times faster**.  

The algorithm uses practically no memory.

Since collision check is roughly in O(n), computation time should be in O(nk<sup>n</sup>), with k slightly lower than 3.  
On my [email protected], n=17 takes about 1:30 (about 2 million snakes/minute).

I am not done optimizing, but I would not expect more than a x3 gain, so basically I can hope to reach maybe n=20 under an hour, or n=24 under a day.

Reaching the first known unbendable shape (n=31) would take between a few years and a decade, assuming no power outages.

Counting shapes
-
A **N** size snake has **N-1** joints.  
Each joint can be left straight or bent to the left or right (3 possibilities).  
The number of possible foldings is thus 3<sup>N-1</sup>.  
Collisions will reduce that number somewhat, so the actual number is close to 2.7<sup>N-1</sup>

However, many such foldings lead to identical shapes.

two shapes are identical if there is either a **rotation** or a **symetry** that can transform one into the other.

Let's define a **segment** as any straight part of the folded body.  
For instance a size 5 snake folded at 2nd joint would have 2 segments (one 2 units long and the second 3 units long).  
The first segment will be named **head**, and the last **tail**.

By convention we orient the snakes's head horizontally with the body pointing right (like in the OP first figure). 
 
We designate a given figure with a list of signed segment lengths, with positive lengths indicating a right folding and negative ones a left folding.  
Initial lenght is positive by convention.

Separating segments and bends
-
If we consider only the different ways a snake of length N can be split into segments, we end up with a repartition identical to the [compositions](http://en.wikipedia.org/wiki/Composition_%28combinatorics%29) of N.  

Using the same algorithm as shown in the wiki page, it is easy to generate all the 2<sup>N-1</sup> possible ***partitions*** of the snake.

Each partition will in turn generate all possible foldings by applying either left or right bends to all of its joints. One such folding will be called a **configuration**.

All possible partitions can be represented by an integer of N-1 bits, where each bit represents the presence of a joint. We will call this integer a ***generator***.

Pruning partitions
-
By noticing that bending a given partition from the head down is equivalent to bending the symetrical partition from the tail up, we can find all couples of symetrical partitions and eliminate one out of two.  
The generator of a symetrical partition is the partition's generator written in reverse bit order, which is trivially easy and cheap to detect.  

This will eliminate almost half of the possible partitions, the exceptions being the partitions with "palindromic" generators that are left unchanged by bit reversal (for instance 00100100).

Taking care of horizontal symetries
-
With our conventions (a snake starts pointing to the right), the very first bend applied to the right will produce a family of foldings that will be horizontal symetrics from the ones that differ only by the first bend.

If we decide that the first bend will always be to the right, we eliminate all horizontal symetrics in one big swoop.

Mopping up the palindromes
-

These two cuts are efficient, but not enough to take care of these pesky palindromes.  
The most thorough check in the general case is as follows:

consider a configuration C with a palindromic partition.

- if we *invert* every bend in C, we end up with a horizontal symetric of C.
- if we *reverse* C (applying bends from the tail up), we get the same figure rotated right
- if we both reverse and invert C, we get the same figure rotated left.

We could check every new configuration against the 3 others. However, since we already generate only configurations starting with a right turn, we only have one possible symetry to check:

- the inverted C will start with a left turn, which is by construction impossible to duplicate
- out of the reversed and inverted-reversed configurations, only one will start with a right turn.  
That is the only one configuration we can possibly duplicate.

Eliminating duplicates without any storage
-
My initial approach was to store all configurations in a huge hash table, to eliminate duplicates by checking the presence of a previously computed symetric configuration.

Thanks to the aforementioned article, it became clear that, since partitions and foldings are stored as bitfields, they can be compared like any numerical value.  
So to eliminate one member of a symetric pair, you can simply compare both elements and systematically keep the smallest one (or the greatest one, as you like).  

Thus, testing a configuration for duplication amounts to computing the symetric partition, and if both are identical, the folding. No memory is needed at all.

Order of generation
-
Clearly collision check will be the most time-consuming part, so reducing these computations is a major time-saver.
  
A possible solution is to have a "ragdoll snake" that will start in a flat configuration and be gradually bent, to avoid recomputing the whole snake geometry for each possible configuration.  

By choosing the order in which configurations are tested, so that at most a ragdoll is stored for each total number of joints, we can limit the number of instances to N-1.  

I use a recursive scan of the sake from the tail down, adding a single joint at each level. Thus a new ragdoll instance is built on top of the parent configuration, with a single aditional bend.

This means bends are applied in a sequential order, which seems to be enough to avoid self-collisions in almost all cases.

When self-collision is detected, the bends that lead to the offending move are applied in all possible orders until legit folding is found or all combinations are exhausted.

Static check
-
Before even thinking about moving parts, I found it more efficient to test the static final shape of a snake for self-intersections.

This is done by drawing the snake on a grid. Each possible point is plotted from the head down. If there is a self-intersection, at least a pair of points will fall on the same location.
This requires exactly N plots for any snake configuration, for a constant O(N) time.

The main advantage of this approach is that the static test alone will simply select valid self-avoiding paths on a square lattice, which allows to test the whole algorithm by inhibiting dynamic collision detection and making sure we find the correct count of such paths.

Dynamic check
-
When a snake folds around one joint, each rotated segment will sweep an area whose shape is anything but trivial.  
Clearly you can check collisions by testing inclusion within all such swept areas individually. A global check would be more efficient, but given the areas complexity I can't think of any (except maybe using a GPU to draw all areas and perform a global hit check). 

Since the static test takes care of the starting and ending positions of each segment, we just need to check intersections with the ***arcs*** swept by each rotating segments.

After an interesting discussion with trichoplax and [a bit of JavaScript](http://petiteleve.free.fr/SO/sweep.html) to get my bearings, I came up with this method:

To try to put it in a few words, if you call 

- **C** the center of rotation, 
- **S** a rotating segment of arbitrary lenght and direction that does not contain **C**,
- **L** the line prolongating **S**
- **H** the line orthogonal to **L** passing through **C**,
- **I** the intersection of **L** and **H**,

[![maths][4]][4]  
<sub>(source: [free.fr](http://petiteleve.free.fr/SO/sweep.png))</sub>  

For any segment that does not contain **I**, the swept area is bound by 2 arcs (and 2 segments already taken care of by the static check).
  
If **I** falls within the segment, the arc swept by I must also be taken into account.

This means we can check each unmoving segment against each rotating segment with 2 or 3 segment-with-arc intersections

I used vector geometry to avoid trigonometric functions altogether.  
Vector operations produce compact and (relatively) readable code.  

Segment-to-arc intersection requires a floating point vector, but the logic should be immune to rounding errors.  
I found this elegant and efficient solution in an obscure forum post. I wonder why it is not more widely publicized.

Does it work?
-
Inhibiting dynamic collision detection produces the correct self-avoiding paths count up to n=19, so I'm pretty confident the global layout works.

Dynamic collision detection produces consistent results, though the check of bends in different order is missing (for now).  
As a consequence, the program counts snakes that can be bent from the head down (i.e. with joints folded in order of increasing distance from the head).

 [4]: https://i.sstatic.net/MiWC4.png