<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Developing for Developers : Algorithms</title><link>http://blogs.msdn.com/devdev/archive/tags/Algorithms/default.aspx</link><description>Tags: Algorithms</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Secret sharing</title><link>http://blogs.msdn.com/devdev/archive/2005/10/02/476273.aspx</link><pubDate>Mon, 03 Oct 2005 01:31:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:476273</guid><dc:creator>dcoetzee</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/devdev/comments/476273.aspx</comments><wfw:commentRss>http://blogs.msdn.com/devdev/commentrss.aspx?PostID=476273</wfw:commentRss><description>One of the most difficult problems in cryptographic key management is
keeping a secret key safe from both compromise and loss. If you don't make
enough backups, the key might be destroyed in a hardware failure or
natural disaster. But if any backup is compromised, the key is
compromised.&lt;br&gt;
&lt;br&gt;Rather than invent new tools, one might try to solve the problem
with encryption. Just encrypting the key normally is not helpful, since
then you have the
same problem with the key needed to decrypt it. Instead, if you encrypt
your key using multiple keys, then
store these keys in different locations, you need all the keys to
decrypt it. This protects against compromise, but if any key is lost,
so is the original key. To fix this,
you'd have to encrypt the original key many times, each time using just
some of
the keys. For example, suppose you had 5 keys and you wanted to be able
to decrypt using any 3 of them. Then you'd need to perform all the
following encryptions:&lt;br&gt;
&lt;ul&gt;
  &lt;li&gt;E&lt;sub&gt;1&lt;/sub&gt;(E&lt;sub&gt;2&lt;/sub&gt;(E&lt;sub&gt;3&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;1&lt;/sub&gt;(E&lt;sub&gt;2&lt;/sub&gt;(E&lt;sub&gt;4&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;1&lt;/sub&gt;(E&lt;sub&gt;2&lt;/sub&gt;(E&lt;sub&gt;5&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;1&lt;/sub&gt;(E&lt;sub&gt;3&lt;/sub&gt;(E&lt;sub&gt;4&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;1&lt;/sub&gt;(E&lt;sub&gt;3&lt;/sub&gt;(E&lt;sub&gt;5&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;1&lt;/sub&gt;(E&lt;sub&gt;4&lt;/sub&gt;(E&lt;sub&gt;5&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;2&lt;/sub&gt;(E&lt;sub&gt;3&lt;/sub&gt;(E&lt;sub&gt;4&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;2&lt;/sub&gt;(E&lt;sub&gt;3&lt;/sub&gt;(E&lt;sub&gt;5&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;2&lt;/sub&gt;(E&lt;sub&gt;4&lt;/sub&gt;(E&lt;sub&gt;5&lt;/sub&gt;(K)))&lt;/li&gt;
  &lt;li&gt;E&lt;sub&gt;3&lt;/sub&gt;(E&lt;sub&gt;4&lt;/sub&gt;(E&lt;sub&gt;5&lt;/sub&gt;(K)))&lt;/li&gt;
&lt;/ul&gt;
The number of encryptions needed grows very quickly. It also takes
quite a bit of time and space to store all these multiply-encrypted
keys. Let's look at some other solutions.&lt;br&gt;

&lt;br&gt;

One solution is to simply break the key file into two parts,
make a copy of each, and store the four parts in four locations. Then,
even if any one is compromised, the key is not revealed. Unfortunately,
this scheme is not that helpful, because just knowing half of someone's
private key is enough to drastically decrease the time needed for a
brute-force search to crack the remainder of the key.&lt;br&gt;
&lt;br&gt;
Here's another simple solution: create a one-time pad (a file of random
data) the same size as the private key, then XOR them together. Put the
result in one place and the pad in another, and destroy the original
key. We call each file a "share" or "shadow". Later, you can XOR the two shares
together to retrieve the original key. Since the bytes of each file are
random, you can be assured that neither piece will reveal any
information about the key by itself. We can extend this scheme to &lt;i&gt;n&lt;/i&gt; pieces by just creating &lt;i&gt;n&lt;/i&gt; - 1 one-time pads, adding them together, and subtracting this from the key data to obtain the &lt;i&gt;n&lt;/i&gt;th share. Unfortunately, if even one piece is lost, so is the key. This is called an (&lt;i&gt;n,n&lt;/i&gt;) secret sharing scheme, because we have &lt;i&gt;n&lt;/i&gt; shares and need &lt;i&gt;n&lt;/i&gt; of them to reconstruct the secret.&lt;br&gt;
&lt;br&gt;
Here's another more useful scheme: let the private key be represented as the number &lt;i&gt;S&lt;/i&gt;. Choose a random line in the plane passing through the point (0,&lt;i&gt; S&lt;/i&gt;). Next, choose &lt;i&gt;n&lt;/i&gt; random points on that line as your &lt;i&gt;n&lt;/i&gt; shares. Now, if we know any two of the shares, we know the line, and so can find &lt;i&gt;S&lt;/i&gt;. If we only know one share, then we just know one point, and for every &lt;i&gt;T&lt;/i&gt; there is a line passing through (0, &lt;i&gt;T&lt;/i&gt;) and that point; hence, the secret could be anything at all. This is called an (&lt;i&gt;n,2&lt;/i&gt;) secret sharing scheme, because we have &lt;i&gt;n&lt;/i&gt; shares and need 2 of them to reconstruct the secret.&lt;br&gt;
&lt;br&gt;
Particularly useful for individuals is the (3,2) scheme - you can keep
one share on your person on a floppy or USB pen drive at all times, one
share on your hard drive, and one share backed up at a secure remote
location. When you need to use the key, you recreate it briefly using
the shares on your hard drive and your person, then destroy it. If you
lose the floppy or your hard disk crashes, you can recreate the key
from the remaining shares (and make yourself 3 new shares). If any one
share is compromised, the key is not compromised. If you know of a
compromise, you can recreate the key, create 3 new shares, and destroy
the old ones, preventing the attacker from getting a second share. The
new shares are unrelated to the old ones because a different random
line through (0,&lt;i&gt;S&lt;/i&gt;) is used.&lt;br&gt;
&lt;br&gt;
In 1979, Shamir generalized these ideas even further to create a general (&lt;i&gt;n&lt;/i&gt;,&lt;i&gt;k&lt;/i&gt;) secret sharing scheme for any &lt;i&gt;n&lt;/i&gt;, &lt;i&gt;k&lt;/i&gt;. The idea is to choose a random degree-&lt;i&gt;k&lt;/i&gt; polynomial passing through (0,&lt;i&gt;S&lt;/i&gt;), where &lt;i&gt;S&lt;/i&gt; is the secret. Then choose &lt;i&gt;n&lt;/i&gt; random points on this curve. Because there is exactly one degree-&lt;i&gt;k&lt;/i&gt; polynomial passing through any given set of &lt;i&gt;k&lt;/i&gt; points, any &lt;i&gt;k&lt;/i&gt; of these points define the curve and allow us to retrieve the secret. If we have only &lt;i&gt;k&lt;/i&gt; - 1 points, then we don't learn anything about &lt;i&gt;S&lt;/i&gt;, because there is a polynomial passing through (0,&lt;i&gt;T&lt;/i&gt;) and those &lt;i&gt;k&lt;/i&gt; - 1 points for every &lt;i&gt;T&lt;/i&gt;.
To make the notion of choosing random polynomials and random points
precise, Shamir works with polynomials modulo a prime number &lt;i&gt;p&lt;/i&gt;, choosing polynomial coefficients and point coordinates randomly from the integers in [0, &lt;i&gt;p&lt;/i&gt; - 1].&lt;br&gt;
&lt;br&gt;
Secret sharing has more applications than just protecting private keys.
It's also useful in situations where we want to entrust a secret to a
group of people but want to ensure that several of them must cooperate
to access the secret. We don't need to trust them all individually, nor
do they all need to cooperate, so the secret is still available if
members go missing or refuse to cooperate.&lt;br&gt;
&lt;br&gt;
Finally, I wouldn't just tell you all this without providing some
working code you can use. You can download an excellent implementation
of the scheme for UNIX called &lt;a href="http://www.mindrot.org/misc-code.html"&gt;secsplit&lt;/a&gt; (&lt;a href="http://www2.mindrot.org/files/secsplit-1.2.tar.gz"&gt;direct download&lt;/a&gt;).
I don't know of any free .NET implementation, but I'm sure that porting
this 600-line program into any environment wouldn't be difficult.&lt;br&gt;
&lt;br&gt;
Some more resources on secret sharing:&lt;br&gt;
&lt;ul&gt;
  
  &lt;li&gt;&lt;a href="http://www.cs.cornell.edu/Courses/cs513/2000SP/SecretSharing.html"&gt;Lectures notes&lt;/a&gt; from Cornell's CS 513&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Secret_sharing"&gt;Wikipedia article&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Adi Shamir's "&lt;a href="http://www.caip.rutgers.edu/%7Evirajb/readinglist/shamirturing.pdf"&gt;How to Share a Secret&lt;/a&gt;"&lt;br&gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2259"&gt;RSA FAQ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=476273" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/devdev/archive/tags/Algorithms/default.aspx">Algorithms</category></item><item><title>Modular arithmetic and primality testing</title><link>http://blogs.msdn.com/devdev/archive/2005/09/07/462070.aspx</link><pubDate>Wed, 07 Sep 2005 20:29:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:462070</guid><dc:creator>dcoetzee</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/devdev/comments/462070.aspx</comments><wfw:commentRss>http://blogs.msdn.com/devdev/commentrss.aspx?PostID=462070</wfw:commentRss><description>&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Number_theory"&gt;Number theory&lt;/a&gt;
is, roughly speaking, the study of properties of integers. Often a
problem which is easy for real numbers, such as factoring or linear
programming, seems to be considerably more difficult when restricted to
integers (in fact, integer programming is NP-hard). Much of the focus
of modern number theory is on solving these problems efficiently.&lt;/p&gt;
&lt;p&gt;The practical importance of number theory was greatly increased by
the introduction of RSA cryptography, an unprecedented system whereby
information could be securely transmitted without first transmitting
the key and risking its interception. Central to RSA is the ability to
efficiently generate a semiprime, which is simply a product of two very
large primes of roughly equal magnitude. What's useful about these
numbers is that we can generate them efficiently, yet given just the
semiprime, determining the two prime factors is a hard,
unsolved&amp;nbsp;problem. It turns out that there are a lot of primes to
choose from: the probability that a randomly chosen k-bit number is
prime is between 0.69/&lt;em&gt;k &lt;/em&gt;and 0.87/&lt;em&gt;k.&lt;/em&gt; For example, about 7-9% of 10-bit numbers are prime. Consequently, we can find a &lt;em&gt;k&lt;/em&gt;-bit prime number in an expected O(&lt;em&gt;k&lt;/em&gt;) random guesses. The only remaining problem is to find an efficient algorithm for testing these guesses for primality.&lt;/p&gt;
&lt;p&gt;Brute force primality tests such as simply dividing the number by
all values less than the square root are ineffective for large numbers.
To get around this, we'll need&amp;nbsp;an important&amp;nbsp;tool called &lt;a href="http://en.wikipedia.org/wiki/Modular_arithmetic"&gt;modular arithmetic&lt;/a&gt;.
If you've written C, you may be surprised to know that you're already
familiar with modular arithmetic - the "wrap around" behavior of &lt;em&gt;k&lt;/em&gt;-bit integers is an implementation of arithmetic mod 2&lt;sup&gt;&lt;em&gt;k&lt;/em&gt;&lt;/sup&gt;. For example, this piece of code computes the value 17:&lt;/p&gt;&lt;pre&gt;    unsigned char c1 = 177, c2 = 96;&lt;br&gt;    unsigned char sum = c1 + c2;&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;In mathematical notation, we would say:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;177 + 96 ≡ 17 (mod 256)&lt;/p&gt;This
tells us that the numbers 177 + 96 and 17 differ by a multiple of 256,
or in other words have the same last 8 bits. Multiplication mod 256 is
likewise similar to multiplication of unsigned chars in C: &lt;pre&gt;    unsigned char c1 = 177, c2 = 23;&lt;br&gt;    unsigned char product = c1 * c2;&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;177 × 23 ≡ 231 (mod 256)&lt;/p&gt;
&lt;p&gt;Modular arithmetic works exactly the same for other moduli than 256;
the only difference is the number where it wraps around. For example,
if you compute 10 + 10 mod 17, you get 3.&lt;/p&gt;
&lt;p&gt;The interesting thing about modular arithmetic is that it can be shown that the values form a commutative &lt;a href="http://en.wikipedia.org/wiki/Ring_%28algebra%29"&gt;ring&lt;/a&gt;. This means, among other things, that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Addition and multiplication are commutative (you can reverse their operands without changing the result). 
&lt;/li&gt;&lt;li&gt;The associative property holds for both addition and
multipliation: (177 × 23) × 45 gives the same thing as 177 × (23 × 45),
even if computed with unsigned chars. &lt;/li&gt;&lt;li&gt;The distributive property holds: (177&amp;nbsp;+ 23) × 45 is equal to (177 × 45)&amp;nbsp;+ (23 × 45).&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;If none of these surprise you, this might: if the modulus is a power of 2, as in machine arithmetic, every odd number &lt;em&gt;m&lt;/em&gt; has a multiplicative&amp;nbsp;inverse. An inverse is simply a number&amp;nbsp;&lt;em&gt;n&lt;/em&gt; such that &lt;em&gt;m &lt;/em&gt;× &lt;em&gt;n&lt;/em&gt; = 1.&amp;nbsp;What good is this? Well, suppose you want to divide a number &lt;em&gt;k&lt;/em&gt; by&amp;nbsp;7 on a machine with no hardware division. You know that &lt;em&gt;k&lt;/em&gt; is divisible by 7. The inverse of 7 mod 256 is 183. Because 7 × 183 = 1, we have 7 × 183 × &lt;em&gt;k&lt;/em&gt; = &lt;em&gt;k&lt;/em&gt;. Divide both sides by 7, and we get 183 × &lt;em&gt;k&lt;/em&gt; = &lt;em&gt;k&lt;/em&gt;/7. In other words, multiplying by a number's inverse is the same as dividing by that number, provided that &lt;em&gt;k&lt;/em&gt; is evenly divisible by the number.&lt;/p&gt;
&lt;p&gt;Now we come back to our original problem: how can modular arithmetic
help us determine primality? Well, it's not hard to show that if &lt;em&gt;p&lt;/em&gt; is prime, then:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;For all &lt;em&gt;a &lt;/em&gt;with&lt;em&gt; &lt;/em&gt;0 &amp;lt; &lt;em&gt;a&lt;/em&gt; &amp;lt; &lt;em&gt;p&lt;/em&gt;, &lt;em&gt;a&lt;/em&gt;&lt;sup&gt;&lt;em&gt;p&lt;/em&gt;&lt;/sup&gt; ≡ &lt;em&gt;a&lt;/em&gt; (mod &lt;em&gt;p&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;This is called &lt;a href="http://en.wikipedia.org/wiki/Fermat%27s_little_theorem"&gt;Fermat's little theorem&lt;/a&gt;. What's useful about it is not only that it's true for all primes, but that if you find an &lt;em&gt;a&lt;/em&gt; such that it does not hold, you have proven that the number &lt;em&gt;p&lt;/em&gt; is composite. This can be checked efficiently because there is &lt;a href="http://en.wikipedia.org/wiki/Exponentiation_by_squaring"&gt;an efficient algorithm for computing large powers&lt;/a&gt;.
This is simply an existence proof - it tells us nothing about what the
factors are. It can be shown that for most composite numbers, if you
just select several &lt;em&gt;a&lt;/em&gt; at random and try them, one is likely to fail the test. Unfortunately, there are an infinite number of special numbers called &lt;a href="http://en.wikipedia.org/wiki/Carmichael_number"&gt;Carmichael numbers&lt;/a&gt; for which the above result holds for all &lt;em&gt;a&lt;/em&gt;, even though they are not prime.&lt;/p&gt;
&lt;p&gt;To get around this, we design a new, slightly more
complicated&amp;nbsp;test which is not susceptible to this problem. I take
this from the excellent book &lt;em&gt;&lt;a href="http://www.amazon.com/exec/obidos/tg/detail/-/0387252827/"&gt;Prime Numbers: A Computational Perspective&lt;/a&gt;&lt;/em&gt;, by Richard Crandall and Carl Pomerance. Suppose&amp;nbsp;&lt;em&gt;p&lt;/em&gt; is an odd prime, and that the binary representation of &lt;em&gt;p&lt;/em&gt; - 1 is the odd number &lt;em&gt;t&lt;/em&gt; followed by &lt;em&gt;s&lt;/em&gt; zeros. Then one of the following is true for every &lt;em&gt;a&lt;/em&gt; with 0 &amp;lt; &lt;em&gt;a&lt;/em&gt; &amp;lt; &lt;em&gt;p &lt;/em&gt;- 1:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&lt;sup&gt;t&lt;/sup&gt;&lt;/em&gt; ≡&amp;nbsp;1 (mod &lt;em&gt;p&lt;/em&gt;)&lt;br&gt;&lt;em&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&lt;sup&gt;&lt;em&gt;t&lt;/em&gt; &amp;lt;&amp;lt; &lt;em&gt;i&lt;/em&gt;&lt;/sup&gt;&lt;/em&gt; ≡&amp;nbsp;p - 1 (mod &lt;em&gt;p&lt;/em&gt;) for some &lt;em&gt;i&lt;/em&gt; with 0 ≤ &lt;em&gt;i&lt;/em&gt; &amp;lt; &lt;em&gt;s&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Above "&amp;lt;&amp;lt;" denotes the shift-left operator. It can be shown
that for any odd composite number &amp;gt; 9, both of the above will fail
for at least 3/4 of the &lt;em&gt;a&lt;/em&gt; between 1 and &lt;em&gt;p&lt;/em&gt;-2. We can use this to design a simple probabilistic algorithm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Choose an &lt;em&gt;a&lt;/em&gt; at random with 0 &amp;lt; &lt;em&gt;a&lt;/em&gt; &amp;lt; &lt;em&gt;p&lt;/em&gt;-1. 
&lt;/li&gt;&lt;li&gt;Check if one of the above formulas holds. If not, &lt;em&gt;p&lt;/em&gt; is composite. 
&lt;/li&gt;&lt;li&gt;If we have iterated at least T times,&amp;nbsp;claim that &lt;em&gt;p&lt;/em&gt; is prime. Otherwise, go back to step 1.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Here's an (untested) piece of C# code which implements this simple
algorithm, assuming that BigInteger is a suitable arbitrary-precision
integer type (the Framework provides no such type):&lt;/p&gt;&lt;pre&gt;    public bool IsProbablePrime(BigInteger p, int T) {&lt;br&gt;        int s = 0;&lt;br&gt;        BigInteger t = p - 1;&lt;br&gt;        while (t.IsEven()) {&lt;br&gt;            s++;&lt;br&gt;            t &amp;gt;&amp;gt;= 1;&lt;br&gt;        }&lt;br&gt;        for (int repeat = 0; repeat &amp;lt; T; T++) {&lt;br&gt;            BigInteger a = BigInteger.Random(1, p - 2);&lt;br&gt;            if (!PassesPrimalityTest(a, p, s, t)) {&lt;br&gt;                return false;&lt;br&gt;            }&lt;br&gt;        }&lt;br&gt;        return true;&lt;br&gt;    }&lt;br&gt;&lt;br&gt;    public bool PassesPrimalityTest(BigInteger a, BigInteger p, int s, BigInteger t) {&lt;br&gt;        BigInteger b = BigInteger.ModPower(a, t, p); // b = a^t mod p&lt;br&gt;        if (b == 1 || b == p - 1) return true;&lt;br&gt;        for (int j = 1; j &amp;lt; s; j++) {&lt;br&gt;            b = BigInteger.ModPower(b, 2, p);&lt;br&gt;            if (b == p - 1) return true;&lt;br&gt;        }&lt;br&gt;        return false;&lt;br&gt;    }&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;Because each trial is independent, the chance of erroneously claiming that &lt;em&gt;p&lt;/em&gt; is prime is 1/4&lt;sup&gt;T&lt;/sup&gt;,
which is ridiculously tiny even for small T, like T = 20. We can now
use this to quickly locate large prime numbers and generate semiprimes
for RSA cryptography.&lt;/p&gt;
&lt;p&gt;Unfortunately, these tests identify composite numbers but reveal
nothing about their factors. This makes them useless for factoring
numbers. Unlike many other hard problems, the factoring problem is
believed to not be NP-complete, and so may very well have an efficient
algorithm that no one has found yet. Such an algorithm would make RSA
encryption useless and win you &lt;a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2093"&gt;$625,000&lt;/a&gt;.
Combinations of advanced techniques have managed to factor very large
numbers, but only at an enormous expense in computing time and
hardware. This is one of the most active current areas of research in
number theory and computer science.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=462070" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/devdev/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://blogs.msdn.com/devdev/archive/tags/Theory/default.aspx">Theory</category></item></channel></rss>