Last time in this series we built a Natural object that represented a number as a linked list of bits, with the least significant bit in the head and the most significant bits in the tail. We use the “null object pattern” where a Zero tail represents that the remaining bits in the sequence are all zero, and the sequence of bits never ends in a zero bit followed by Zero.[1. If that sounds different than what I wrote the last time, reread last week’s post. I slightly changed the organization of the structure based on a reader suggestion.]

So let’s then divide our implementation into two parts, one which enforces the public part of the contract that null is meaningless:

public static Natural operator +(Natural x, Natural y) { if (ReferenceEquals(x, null)) throw new ArgumentNullException("x"); else if (ReferenceEquals(y, null)) throw new ArgumentNullException("y"); else return Add(x, y); }

Note that now that we’re past the null checks we know that we will never dereference null so long as we don’t look at the tail of Zero .

And again, recall that I am always going to spell out when I am doing reference equality on Natural s, and I am going to mostly use a “by cases” approach to designing each method. This works well for recursive methods. Actually, now would be a good time for my standard refresher on how to write a recursive method:

Are we in a trivial case? Then solve the problem and return the solution.

Otherwise, split the problem into one or more strictly smaller problems.

Solve the subproblems recursively.

Combine their solutions to solve the larger problem.

So let’s think about addition of two sequences of bits with that in mind. What are the trivial cases? If either addend is Zero then the result can be the other addend. The non-trivial case is then that we are adding a number x with a tail and a least significant bit to a number y with a tail and a least significant bit. I’ll use the notation { tail : head } to compactly characterize a Natural object throughout this series.

x + y = { xtail : xhead } + { ytail : yhead } = ???

We know that numerically the number { xtail : xhead } has the numeric value of 2 * xtail + xhead . That is, if we have say { One : ZeroBit } , then the value of the tail is 1, twice that is 2, add the least significant bit — 0 — and the result is 2. So we can simplify as follows:

x + y = { xtail : xhead } + { ytail : yhead } = 2 * xtail + xhead + 2 * ytail + yhead = 2 * (xtail + ytail) + (xhead + yhead)

We have our recursive part; we can calculate xtail + ytail recursively, and then double it by making a new Natural with the sum as the tail and zero as the bit. But what about the other part?

There are three cases. If xhead and yhead are both zero then we are done; adding zero is a no-op that we can skip. If one of them is one and the other is zero then we can set the low bit of the sum to one instead of zero. But what if both of them are one? Now we’ve got to add two to the sum, and we don’t know how to do that. Perhaps there is another way we can break this down. Let’s divide that up into cases again.

Suppose xhead is 0. Then:

2 * xtail + xhead + 2 * ytail + yhead = 2 * (xtail + ytail) + (0 + yhead) = { xtail + ytail : yhead }

Suppose yhead is 0. Then:

2 * xtail + xhead + 2 * ytail + yhead = 2 * (xtail + ytail) + (xhead + 0) = { xtail + ytail : xhead }

Otherwise, both heads are 1:

2 * xtail + xhead + 2 * ytail + yhead = 2 * (xtail + ytail) + (1 + 1) = 2 * (xtail + ytail) + 2 = 2 * (xtail + ytail + 1) = { xtail + ytail + 1 : 0 }

Aha! We can turn that last case into two recursive additions.[1. The usual way to explain this is that the extra one is a “carry bit”, but I’ve always found that confusing and unnecessary. The need for the extra one follows directly from the algebra above; there’s no need to invoke the pencil-and-paper addition that we learned as children.] Now we can write the code very easily:

private static Natural Add(Natural x, Natural y) { if (ReferenceEquals(x, Zero)) return y; else if (ReferenceEquals(y, Zero)) return x; else if (x.head == ZeroBit) return Create(Add(x.tail, y.tail), y.head); else if (y.head == ZeroBit) return Create(Add(x.tail, y.tail), x.head); else return Create(Add(Add(x.tail, y.tail), One), ZeroBit); }

And finally, for debugging purposes it is nice to have an implementation of ToString . It’s recursive and inefficient because hey, like I said, this is not pragmatic code here.

public override string ToString() { if (ReferenceEquals(this, Zero)) return "0"; else return tail.ToString() + head.ToString(); }

And now we can actually write some code:

var n0 = Natural.Zero; var n1 = Natural.One; var n2 = n1 + n1; var n3 = n2 + n1; var n4 = n2 + n2; var n5 = n2 + n3; var n6 = n3 + n3; Console.WriteLine(n0); Console.WriteLine(n1); Console.WriteLine(n2); Console.WriteLine(n3); Console.WriteLine(n4); Console.WriteLine(n5); Console.WriteLine(n6);

and get the expected output:

0 01 010 011 0100 0101 0110

OK, addition is done. Next time on FAIC: We’ll talk a bit about the increment operator in C# vs C and C++.