Thursday, May 29, 2008

J and Project Euler p35

Problem definition:
------------------------------------------
The number, 197, is called a circular prime because all rotations of the digits: 197, 971, and 719, are themselves prime.

There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, and 97.

How many circular primes are there below one million?
------------------------------------------

This time again solution in J language:


rot =: |: ". (i.@:#) ((({:,}:) ^: )) (@: ":)
ffac =: 0{|: @: q: @: rot
circ =: ((+/@: (ffac = rot)) = (#@:":))
tot =: +/ @: (circ"0) @: (2&+)
tot i.999998

Tuesday, May 27, 2008

Learning J language - euler problem36

Project euler problem 36 definition:
--------------------------------------
The decimal number, 585 = 1001001001 (binary), is palindromic in both bases.

Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2.

(Please note that the palindromic number, in either base, may not include leading zeros.)
--------------------------------------

So, I has some interest in array programming language J. I try to learn it a bit. And as such, I tried to solve this euler problem with the help of J language. What I learned is that this J language is VERY powerful and also it`s programs is very short, but also it is very hard to learn this language and of course - hard to understand the code (if you don`t know the language). But as soon as you proceed - more things are showing up from the fog :-).

So this is the solution:


+/@:((0:`(0&+))@.((+/@:(=|.)=#)@:":*.(+/@:(=|.)=#)@:#:)"0)i.1000000


One shot - One Kill, oh sorry, I mean: One line - One Goal :-)

As you see this code is not much understandable, but I have a little bit more readable code, acctually code is the same- just with defined function names.
This is it:


equalbits=: +/@:(=|.)
decpalind =: (equalbits=#)@:":
binpalind =: (equalbits=#)@:#:
bothpalind =: (decpalind*.binpalind)"0
palindsum =: +/@:((0:`(0&+))@.bothpalind)
palindsum i.1000000


Also just 6 lines of code. And a bit more readable, but not more if you don`t know some J basics. This language is very powerfull - especially at data processing.
If you want to learn J some basics, I would recommend this online book (also comes for free as documentation in J installation pack). Note - J interpreter is also free. This book is very good introduction, without it I would not take a look at J.
So all in all J - powerfull,interesting,strange,hard language in the same time, IMHO.
Have fun !!

Wednesday, May 21, 2008

Project Euler p102 - finding origin

Problem description:
-----------------------------------------------------
Three distinct points are plotted at random on a Cartesian plane, for which -1000 ≤ x, y ≤ 1000, such that a triangle is formed.

Consider the following two triangles:

A(-340,495), B(-153,-910), C(835,-947)

X(-175,41), Y(-421,-714), Z(574,-645)

It can be verified that triangle ABC contains the origin, whereas triangle XYZ does not.

Using triangles.txt (right click and 'Save Link/Target As...'), a 27K text file containing the co-ordinates of one thousand "random" triangles, find the number of triangles for which the interior contains the origin.

NOTE: The first two examples in the file represent the triangles in the example given above.
-----------------------------------------------------

I`ve exploited the idea that for origin to be in the triangle interior, triangle lines segments should cross x and y axis 2 times: x+ / x- and y+ / y-.
Basically it is enough to check does triangle cross y+ and y- axis parts, but I didn`t know that and checked both axis. This worked too :-)

Solution ( C# as always):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
class Program
{
static int[][] ReadTriangleCoordinates(string file)
{
int[][] res;
StreamReader sr = new StreamReader(file);
res = Array.ConvertAll(sr.ReadToEnd().TrimEnd().Split("\n".ToCharArray()),
s => {return Array.ConvertAll(s.Split(",".ToCharArray()),
n => { return int.Parse(n); }) ;});
sr.Close();
return res;
}

static void CrossesAxisAt(int x1,
int y1,
int x2,
int y2,
out double? xAt,
out double? yAt)
{
int dx = x2 - x1;
int dy = y2 - y1;
int xmin = Math.Min(x1, x2);
int ymin = Math.Min(y1, y2);
int xmax = Math.Max(x1, x2);
int ymax = Math.Max(y1, y2);
double? xcross;
double? ycross;
double a;
double b;

if (dx == 0)
{
xcross = x1;
ycross = null;
}
else if (dy == 0)
{
xcross = null;
ycross = y1;
}
else
{
a = (double)dy / (double)dx;
b = y1 - (a * x1);
xcross = -(b / a);
ycross = b;
}

if (xcross != null)
{
if (xcross < xmin || xcross > xmax
|| ymin > 0 || ymax < 0)
xcross = null;
}

if (ycross != null)
{
if (ycross < ymin || ycross > ymax
|| xmin > 0 || xmax < 0)
ycross = null;
}

xAt = xcross;
yAt = ycross;
}

static bool ContainsOrigin(int[] abc)
{
double? xc1, xc2, xc3;
double? yc1, yc2, yc3;
int xposc = 0, xnegc = 0, yposc = 0, ynegc = 0;
bool res;

CrossesAxisAt(abc[0], abc[1], abc[4], abc[5], out xc1, out yc1);
CrossesAxisAt(abc[0], abc[1], abc[2], abc[3], out xc2, out yc2);
CrossesAxisAt(abc[2], abc[3], abc[4], abc[5], out xc3, out yc3);

xposc = ((xc1 > 0) ? 1 : 0) + ((xc2 > 0) ? 1 : 0) + ((xc3 > 0) ? 1 : 0);
xnegc = ((xc1 < 0) ? 1 : 0) + ((xc2 < 0) ? 1 : 0) + ((xc3 < 0) ? 1 : 0);
yposc = ((yc1 > 0) ? 1 : 0) + ((yc2 > 0) ? 1 : 0) + ((yc3 > 0) ? 1 : 0);
ynegc = ((yc1 < 0) ? 1 : 0) + ((yc2 < 0) ? 1 : 0) + ((yc3 < 0) ? 1 : 0);

if (xposc > 0 && xnegc > 0 && yposc > 0 && ynegc > 0)
res = true;
else
res = false;

return res;
}

static int TrianglesWithOrigin(string file)
{
int[][] tr = ReadTriangleCoordinates(file);
int trcount = 0;

for (int i = 0; i < tr.Length; i++)
{
if (ContainsOrigin(tr[i]))
trcount++;
}

return trcount;
}

static void Main(string[] args)
{
Console.WriteLine(TrianglesWithOrigin("C:\\ProjectEuler\\triangles.txt"));
Console.ReadLine();
}
}
}

Sunday, May 18, 2008

IBM Ponder This, May 2008 - cellular automaton

From the IBM project "ponder this", may 2008:
-------------------------------------------------
This month's puzzle is about Conway's Game of life.
The game is a cellular automaton on a grid where in each generation, every cell determines its state (dead or alive) based on its eight neighbors: a dead cell will change its state to alive if and only if exactly three of its neighbors are alive; A live cell will stay alive if and only if two or three of its neighbors are alive.
All the changes are done simultaneously.
Find a possible state of the universe whose next generation is depicted below.


....................
....................
..###..###...#...#..
...#....#.#..##.##..
...#....##...#.#.#..
...#....#.#..#...#..
..###..###...#...#..
....................
....................


"." denotes dead cells and "#" denotes live cells.
-------------------------------------------------

So after a bit of thinking i chose Selfish Gene Algorithm, which I also presented on my blog article. Please note - I will present solution which will use 4 classes from the given my blog link above. So if you need to test this solution - download the code from that article also and import these classes:
- SGAallele.cs
- SGAgenotype.cs
- SGAlocus.cs
- SGAlocusGroup.cs
Now about solution. In general - solution with the help of selfish gene algorithm, tries to evolve cellular automaton configuration which next generation is similar to the one described in problem definition. Fitness is calculated as wrong cell amount - that is cell, which is not in place of target configuration, amount. Algorithm tries to minimize that wrong cell amount. So solution is this:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SGA;

namespace ConsoleApplication18
{
class Program
{
static int counter = 0;
static bool[][] grid = InitialGrid();
static SGAgenotype gen;

static void ShowCells(bool[][] cells)
{
string outp;
outp = cells.Aggregate("", (acc1, ba) =>
acc1 + ba.Aggregate("", (acc2, b) =>
acc2 + ((b) ? "#" : ".")) + "\n");
Console.Write(outp);
}

static bool[][] InitialGrid()
{
bool[][] c = new bool[9][];
c[0] = new bool[20];
c[1] = new bool[20];
c[2] = new bool[] { false, false, true, true, true, false, false,
true, true, true, false, false, false, true,
false, false, false, true, false, false };
c[3] = new bool[] { false, false, false, true, false, false, false,
false, true, false, true, false, false, true,
true, false, true, true, false, false };
c[4] = new bool[] { false, false, false, true, false, false, false,
false, true, true, false, false, false, true,
false, true, false, true, false, false };
c[5] = new bool[] { false, false, false, true, false, false, false,
false, true, false, true, false, false, true,
false, false, false, true, false, false };
c[6] = new bool[] { false, false, true, true, true, false, false, true,
true, true, false, false, false, true, false,
false, false, true, false, false };
c[7] = new bool[20];
c[8] = new bool[20];

return c;
}

static int LiveCells(bool[][] grid, int row, int col)
{
int live = 0;

for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
if (i != row || j != col)
{
if (grid[i][j])
live++;
}
}
}

return live;
}

static bool WillLive(bool IsLive, int LiveNeighbours)
{
bool state;

if (IsLive)
state = (LiveNeighbours < 2 || LiveNeighbours > 3) ? false : true;
else
state = (LiveNeighbours == 3) ? true : false;

return state;
}

static bool[][] ComputeStates(bool[][] grid)
{
bool[][] res = new bool[9][];
res[0] = new bool[20];
res[1] = new bool[20];
res[2] = new bool[20];
res[3] = new bool[20];
res[4] = new bool[20];
res[5] = new bool[20];
res[6] = new bool[20];
res[7] = new bool[20];
res[8] = new bool[20];

for (int i = 1; i < grid.Length - 1; i++)
{
for (int j = 1; j < grid[i].Length - 1; j++)
{
res[i][j] = WillLive(grid[i][j], LiveCells(grid, i, j));
}
}

return res;
}

static bool[][] CellGridFromGenotype(SGAgenotype gen, SGAgenotype.GenomeNo num)
{
int row, col;
bool v;
int gene = -1;
bool[][] res = new bool[9][];
res[0] = new bool[20];
res[1] = new bool[20];
res[2] = new bool[20];
res[3] = new bool[20];
res[4] = new bool[20];
res[5] = new bool[20];
res[6] = new bool[20];
res[7] = new bool[20];
res[8] = new bool[20];

for (int i = 0; i < gen.locusGroups.Length; i++)
{
row = (i) / 20;
col = (i) - (row * 20);

if (num == SGAgenotype.GenomeNo.FirstGenome)
gene = gen.locusGroups[i].loci[0].FirstGeneIndex;
else if (num == SGAgenotype.GenomeNo.SecondGenome)
gene = gen.locusGroups[i].loci[0].SecondGeneIndex;
else if (num == SGAgenotype.GenomeNo.None)
gene = gen.locusGroups[i].loci[0].BestGeneIndex;

v = Convert.ToBoolean(gen.locusGroups[i].loci[0].Alleles[gene].Value);

res[row][col] = v;
}

return res;
}

static int WrongCellCount(bool[][] previousStates)
{
int res = 0;
bool[][] nextStates = ComputeStates(previousStates);

for (int i = 0; i < grid.Length; i++)
{
for (int j = 0; j < grid[i].Length; j++)
{
if (nextStates[i][j] != grid[i][j])
res++;
}
}

return res;
}

static SGAgenotype.CompareResults CompareCellAutomata()
{
SGAgenotype.CompareResults cr;
bool[][] grid1 = CellGridFromGenotype(gen, SGAgenotype.GenomeNo.FirstGenome);
bool[][] grid2 = CellGridFromGenotype(gen, SGAgenotype.GenomeNo.SecondGenome);
bool[][] gridb = CellGridFromGenotype(gen, SGAgenotype.GenomeNo.None);

int wcellsGene1 = WrongCellCount(grid1);
int wcellsGene2 = WrongCellCount(grid2);
int wcellsGeneb = WrongCellCount(gridb);

cr.CompareEachOther = SGAgenotype.GenomeNo.None;
cr.CompareWithBest = SGAgenotype.GenomeNo.None;

if (wcellsGene1 < wcellsGene2)
cr.CompareEachOther = SGAgenotype.GenomeNo.FirstGenome;
else if (wcellsGene2 < wcellsGene1)
cr.CompareEachOther = SGAgenotype.GenomeNo.SecondGenome;

if (wcellsGene1 < wcellsGeneb)
cr.CompareWithBest = SGAgenotype.GenomeNo.FirstGenome;
else if (wcellsGene2 < wcellsGeneb)
cr.CompareWithBest = SGAgenotype.GenomeNo.SecondGenome;

counter++;

if (counter%1000==0)
{
Console.Clear();
ShowCells(gridb);
Console.WriteLine("====================");
ShowCells(ComputeStates(gridb));
Console.WriteLine("====================\nPlease wait...");
}

return cr;
}

static void Main(string[] args)
{
gen = new SGAgenotype(new SGAlocus[] { new SGAlocus(0, 1, 1) },
180, CompareCellAutomata);
gen.AlleleAffectValue *= 0.1;
gen.MutationProbability *= 1.5;
gen.Evolve(400000);
Console.WriteLine("DONE.");
Console.ReadLine();
}
}
}


After execution we get this picture-


...............#....
.#....#.....#.....##
...##...##..........
...#....#....##.##.#
....#....##..#...#.# ANSWER
..#.....#.....#..#..
...#....##..#.....#.
...#..#.....#...#...
....................
====================
....................
....................
..###..###...#...#..
...#....#.#..##.##..
...#....##...#.#.#.. NEXT GENERATION OF CELLS
...#....#.#..#...#..
..##...###...#...#..
....................
....................
====================


First picture is answer and second (below) is next generation of cells.
We see that this is approximate answer because next generation differs by 1 cell from the given situation in problem definition. Well, this is because I`ve used genetic algorithm which by definition is probabilistic and doesn`t guarantee to give exact solution. But very well calculates approximate solutions.
So all in all - it was fun to play with selfish gene algorithm and cellular automaton.

Tuesday, May 13, 2008

Traveler's dilemma and bonus factor

Traveler's dilemma description is following (taken from wikipedia):
-------------------------------------------------------
An airline loses two suitcases belonging to two different travelers. Both suitcases happen to be identical and contain identical antiques. An airline manager tasked to settle the claims of both travelers explains that the airline is liable for a maximum of $100 per suitcase, and in order to determine an honest appraised value of the antiques the manager separates both travelers so they can't confer, and asks them to write down the amount of their value at no less than $2 and no larger than $100. He also tells them that if both write down the same number, he will treat that number as the true dollar value of both suitcases and reimburse both travelers that amount. However, if one writes down a smaller number than the other, this smaller number will be taken as the true dollar value, and both travelers will receive that amount along with a bonus/malus: $2 extra will be paid to the traveler who wrote down the lower value and a $2 deduction will be taken from the person who wrote down the higher amount. The challenge is: what strategy should both travelers follow to decide the value they should write down?
-------------------------------------------------------

Experiment was conducted as following:
- BONUS was chosen between 2 and 20
- Possible travelers request was between BONUS and 100.
- Travelers chooses cost randomly (for no-strategy imitation)
- Cost random selection was repeated 5000000 times
- After that optimal request was calculated as travelers choise which gives the greatest profit.
- And finally these optimal requests are plot in diagram as function of BONUS.

C# code which conducted experiments on random travelers choise is this:

class Program
{
static void Payoff(int Aoffer, int Boffer, int extra, out int Apayoff, out int Bpayoff)
{
int Aextra, Bextra;
int minoffer = Math.Min(Aoffer, Boffer);

if (Aoffer == Boffer)
{
Aextra = Bextra = 0;
}
else
{
Aextra = (Aoffer == minoffer) ? extra : -extra;
Bextra = (Boffer == minoffer) ? extra : -extra;
}
Apayoff = minoffer + Aextra;
Bpayoff = minoffer + Bextra;
}

static long OptimalSelection(int costFrom, int costTo, int goods)
{
long[] payoffA = new long[costTo + 1];
long[] payoffB = new long[costTo + 1];
Random r = new Random();
int Ac, Bc;
int Pa, Pb;
long Aopt = -1, Bopt = -1;
long Amax = 0, Bmax = 0;

for (int i = 1; i <= goods; i++)
{
Ac = r.Next(costFrom, costTo + 1);
Bc = r.Next(costFrom, costTo + 1);
Payoff(Ac, Bc, costFrom, out Pa, out Pb);
payoffA[Ac] += Pa;
payoffB[Bc] += Pb;
}

for (int j = costFrom; j <= costTo; j++)
{
if (payoffA[j] > Amax) { Amax = payoffA[j]; Aopt = j; }
if (payoffB[j] > Bmax) { Bmax = payoffB[j]; Bopt = j; }
}

return (Aopt + Bopt) / 2;
}

static void MeasureBonusInfluence()
{
double[] bonus = Array.ConvertAll(Enumerable.Range(2, 20).ToArray(),
v => (double)(v));
double[] optimal = Array.ConvertAll(bonus,
v => (double)OptimalSelection((int)v, 100, 5000000));

GoogleChart.PlotLineChart(bonus,
optimal,
"Bonus ($)",
"Optimal request ($)",
"Traveler's dilemma - bonus factor",
"400x200");
}

static void Main(string[] args)
{
MeasureBonusInfluence();
}
}

class GoogleChart
{
public static void PlotLineChart(double[] Xvalues,
double[] Yvalues,
string Xlabel,
string Ylabel,
string Title,
string imgsize)
{
string urltemplate = "http://chart.apis.google.com/chart?cht=lxy&chs=@imgsize@&chd=t:@xvalues@%7C@yvalues@&chds=@xmin@,@xmax@,@ymin@,@ymax@&chxr=0,@xmin@,@xmax@%7C1,@ymin@,@ymax@&chxt=x,y,x,y&chxl=2:%7C@xlabel@%7C3:%7C@ylabel@%7C&chxp=2,50%7C3,50&chtt=@title@";
string xvalagg = Xvalues.Aggregate("", (old, next) =>
old + ((old == "") ? "" : ",") + next.ToString(CultureInfo.InvariantCulture));
string yvalagg = Yvalues.Aggregate("", (old, next) =>
old + ((old == "") ? "" : ",") + next.ToString(CultureInfo.InvariantCulture));

if (imgsize == null) imgsize = "300x200";

urltemplate = urltemplate.Replace("@imgsize@", imgsize);
urltemplate = urltemplate.Replace("@xvalues@", xvalagg);
urltemplate = urltemplate.Replace("@yvalues@", yvalagg);
urltemplate = urltemplate.Replace("@xmin@", Xvalues.Min().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@xmax@", Xvalues.Max().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@ymin@", Yvalues.Min().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@ymax@", Yvalues.Max().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@xlabel@", Xlabel);
urltemplate = urltemplate.Replace("@ylabel@", Ylabel);
urltemplate = urltemplate.Replace("@title@", Title);

System.Diagnostics.Process.Start(urltemplate);
}
}


Finally after execution of this program, we get this picture of optimal request relation with bonus amount (if travelers chooses randomly):

Wednesday, May 7, 2008

Google chart API in .NET

Yes I know - there are good .NET libraries for using google chart API, but I was interested to dig a little bit deeper into google chart API. And I have to say that google chart is worse than opensource program GNUPLOT (in the sense of functionality), but ok whatever... And second target was - to have as less code (including libraries) as possible for charting VERY simple XY graphs. So after studing a bit google chart documentation- I wrote very primitive class for plotting simple XY graph. For plotting graphs with this code you need:
- Internet connection
- .NET framework 3.5
- Internet browser (whatever you like)
So this is the code:

class Program
{
static void Main(string[] args)
{
double[] xval;
double[] ysin, yexp;

xval = new double[] { 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5 };
ysin = Array.ConvertAll(xval, v => { return Math.Sin(v); });
yexp = Array.ConvertAll(xval, v => { return Math.Exp(v); });

GoogleChart.PlotLineChart(xval, ysin, "x values", "sin(x)", "sin(x) function", null);
GoogleChart.PlotLineChart(xval, yexp, "x values", "exp(x)", "exp(x) function", null);
}
}

class GoogleChart
{
public static void PlotLineChart(double[] Xvalues,
double[] Yvalues,
string Xlabel,
string Ylabel,
string Title,
string imgsize)
{
string urltemplate = "http://chart.apis.google.com/chart?cht=lxy&chs=@imgsize@&chd=t:@xvalues@%7C@yvalues@&chds=@xmin@,@xmax@,@ymin@,@ymax@&chxr=0,@xmin@,@xmax@%7C1,@ymin@,@ymax@&chxt=x,y,x,y&chxl=2:%7C@xlabel@%7C3:%7C@ylabel@%7C&chxp=2,50%7C3,50&chtt=@title@";
string xvalagg = Xvalues.Aggregate("", (old, next) =>
old + ((old == "") ? "" : ",") + next.ToString(CultureInfo.InvariantCulture));
string yvalagg = Yvalues.Aggregate("", (old, next) =>
old + ((old == "") ? "" : ",") + next.ToString(CultureInfo.InvariantCulture));

if (imgsize == null) imgsize = "300x200";

urltemplate = urltemplate.Replace("@imgsize@", imgsize);
urltemplate = urltemplate.Replace("@xvalues@", xvalagg);
urltemplate = urltemplate.Replace("@yvalues@", yvalagg);
urltemplate = urltemplate.Replace("@xmin@", Xvalues.Min().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@xmax@", Xvalues.Max().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@ymin@", Yvalues.Min().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@ymax@", Yvalues.Max().ToString(CultureInfo.InvariantCulture));
urltemplate = urltemplate.Replace("@xlabel@", Xlabel);
urltemplate = urltemplate.Replace("@ylabel@", Ylabel);
urltemplate = urltemplate.Replace("@title@", Title);

System.Diagnostics.Process.Start(urltemplate);
}
}

With this sample code there are generated these nice graphs:


This is it. Have fun with charting !

Tuesday, May 6, 2008

Project Euler p59 - breaking encryption

Problem definition:
------------------------------------------------
Each character on a computer is assigned a unique code and the preferred standard is ASCII (American Standard Code for Information Interchange). For example, uppercase A = 65, asterisk (*) = 42, and lowercase k = 107.

A modern encryption method is to take a text file, convert the bytes to ASCII, then XOR each byte with a given value, taken from a secret key. The advantage with the XOR function is that using the same encryption key on the cipher text, restores the plain text; for example, 65 XOR 42 = 107, then 107 XOR 42 = 65.

For unbreakable encryption, the key is the same length as the plain text message, and the key is made up of random bytes. The user would keep the encrypted message and the encryption key in different locations, and without both "halves", it is impossible to decrypt the message.

Unfortunately, this method is impractical for most users, so the modified method is to use a password as a key. If the password is shorter than the message, which is likely, the key is repeated cyclically throughout the message. The balance for this method is using a sufficiently long password key for security, but short enough to be memorable.

Your task has been made easy, as the encryption key consists of three lower case characters. Using cipher1.txt (right click and 'Save Link/Target As...'), a file containing the encrypted ASCII codes, and the knowledge that the plain text must contain common English words, decrypt the message and find the sum of the ASCII values in the original text.
------------------------------------------------

C# brute-force solution, running in 42 seconds:

static byte[] ReadCipher(string file)
{
StreamReader sr = new StreamReader(file);
string[] buf = sr.ReadToEnd().Trim().Split(",".ToCharArray());
byte[] res = Array.ConvertAll(buf, s => { return byte.Parse(s); });
sr.Close();

return res;
}

static byte[] TryDecipher(byte[] cip, string key)
{
int ind = 0;
byte[] dec = new byte[cip.Length];

for (int i = 0; i < cip.Length; i++)
{
dec[i] = (byte)((int)cip[i] ^ (int)key[ind]);
ind = (ind >= 2) ? 0 : ind + 1;
}

return dec;
}

static int KeywordsInText(string text, string[] keywords)
{
int kc = 0;

for (int i = 0; i < keywords.Length; i++)
{
if (text.ToLower().IndexOf(" " + keywords[i] + " ") > -1) kc++;
}

return (kc*100) / keywords.Length;
}

static void Decipher(string file)
{
string[] keywords = new string[] {"the","be","is","are","was","to","of","and","a","in","into","that","have","has","had","I","it","for","not","on","with","he","as","you","do","did","done","at","this","but","his","by","from","they","we","say","her","she","or","an","will","shall","my","one","all","would","should","there","here","their","what","so","up","out","if","about","who","get","got","which","go","went","gone","me"};
string passchars = "qwertyuioplkjhgfdsazxcvbnm";
string curkey = "";
byte[] cipher = ReadCipher(file);
byte[] decoded = new byte[cipher.Length];
string dectext;
int keywordcount = 0;
int maxkeywords = 0;
string bestkey = "";
byte[] bestbytes = new byte[cipher.Length];
string besttext = "";
long bytesum = 0;

for (int i1 = 0; i1 < passchars.Length; i1++)
{
for (int i2 = 0; i2 < passchars.Length; i2++)
{
for (int i3 = 0; i3 < passchars.Length; i3++)
{
curkey = passchars[i1].ToString() + passchars[i2].ToString() + passchars[i3].ToString();
decoded = TryDecipher(cipher, curkey);
dectext = ASCIIEncoding.ASCII.GetString(decoded);
keywordcount = KeywordsInText(dectext, keywords);

if (keywordcount > maxkeywords)
{
maxkeywords = keywordcount;
bestkey = curkey;
bestbytes = decoded;
besttext = dectext;
}
}
}
}

for (int i = 0; i < bestbytes.Length; i++)
{
bytesum += (long)bestbytes[i];
}

Console.WriteLine("Byte sum: {0}\nPassword: {1}\nDecoded message 50 chars: {2}\nMessage has {3}% common words",
bytesum,bestkey,besttext.Substring(0,50),maxkeywords);
}

static void Main(string[] args)
{
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
Decipher("C:\\ProjectEuler\\cipher1.txt");
sw.Stop();
Console.WriteLine("Decryption time: {0} seconds",sw.Elapsed.Seconds);
Console.ReadLine();
}