Wednesday, April 22, 2009

Israel to US: shut up and do what I say (via Reddit)

"The Obama Administration will put forth new peace initiatives only if Israel wants it to, said Foreign Minister Avigdor Lieberman in his first comprehensive interview on foreign policy since taking office.

"Believe me, America accepts all our decisions," Lieberman told the Russian daily Moskovskiy Komosolets."

http://www.haaretz.com/hasen/spages/1080097.html

Tuesday, April 21, 2009

Dell Latitude: 8 hours on a single charge!

Tonight (by accident) I left my laptop unplugged and in a state where it could not go to sleep automatically. In the morning I found the laptop with its battery completely depleted, but still running!

Kudos to power management systems in Core 2 Duo and Windows Server 2008!

Monday, April 20, 2009

Laissez-faire (via Reddit)

"In Bucharest, you can buy a young girl for 8,000 euros. I mean buy them as in you own them forever or until you sell them to someone else. Most of them are sold by their parents in Moldova when they are little children. As soon as they’re bought, the owner brands them. This is normally a tattoo of his name."

http://www.viceland.com/int/v13n9/htdocs/slavery1.php?country=uk

While this sounds (reads?) insane, it is not really that unusual for an unregulated market to produce this phenomenon. It was not all this different in US in 1890s, and in Russia in 1990s - just the percentage of population is smaller.

Do laws have priority over money, or does money have priority over laws?

The Lifestyle

"It is a tricky situation in which some Americans find themselves after a long boom: They are by no means struggling, compared with the 98% of Americans who make far less, but depending on where they live and the lifestyle choices they have made, they don't necessarily feel rich, either."

http://finance.yahoo.com/focus-retirement/article/106934/Wealth-Less-Effect-Earning-Well-Feeling-Otherwise

Oh, yes, the life style choices... Take me, for instance. My personal choice, as anyone who knows me well will confirm, is to fly my own 747. I love to travel, and the fact that I have to wait in airports, sleep in a chair on long flights, and stand in long lines is simply unacceptable. It makes me feel positively broke. Middle class you say? As far as I am concerned, I am a pauper.

Now, Obama thinks that we are rich and wants to tax our hard earned money (that is, the amount above $250k) at 3% more. Which means that if we were to make, say, $350k next year, we'd be forking over $3000 more to the government in taxes. Instead of buying one of these: http://1-800-magic.blogspot.com/2009/04/computer-porn.html, which my chosen lifestyle dictates that I have. Talk about socialism!

Malevich has setup!

I've been working on Malevich - my code review system - on and off for almost 5 months now.

It turned out to be very successful - my team was using it for all of our first milestone, and was very happy about it (we're now pushing 1500 change lists that went through the system). Many other Microsoft teams adopted it since, to the tune of at least a couple hundred people - and these are just the people who contacted me.

The biggest problem so far was the setup. The deployment was hard - it required Visual Studio, a service pack, an expansion pack, and tons of manual work. A lot of errors were made, and in many cases I had to drop by and set things right afterwards.

Not any more! The first official build is out, and it has a real setup! The setup checks the dependencies, deploys the database, publishes the web site, and configures notifier automatically, no Visual Studio is required!

So if you wanted to try Malevich but were intimidated by the complexity of installation - the wait is over. You still do have to install Server 2008 with IIS and SQL Server 2008, but the rest is done for you.

Just get the MSI here: http://malevich.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=26374

Happy code reviewing!

Sunday, April 19, 2009

How is this not treason? (via Reddit)

"Rep. Jane Harman , the California Democrat with a longtime involvement in intelligence issues, was overheard on an NSA wiretap telling a suspected Israeli agent that she would lobby the Justice Department reduce espionage-related charges against two officials of the American Israeli Public Affairs Committee, the most powerful pro-Israel organization in Washington."

...

"In exchange for Harman’s help, the sources said, the suspected Israeli agent pledged to help lobby Nancy Pelosi, D-Calif., then-House minority leader, to appoint Harman chair of the Intelligence Committee after the 2006 elections, which the Democrats were heavily favored to win."

http://www.cqpolitics.com/wmspage.cfm?docID=hsnews-000003098436

Saturday, April 18, 2009

The new government

"As far as the most important matter is concerned, there is complete unanimity. Liberman, Netanyahu, Barak, Ellie Yishai of Shas and Danny Hershkovitz of the “Jewish Home” party are in total agreement about the Palestinians. All of them agree on the need to prevent the establishment of a real Palestinian state. All of them agree not to talk with Hamas. All of them support the settlement enterprise. During Barak’s stint as Prime Minister, the settlements grew even faster than during Netanyahu’s tenure. Liberman is himself a settler, Hershkovitz’s party represents the settlers. All of them believe that there is no need for peace, that peace is bad for us. (After all, it was Barak, not Netanyahu or Liberman, who coined the phrase “We Have No Partner for Peace”.)"

http://zope.gush-shalom.org/home/en/channels/avnery/1238277190

Computer porn


2xIntel Xeon E5410 Harpertown 2.33GHz 12MB L2 Cache LGA 771 80W Quad-Core Processorhttp://www.newegg.com/Product/Product.aspx?Item=N82E16819117150$590
1xSUPERMICRO MBD-X7DCL-I Dual LGA 771 Intel 5100 ATX Server Motherboardhttp://www.newegg.com/Product/Product.aspx?Item=N82E16813182146$300
6xKingston 4GB 240-Pin DDR2 SDRAM ECC Registered DDR2 667 (PC2 5300) Server Memoryhttp://www.newegg.com/Product/Product.aspx?Item=N82E16820134835$504
5xWestern Digital VelociRaptor WD1500HLFS 150GB 10000 RPM 16MB Cache SATA 3.0Gb/s 3.5" Hard Drivehttp://www.newegg.com/Product/Product.aspx?Item=N82E16822136296$900
1xAthena Power AP-P4ATX85FE 20+4Pin 850W Single ATX12V & EPS12V Power Supplyhttp://www.newegg.com/Product/Product.aspx?Item=N82E16817104126$155
1xSILVERSTONE KUBLAI Series KL03-B Black 2.5mm aluminum front door, 0.8mm SECC body ATX Full Tower Computer Casehttp://www.newegg.com/Product/Product.aspx?Item=N82E16811163100$155

...yields an 8-core, 24GB monster capable of hosting 10 VMs comfortably for $2600.

Saturday, April 11, 2009

Capitalism, socialism

Wikipedia defines capitalism thus:

"Capitalism is an economic system in which wealth, and the means of producing wealth, are privately owned and controlled rather than state owned and controlled. Through capitalism, the land, labor, and capital are owned, operated, and traded by private individuals either singly or jointly, and investments, distribution, income, production, pricing and supply of goods, commodities and services are determined by voluntary private decision in a market economy. A distinguishing feature of capitalism is that each person owns his or her own labor and therefore is allowed to sell the use of it to employers." (http://en.wikipedia.org/wiki/Capitalism).

The definition puts "ownership" and "control" of the capital in the same sentence, but does not stipulate that it is the same individual that both owns and controls the resources.

And therein lies our problem.

Commonly accepted theory of why capitalism is preferable to socialism goes like this: the owners generally make better decisions about allocating the resources than non-owners. When the owners do make sub-optimal decision, they lose their ownership stake to the smarter and more responsible new owners.

So far, so good. Except this is not at all the system that we have in the United States. In the US, the majority of the capital is owned by one set of people - the shareholders - and managed by an entirely different set of people - the corporate management.

The interests of these groups of people are not at all aligned (http://1-800-magic.blogspot.com/2007/12/risk-vs-reward.html), and, what's worse, neither group is truly interested in the long-term success of the business.

As a result, while the executives in younger companies which are still managed by the original owners do often tend to do the right thing, I cannot call the behavior of the managerial class in most American enterprises anything other than looting.

This approach to management has been most visible on Wall Street, but look at any older US enterprise, and you will see the same looting of company resources everywhere, from car manufacturers to steel mills to utility companies.

Comparing to the old Soviet days, the system as we have it right now may actually be worse. At least the bureaucrats in Soviet Union were not paying themselves multimillion dollar bonuses and did not zip around in private planes as the economy crumbled.

Thursday, April 9, 2009

Discounting risk

I wrote this article in December 2007 and it describes perfectly the situation we're in now: http://1-800-magic.blogspot.com/2007/12/risk-vs-reward.html

Wednesday, April 8, 2009

If you had only two news articles to read this week (via Reddit)...

...I would recommend "Ten principles for a Black Swan-proof world" by Nicolas Taleb (best known for his book "The Black Swan: The Impact of the Highly Improbable" about underpricing risk) and The dark side of Dubai. This last one is absolutely surreal. Make sure you read it at least through "The Lifestyle" chapter. It is really, truly, absolutely unbelievable.

Thursday, April 2, 2009

Visual Studio SP1 installer blues

I have but one question - what is taking it so long?

It takes 1.5 hours on my 2.5GHz, 4GB Core 2 Duo laptop running Windows Server 2008.

In 1.5 hours one could, at a typical speed of my laptop:
- Write 263GB of data to disk, at 50MBps
- Transfer 131GB of data over the network, at 200Mbps
- Copy 15 TB (!) of memory (on 800Mhz bus)
- Execute a whopping 26 trillion instructions (2 cores retiring 2 instructions per core per clock).

WHAT DOES THIS THING DO???

Wednesday, April 1, 2009

Weather plays April Fool's in Seattle

It's April 1st, and it's SNOWING! Here's what it looks like from our window.

Tuesday, March 31, 2009

The Lord of the Rings and Atlas Shrugged (via Reddit)

"There are two novels that can change a bookish fourteen-year old’s life: The Lord of the Rings and Atlas Shrugged. One is a childish fantasy that often engenders a lifelong obsession with its unbelievable heroes, leading to an emotionally stunted, socially crippled adulthood, unable to deal with the real world. The other, of course, involves orcs."

http://www.amptoons.com/blog/archives/2009/03/29/quote-8/

Justice and accountability, the American way (via Reddit)

1. The hospital makes a medical error; patient dies.
2. The hospital falsifies the medical record, including the testimony of the doctor given during the investigation.
3. The doctor who gave the testimony produces the original copy.
4. The hospital settles out of the court. The article does not say this, but I bet that the people who were involved in hiding the evidence probably still work for that hospital.

“There isn’t going to be a trial,” he said. “The hospital offered the patient’s family an 8-figure settlement, and they have accepted.”

http://open.salon.com/blog/amytuteurmd/2009/03/30/they_killed_my_patient_then_they_tried_to_hide_it

Malevich hits 1000th code review!

We've been using it for 3 months now in our team of roughly 45 people - 20 devs and 25 testers. This week we've had 1000th code review completed.

Here are a few stats, as of right now:

1008 change lists
17081 file versions
2863 review iterations
7851 comments

If you work at Microsoft and would like to try Malevich, I've set up a test change list here: http://sergeysomsg2/Malevich/default.aspx?cid=299. You can add yourself as a reviewer, leave comments in files, and submit review iterations.

If you're outside Microsoft and want to try it, the source and installation instructions are here: http://www.codeplex.com/Malevich.

Monday, March 30, 2009

Welcome to the ultimate in banana republics! (via Reddit)

Written by a former chief economist at IMF.

"Typically, these countries are in a desperate economic situation for one simple reason—the powerful elites within them overreached in good times and took too many risks. Emerging-market governments and their private-sector allies commonly form a tight-knit—and, most of the time, genteel—oligarchy, running the country rather like a profit-seeking company in which they are the controlling shareholders. When a country like Indonesia or South Korea or Russia grows, so do the ambitions of its captains of industry. As masters of their mini-universe, these people make some investments that clearly benefit the broader economy, but they also start making bigger and riskier bets. They reckon — correctly, in most cases — that their political connections will allow them to push onto the government any substantial problems that arise."

(emphasis mine)

http://www.theatlantic.com/doc/print/200905/imf-advice

Sunday, March 29, 2009

Empire strikes forward

It seems that vast majority of Star Wars technology was designed and built by the marketing department. Here are a few engineering suggestions that could help the bad guys win in Epsiodes -3 to 0...

1) Use firearms. Blasters are slow, not very powerful, and easily deflected by a light saber. An AK-47 would be far, far more effective. And don't forget the training! Despite the advantage in fire power, the storm troopers clearly can't shoot straight. Take them out to a range once in a while, and see how much more effective they become only after a couple training sessions!

2) If you use droids to fight your wars, build the best kind - do not waste time and money on anything else. It looks like the Destroyer model is more effective than50 units of the other kind (and even more effective than a Jedi). I doubt it could be more than 3 times more expensive. Why not just build Destroyers?

3) And while we are on this subject, let's have droids use more effective communication channels than human speech when they talk to each other. The same goes for all interfaces between the droids and everything else - spaceships, vehicles, and weapons. There is no reason while a droid should press a button to fire a weapon - a wireless interface would control it far more efficiently. Also, do invest in computerized targeting for your weapons. If your droids can already see, making them very accurate shooters is a relatively trivial task - ask any engineer.

4) Missiles seem to be an extremely effective weapon in Star Wars, except that there are very few of them. Why not build more of them and launch, say, 20 or 30 at a time? I doubt the Jedi, who have visible trouble dealing with one or two, would be able to escape ten. Also, consider building missiles that are FASTER than the star fighters they attack.

5) Consider using encryption. It looks like anyone can plug into the system anywhere and take control of everything in the space ships. This problem has been solved years ago! It really is not that hard.

6) Pack animals are slow, can't carry much, and are very, very hard to maintain. They need food, living compartments, etc. Consider eliminating them from the army in favor of mechanized transporters. Animals are not very effective in executions either - this has been proven many, many times. Instead of staging elaborate shows with giant predators that always fail to kill the prisoners, just shoot your captives, and put their heads on a stick.

7) I cannot overemphasize the importance of conventional firearms. If the probe sent to assassinate Princess Amidala had used a regular rifle instead of the poisonous centipedes, the subsequent events might have taken a very different turn. And in space, consider using nuclear weapons. The laser guns you currenlty mount on your ships are massively underpowered.

8) Did you know that space ships do not need wings to fly in space? Once you get entirely comfortable with this (yes, I do know it's extremely hard, given that they even make fying noises while moving through vacuum, but making this leap of faith might be crucial to your survival - do it!) you can start using very different design paradigms - like, for example, minimizing the surface area so your spacecraft is easier to protect and harder to target.

9) On the subject of spaceship designs - there must be something in the way you build them that makes them explode after they are hit. Unfortunately, this applies to all other vehicles as well. While this generates impressive visuals, this design point leads to unnecessary casualties among your troops. Consider enclosing whatever it is that explores in extra layers of protection. You have already solved this for your small weapons, which do not seem to have the same problem. Why not use similar design everywhere?

10) This is more of a tactical rather than an engineering advice, but I will give it anyway. Instead of deploying the Walkers and other weird ground assault vehicles, consider attacking from the air to suppress the adversary's ground troops.

Follow these simple steps, and you will be invincible - at least as long as the Good Guys(TM) are controlled by the marketing people.

Saturday, March 28, 2009

Free as in beer!

The GNU connotation of software as a form of speech never made any sense to me.

"Free speech" means the freedom of a person to express himself or herself without a fear of persecution. It does not mean the freedom to use the results of this expression by everyone.

Quite the contrary - the literary works by the greatest writers are the results of exercising free speech. But rip "Harry Potter", and you will - deservedly - find yourself slapped with a lawsuit, a fine, and maybe a jail sentence.

The free software movement has produced a lot of great things - from GNU development tools to Linux, BSD, Apache, and Firefox. It has also produced legions of activists that bring in the level of politics, acrimony, and fanboyism that is unbecoming to honorable competition in an area that is as much a science as it is an art.

By doing so they actually take the freedom away from people who chose to distribute the results of their work through channels that are different from the ones favored by the Free Software Foundation.

So here's my variant of "free software". The code distributed on this blog is free - free as in beer. You can redistribute it in full or in parts, with or without source code. You can use it in any derivative work without giving me any credit.

//-----------------------------------------------------------------------
// <copyright>
// Copyright (C) Sergey Solyanik.
//
// This software is in public domain and is "free as in beer". It can be
// redistributed in full or in parts for free and without any preconditions.
// </copyright>
//-----------------------------------------------------------------------

De-gnoming your blog

My blog has been filling up with spam over the last few months - the nasty, long messages that pollute the comment space and are a pain to delete because the bastards were programmatically sticking them in tens and tens of posts.

Luckily, Google has APIs to Blogger, and better yet, they even have C# libraries than make coding against these API easy. I didn't even have to dust off Eclipse!

So an hour later I had the program that allowed me to clean up the spam quickly.

The code sucks in the entire blog (including the abstracts of posts and comments) and allows one to quickly search and delete spam. Type 'help' on the command prompt, and it will explain the usage.

A few convenient shortcuts:

List or delete all comments from someone:
list|delete comments by name
e.g.
list comments by peter.w

List or delete all comments that have the same author as well as the text:

list|delete comments like sample_comment_uri
e.g.
list comments like http://www.blogger.com/feeds/3554166144204741789/3010398536200323405/comments/default/4945114575786176865

List or delete all comments with a phrase in title:
list|delete comments with text_string
e.g.
list comments with wow gold

List comments in a blog or a message:
list comments in blog_name|first_words_of_post_title
e.g.
list comments in Back to Microsoft
list comments in 1-800-MAGIC


If you're one of the truly insane types that would run code compiled by a random guy from the Internet, the built version is here: http://www.solyanik.com/drop/BlogDeSpammer.zip. Otherwise, the code is below, free as in beer!

Incidentally, it might make a good primer on how to retrieve and manipulate Blogger posts and comments programmatically.


//-----------------------------------------------------------------------
// <copyright>
// Copyright (C) Sergey Solyanik.
//
// This software is in public domain and is "free as in beer". It can be
// redistributed in full or in parts for free and without any preconditions.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Google.GData.Client;

namespace BlogDeSpammer
{
/// <summary>
/// Comment class. Just a trivial holder of essential comment data.
/// </summary>
class Comment
{
public string Text;
public string Author;
public string EditUri;

public Comment(string text, string author, string editUri)
{
Text = text;
Author = author;
EditUri = editUri;
}
}

/// <summary>
/// Post class. Just a trivial holder of essential post data.
/// </summary>
class Post
{
public string Title;
public string CommentsFeed;
public ICollection<Comment> Comments;

public Post(string title, string commentsFeed)
{
Title = title;
CommentsFeed = commentsFeed;
Comments = null;
}
}

/// <summary>
/// Blog class. Just a trivial holder of essential blog data.
/// </summary>
class Blog
{
public string Name;
public string Feed;
public ICollection<Post> Posts;
public Blog(string name, string feed)
{
Name = name;
Feed = feed;
Posts = null;
}
}

/// <summary>
/// Main functionality.
/// </summary>
class Program
{
/// <summary>
/// Gets collection of blogs belonging to the user.
/// </summary>
/// <param name="blogger"> Blogger service. Must be initialized with the parameters.
/// </param>
/// <returns> Collection of blogs. </returns>
private static ICollection<Blog> GetBlogs(Service blogger)
{
List<Blog> blogs = new List<Blog>();

FeedQuery query = new FeedQuery();
query.Uri = new Uri("http://www.blogger.com/feeds/default/blogs");
AtomFeed results = blogger.Query(query);
while (results != null && results.Entries.Count > 0)
{
foreach (AtomEntry entry in results.Entries)
{
string blogFeedLink = null;
foreach (AtomLink link in entry.Links)
{
if (BaseNameTable.ServiceFeed.Equals(link.Rel))
{
blogFeedLink = link.HRef.ToString();
break;
}
}
blogs.Add(new Blog(entry.Title.Text, blogFeedLink));
}

if (results.NextChunk == null)
break;

query.Uri = new Uri(results.NextChunk);
results = blogger.Query(query);
}

return blogs;
}

/// <summary>
/// Retrieves all the posts for the blog.
/// </summary>
/// <param name="blogger"></param>
/// <param name="blog"></param>
/// <returns> Collection of posts. </returns>
private static ICollection<Post> GetPosts(Service blogger, Blog blog)
{
List<Post> posts = new List<Post>();

FeedQuery query = new FeedQuery();
query.Uri = new Uri(blog.Feed);
AtomFeed results = blogger.Query(query);
while (results != null && results.Entries.Count > 0)
{
foreach (AtomEntry entry in results.Entries)
{
string commentsFeedLink = null;
foreach (AtomLink link in entry.Links)
{
if ("replies".Equals(link.Rel) &&
"application/atom+xml".Equals(link.Type))
{
commentsFeedLink = link.HRef.ToString();
break;
}
}
Console.Write(".");
if (commentsFeedLink != null)
posts.Add(new Post(entry.Title.Text, commentsFeedLink));
}

if (results.NextChunk == null)
break;

query.Uri = new Uri(results.NextChunk);
results = blogger.Query(query);
}

return posts;
}

/// <summary>
/// Gets comments for the given post.
/// </summary>
/// <param name="blogger"> Blogger service, initialized with credentials. </param>
/// <param name="post"> The post. </param>
/// <returns> Collection of credentials. </returns>
private static ICollection<Comment> GetComments(Service blogger, Post post)
{
List<Comment> comments = new List<Comment>();

FeedQuery query = new FeedQuery();
query.Uri = new Uri(post.CommentsFeed);
AtomFeed results = blogger.Query(query);
while (results != null && results.Entries.Count > 0)
{
foreach (AtomEntry entry in results.Entries)
{
Console.Write(".");
comments.Add(new Comment(entry.Title.Text, entry.Authors[0].Name,
entry.EditUri.ToString()));
}

if (results.NextChunk == null)
break;

query.Uri = new Uri(results.NextChunk);
results = blogger.Query(query);
}
return comments;
}

/// <summary>
/// Main. Does all the work.
/// </summary>
/// <param name="args"> Command line parameters. </param>
static void Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Usage: blogdespammer username password");
return;
}

string username = args[0];
string password = args[1];

Console.Write("Loading");

Service blogger = new Service("blogger", "BlogDeSpammer");
blogger.Credentials = new GDataCredentials(username, password);

ICollection<Blog> blogs = GetBlogs(blogger);
foreach (Blog blog in blogs)
{
blog.Posts = GetPosts(blogger, blog);
foreach (Post post in blog.Posts)
post.Comments = GetComments(blogger, post);
}

Console.WriteLine("\n\nType 'help' for help, 'exit' to quit.");
for (; ; )
{
Console.Write("> ");
string input = Console.ReadLine();
if (input.Equals("exit", StringComparison.CurrentCultureIgnoreCase))
break;

if (input.Equals("help", StringComparison.CurrentCultureIgnoreCase))
{
Console.WriteLine(
"Type:\n\texit\n\t\t-- to quit.\n\thelp\n\t\t--to get this text.\n");
Console.WriteLine(
"\tlist blogs\n\t\t-- to list available blogs.\n");
Console.WriteLine(
"\tlist posts in blog\n\t\t-- to list posts in a blog.\n");
Console.WriteLine(
"\tlist comments in blog\n\t\t-- to list comments in a blog.\n");
Console.WriteLine(
"\tlist comments in post\n\t\t-- to list comments in a post.\n");
Console.WriteLine(
"\tlist comments by author\n\t\t-- to list comments by " +
"a particular author.\n");
Console.WriteLine(
"\tlist comments like commenturi\n\t\t-- to list comments " +
"identical to a comment.\n");
Console.WriteLine(
"\tlist comments with text\n\t\t-- to list comments " +
"containing text segment.\n");
Console.WriteLine(
"\tdelete comments by author\n\t\t-- to delete all " +
"comments by a particular author.\n");
Console.WriteLine(
"\tdelete comments like commenturi\n\t\t-- to delete " +
"all comments that are the same as a " +
"comment with this URI.\n");
Console.WriteLine(
"\tdelete comments with text\n\t\t-- to list comments " +
"containing text segment.\n");
Console.WriteLine("Names above could be either text names or URIs.");
continue;
}

if (input.Equals("list blogs",
StringComparison.CurrentCultureIgnoreCase))
{
foreach (Blog blog in blogs)
Console.WriteLine("{0} ({1})", blog.Name, blog.Feed);
continue;
}

if (input.StartsWith("list posts in ",
StringComparison.CurrentCultureIgnoreCase))
{
string blogname = input.Substring(14);
foreach (Blog blog in blogs)
{
if (blog.Name.StartsWith(blogname) ||
blog.Feed.Equals(blogname))
{
foreach (Post post in blog.Posts)
Console.WriteLine("{0} ({1})", post.Title,
post.CommentsFeed);
}
}
continue;
}

if (input.StartsWith("list comments in ",
StringComparison.CurrentCultureIgnoreCase))
{
string name = input.Substring(17);
foreach (Blog blog in blogs)
{
if (blog.Name.StartsWith(name) || blog.Feed.Equals(name))
{
foreach (Post post in blog.Posts)
{
Console.WriteLine("{0} {1}", post.Title,
post.CommentsFeed);
foreach (Comment comment in post.Comments)
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
break;
}

foreach (Post post in blog.Posts)
{
if (post.Title.StartsWith(name) ||
post.CommentsFeed.Equals(name))
{
Console.WriteLine("{0} {1}", post.Title,
post.CommentsFeed);
foreach (Comment comment in post.Comments)
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
}
}
continue;
}

if (input.StartsWith("list comments by ",
StringComparison.CurrentCultureIgnoreCase))
{
string author = input.Substring(17);
foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
foreach (Comment comment in post.Comments)
{
if (comment.Author.Equals(author,
StringComparison.CurrentCultureIgnoreCase))
{
Console.WriteLine("{0} : {1}",
blog.Name, post.Title);
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
}
}
}
continue;
}

if (input.StartsWith("list comments with ",
StringComparison.CurrentCultureIgnoreCase))
{
string text = input.Substring(19);
foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
foreach (Comment comment in post.Comments)
{
if (comment.Author.Contains(text) ||
comment.Text.Contains(text))
{
Console.WriteLine("{0} : {1}",
blog.Name, post.Title);
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
}
}
}
continue;
}

if (input.StartsWith("list comments like ",
StringComparison.CurrentCultureIgnoreCase))
{
string prototypeUri = input.Substring(19);
Comment prototypeComment = null;

foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
foreach (Comment comment in post.Comments)
{
if (prototypeUri.Equals(comment.EditUri))
{
prototypeComment = comment;
goto found;
}
}
}
}
continue;

found:
foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
foreach (Comment comment in post.Comments)
{
if (comment.Author.Equals(prototypeComment.Author,
StringComparison.CurrentCultureIgnoreCase) &&
comment.Text.Equals(prototypeComment.Text))
{
Console.WriteLine("{0} : {1}", blog.Name, post.Title);
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
}
}
}
continue;
}

if (input.StartsWith("delete comments by ",
StringComparison.CurrentCultureIgnoreCase))
{
string author = input.Substring(19);

foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
List<Comment> deleted = new List<Comment>();

foreach (Comment comment in post.Comments)
{
if (comment.Author.Equals(author,
StringComparison.CurrentCultureIgnoreCase))
{
Console.WriteLine("{0} : {1}", blog.Name, post.Title);
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
blogger.Delete(new Uri(comment.EditUri));
deleted.Add(comment);
}
}

foreach (Comment comment in deleted)
post.Comments.Remove(comment);
}
}
continue;
}

if (input.StartsWith("delete comments like ",
StringComparison.CurrentCultureIgnoreCase))
{
string prototypeUri = input.Substring(21);
Comment prototypeComment = null;

foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
foreach (Comment comment in post.Comments)
{
if (prototypeUri.Equals(comment.EditUri))
{
prototypeComment = comment;
goto found;
}
}
}
}
continue;

found:
foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
List<Comment> deleted = new List<Comment>();

foreach (Comment comment in post.Comments)
{
if (comment.Author.Equals(prototypeComment.Author,
StringComparison.CurrentCultureIgnoreCase) &&
comment.Text.Equals(prototypeComment.Text))
{
blogger.Delete(new Uri(comment.EditUri));
deleted.Add(comment);
Console.WriteLine("{0} : {1}", blog.Name, post.Title);
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
}
foreach (Comment comment in deleted)
post.Comments.Remove(comment);
}
}
continue;
}

if (input.StartsWith("delete comments with ",
StringComparison.CurrentCultureIgnoreCase))
{
string text = input.Substring(21);
foreach (Blog blog in blogs)
{
foreach (Post post in blog.Posts)
{
List<Comment> deleted = new List<Comment>();

foreach (Comment comment in post.Comments)
{
if (comment.Author.Contains(text) ||
comment.Text.Contains(text))
{
blogger.Delete(new Uri(comment.EditUri));
deleted.Add(comment);
Console.WriteLine("{0} : {1}", blog.Name, post.Title);
Console.WriteLine("-- {0} ({1})\n{2}\n",
comment.Author, comment.EditUri,
comment.Text);
}
}

foreach (Comment comment in deleted)
post.Comments.Remove(comment);
}
}
continue;
}

Console.WriteLine(
"Can't parse. Please use 'help' to look up valid commands.");
}


Console.WriteLine("Done!");
}
}
}

Thursday, March 26, 2009

Funny quote: arguing with idiots

"Never argue with an idiot. They bring you down to their level and beat you with experience."

Source unknown.

Monday, March 23, 2009

Death of journalism

So this is what passes for publishable news these days - Paul Krugman writes two pages in NYT Opinions section (http://www.nytimes.com/2009/03/23/opinion/23krugman.html?_r=1), and Reuters immediately quotes half of it on its web site: http://www.reuters.com/article/GCA-CreditCrisis/idUSTRE52M4SS20090323.

Guess which one of them was cross-linked from Reddit? You guessed right - the Reuters one.

What should have been a link to the original article, has become a copycat in its own right. A page was added to the Web. A "journalist" was paid for it. The reporting standards went down one notch further. The life continues...

Sunday, March 22, 2009

The big takeover (via Reddit)

"These people need their trips to Baja, their spa treatments, their hand jobs," says an official involved in the AIG bailout, a serious look on his face, apparently not even half-kidding. "They don't function well without them."

http://www.rollingstone.com/politics/story/26793903/the_big_takeover/print

If you thought iFart and iBoobs were weird...

...behold the Cylon Detector iPhone App!

http://www.scifi.com/battlestar/iphone/

Hooray for long American tradition for consumer-driven innovation!
----
Thought about it some more, and came to conclusion that the consumers of this stuff are no weirder than Sarah Palin voters after all :-)...

Friday, March 20, 2009

On hacking cute shiny objects (via Reddit)

"Safari on the Mac is easier to exploit. The things that Windows do to make it harder (for an exploit to work), Macs don’t do. Hacking into Macs is so much easier. You don’t have to jump through hoops and deal with all the anti-exploit mitigations you’d find in Windows.

It’s more about the operating system than the (target) program. Firefox on Mac is pretty easy too. The underlying OS doesn’t have anti-exploit stuff built into it."

http://blogs.zdnet.com/security/?p=2941

Thursday, March 12, 2009

New TFS documentation pushed

I blogged earlier (http://1-800-magic.blogspot.com/2009/02/everything-you-know-about-diffing-files.html) about how terrible TFS API documentation was.

This is no longer true - the latest round that just got pushed on MSDN is solid. Good job, team!

http://msdn.microsoft.com/en-us/library/bb130146.aspx

Monday, March 9, 2009

How to install TFS on a single domain-joined server

Since TFS documentation is so convoluted, I thought I would put together a SIMPLE list of what needs to be done.

Software required:

  • Windows Server 2008 Standard

  • SQL Server 2008 Standard Edition

  • Sharepoint Server 2007 WITH SP1

  • TFS 2008

  • TFS 2008 SP1



All this software is available from MSDN.

Also: one domain account different from the installing user. Should NOT have admin rights. Should NOT be a local account. This is very important.

Prework:
Prepare TFS 2008 slipstreamed with SP1 as follows:

  1. Copy the contents of TFS DVD AT folder to a directory, say c:\tfs\base:
        xcopy /die d:\at c:\tfs\base

  2. Unpack the SP1 installer to a different directory, say c:\tfs\sp1:
        en_visual_studio_team_system_2008_team_foundation_server_service_pack_1_x86_x64wow.exe /extract:c:\tfs\sp1

  3. Slip-stream the sp1:
        msiexec /a c:\tfs\base\vs_setup.msi /p TFS90sp1-KB949786.msp TARGETDIR=c:\tfs\slipstream

  4. Create ISO and burn it on a DVD using ISO Recorder (http://isorecorder.alexfeinman.com/W7.htm)



Installation steps:

You must be logged as a domain user and have admin rights to the machine.

If you are installing on a VM, take snapshots between the installation steps, so if something goes wrong, you can restart easily.


  1. Install Server 2008. Configure, join the domain, apply all updates. Reboot if necessary.

  2. Add Web Server (IIS) role from Server Manager->Roles->Add roles. Select all components.

  3. Install Windows Sharepoint Server 2007 SP1 using all defaults (non-SP1 build will not install on Server 2008).

  4. Install SQL Server 2008.
    • Select all components to install.

    • On the page that allows you to specify the instance name, select the default instance.

    • On service accounts page, specify "Network Service" everywher (where it's editable) and make all services that can be made autostart do so.

    • Ensure Windows authentication is selected (the default), and click "Add current user" to administrators of the computer on the two pages which ask you to specify the admin user (one for SQL, one for analysis services).

    • On the page that asks you about configuring reporting, select "Install, but leave Reporting Services unconfigured" option.

  5. Install TFS.
    • Do not change the default SQL instance name (it will be your computer name - definitely DO NOT switch it to '.').

    • Use "Network Service" where it allows you to (the default), and specify that non-admin user account from pre-requisitive section on the next page.

    • The installation will also ask you for the sharepoint admin URL. The default is incorrect. You can find the right port by opening the sharepoint admin link from the start menu.

  6. RESTART THE SERVER. TFS does not prompt you to do this, but the permissions are synchronized on the service restart, so you won't be able to create a new project until you do this.



You're done.

Ensure that the client computer has Team Explorer that is upgraded to SP1 (creating project will fail in misterious ways if server has SP1 and client does not), and create a new project to test the newly installed system.

Adventures in TFS, continued

I was chronicling my attempt to install TFS on a VM at home so I have a better platform for fixing bugs related to that source control system. The beginning of this adventure is here: http://1-800-magic.blogspot.com/2009/03/adventures-in-tfs.html.

I should have given up on this thing a long, long time ago, but it started feeling like a technical challenge. One man vs. TFS. "The old man and the sea" of sorts. Although there the challenge was much less wanton :-).

Day 7. Create a new VM. This time I take a snapshot in Hyper-V every time something new gets installed.

  • Windows Server 2008 + all updates

  • Snapshot

  • Sharepoint Server 2007 SP1

  • Snapshot

  • SQL Server 2008 Standard

  • Snapshot


This time around TFS setup crashes after complaining that a command with half-a-page worth of command line options returned a non-zero result. Looking at winerror.h, the returned result is an unhandled exception. When I run the same command from a prompt (lots of retyping!), the exception has something to do with SQL reporting services.

At this point I give up and start reading the manual. TFS installation manual is an exercise in hyper-linking, forming a complex graph with many cycles in it. Instead of having 2-3 lists (if you are creating a single server deployment, do this, this, and this), it is an unwieldy bowl of spaghetti docs, each step cross-linking at least 5 more subtopics.

After spending 30 minutes down this maze of twisty little passages, all alike, the only difference I can find is about installing SQL server with unconfigured reporting services.

I roll back to the checkpoint after sharepoint, and this time I choose "Install but not configure reporting services".

TFS install can now complete, but I am back to the square one - trying to create the project in Visual Studio does not work - again, the error message is vague, something about reporting services.

Day 8.

I suddenly remember that I applied Visual Studio SP1 BEFORE I installed TFS client on my client machine. Everything worked so far just editing/checking files in, but I decide to reinstall SP1 just to make sure.

An hour and a half later - yes, this is how long it takes VS SP1 to install - everything works! That was the problem...

Thursday, March 5, 2009

Adventures in TFS

Now that more and more teams are using Malevich (http://www.codeplex.com/Malevich), I am getting more and more TFS users. TFS had become quite popular at Microsoft because it integrates very nicely with Visual Studio, and of course DevDiv is using it (as they should) for dogfood.

TFS has many idiosyncrasies that Perforce does not, so occasionally I am getting bug reports for things that work nicely in Perforce/Source Depot, but not in TFS.

For a while, I was using CodePlex's Malevich enlistment for fixing bugs, but now that the bugs had become harder and harder to reproduce and require more and more setup, I decided to install my own version of TFS.

One thing that most TFS developers usually don't have to do, is TFS setup. Perforce is simple, really simple. You just copy an executable to a computer and run it - that's all. Well, you do have to open a port in the firewall, then the first time you log in, set up an admin account.

Here's what I had to go through to get TFS to work. Of course I was only working at home, at night, so a day is not really a day - it's just a few hours, sometimes even less.

Day 1. Try to install TFS on one of my servers. Discover that it does not work on 64-bit machines (huh???).

Day 2. Try to install TFS on one of my 32-bit servers. The setup at least starts now. If I use MYCOMPUTERNAME as a SQL Server target (the default instance is SQL 2008 on that box), the setup dies with many complaints about SQL Full Text Search not being installed/started/set up to start automatically. If I use '.' as an instance name, the installation goes through - up to the point where it wants a Sharepoint server. I don't have the Sharepoint server.

Day 3. I am turning on Hyper-V on one of my bigger servers. It's a dual Xeon5030 box, so 4 cores. Nice machine. Server 2008 installs almost instantaneously in Hyper-V, and the speed - I can't tell the difference. I also install SQL Server 2008.

Day 4. I have Sharepoint 2007 from MSDN, but it does not install on Server 2008. Need SP1. So I download Sharepoint 2007 with SP1. Install it. Trying to install TFS. No luck - the installation fails because it cannot find SQL Reporting Services. I am verifying - yes, reporting services are installed.

Day 5. I realize that when I use '.' it is finding a SQL 2005 instance brought in by Sharepoint. When I use MACHINENAME, it is finding SQL 2008 instance that I installed for it. SQL 2005 (that instance) does not have reporting services. SQL 2008 does not have Full Text Search as a separate service, so TFS setup is incapable to detect it. Google says I need SP1 for TFS 2008 to install on SQL Server 2008.

Day 6. Checking MSDN - MSDN does not have TFS with slip-streamed SP1. Google gets me an article that describes how to do it myself. The article is more than a bit imprecise, so I am reproducing the actual steps here:
  • Copy the contents of TFS DVD AT folder to a directory, say c:\tfs\base (xcopy /die d:\at c:\tfs\base).

  • Unpack the SP1 installer to a different directory, say c:\tfs\sp1 ( en_visual_studio_team_system_2008_team_foundation_server_service_pack_1_x86_x64wow.exe /extract:c:\tfs\sp1). Why does this have x64wow in its name despite the fact that it won't work on x64, wow or no wow - no idea.

  • Slip-stream the sp1 (msiexec /a c:\tfs\base\vs_setup.msi /p TFS90sp1-KB949786.msp TARGETDIR=c:\tfs\slipstream)


You're done, the slipstream directory contains TFS 2008 SP1. The rest of the stuff on the TFS DVD (build, TE) is not slip-streamable, so just the TFS part is what you need. Make an image from this directory and voila - this will actually install on Server 2008 with SQL Server 2008!

I am trying to now create a new TFS project. I can connect to the server, but when I do try to create a project, it fails.

TF30004: The New Team Project Wizard encountered an unexpected error while initializing the Microsoft.ProjectCreationWizard.Reporting plug-in.

Explanation
TF30171: The Microsoft.ProjectCreationWizard.Reporting plug-in used to create the new team project could not be initialized and returned the following error: TF30224: Failed to retrieve projects from the report server. Please check that the SQL Server Reporting Services Web and Windows services are running and you have sufficient privileges for creating a project..

User Action
Contact your Team Foundation Server administrator.


I am trying to investigate this. It turns out that I have no access to SQL Reporting services - I've installed SQL server as a local admin, and despite the fact that I've added Domain Admins, my user name, and everything to the admins, it did not work.

I decide that the easiest thing would be to just uninstall everything, and reinstall it from a normal user account.

Unfortunately, I start uninstallation from SQL Server. After I uninstall SQL Server, TFS installer crashes on uninstall. It looks like the VM is toast...

To be continued...

Tuesday, March 3, 2009

A very wonderful opportunity...

...has just presented itself in the form of you potentially paying $1000 for 4 gigs of RAM:

http://store.apple.com/us/configure/MB419LL/A?mco=NDE4Mzg5MA

Also, did you know that FileMaker is "#1 name in databases"? You do now!

Sunday, March 1, 2009

Just how dumb are we, really?

http://www.gallup.com/poll/116065/Americans-Views-Bank-Takeovers-Appear-Fluid.aspx

Gallup poll asks half of their polling sample whether they are for or against temporary "takeover" of the banks, and the other half whether they are for or against temporary "nationalization". The rest of the question is identical.

On the takover question: 54% for, 44% against, 3% no opinion.

On the nationalization question: 37% for, 57% against, 6% no opinion.

These people are allowed to vote?

Saturday, February 21, 2009

Leading scientists still reject God

"You clearly can be a scientist and have religious beliefs. But I don't think you can be a real scientist in the deepest sense of the word because they are such alien categories of knowledge."

http://www.stephenjaygould.org/ctrl/news/file002.html

Eventually we must stop the stupid blabber about how religion and science are compatible, and just admit that they are not.

Wednesday, February 18, 2009

Bleak outlook for press accuracy

This is among today's headlines in NYTimes:


I was wondering if "unusually gradual and prolonged period of recovery" is new Fedspeak for a "big, terrible depression". So I searched the speech, which was helpfully linked off the same headline:
http://www.federalreserve.gov/newsevents/speech/bernanke20090218a.htm.

The speech contains no words "unusually", "gradual", or "prolonged". It does contain the word "and".

Tuesday, February 17, 2009

Meet the new boss, part II

"Hertz's statement mirrors a statement from Obama White House Counsel Gregory Craig published Saturday.

"The president is very sympathetic to those who want to find out what happened," Craig told The Washington Post. "But he is also mindful as president of the United States not to do anything that would undermine or weaken the institution of the presidency. So, for that reason, he is urging both sides of this to settle.""

(Emphasis mine)

http://rawstory.com/news/2008/Obama_not_Bush_now_seeking_delay_0217.html

I thought we elected this guy because we wanted a president, not an emperor? Actually, hold this thought. We elected him because we were scared - again. Except this time we were not afraid of the terrorists, but for our money.

America, the land of the brave...

Sunday, February 15, 2009

Recession as an effective garbage collector

"The diminishment has not, God knows, been quantitative. Never has there been so much product. Never has the American art world functioned so efficiently as a full-service marketing industry on the corporate model.

Every year art schools across the country spit out thousands of groomed-for-success graduates, whose job it is to supply galleries and auction houses with desirable retail. They are backed up by cadres of public relations specialists — otherwise known as critics, curators, editors, publishers and career theorists — who provide timely updates on what desirable means."

http://www.nytimes.com/2009/02/15/arts/design/15cott.html?em

I hate HTML

Passionately!

Especially the table layout. This thing was conceived by total morons. I would build a memorial to the authors of this abomination with inscription: "Spit here!"

Consider this:

<style>
.CssTable
{
table-layout: fixed;
width: 750px;
border: solid black 1 px;
}

.CssCell1
{
width: 150px;
}
.CssCell2
{
width: 600px;
}
</style>
<table class = "CssTable">
<tr><td class = "CssCell1">Cell 1</td>>td class="CssCell2">Cell 2</td></tr>
</table>

It does the right thing: the table is 750 pixels, the first column is 150 and the second one is 600. All is well. Now insert this line right after the table tag (as the first row):

<tr><td colspan=2>Long line</td></tr>

...and all the alignemnt just goes to hell. The columns become equally sized. Why?!

There is a solution - it is to create table header and set its sizes to what the desired sizes of columns are. Still, WTF?

Saturday, February 14, 2009

The biggest experiment in compensation rules

Now that hundreds of banks had the compensation for their highest paid people severely limited, we will see how would that translate into financial performance of these institutions.

http://www.washingtonpost.com/wp-dyn/content/article/2009/02/13/AR2009021303288.html?hpid=topnews

My bet - they will do either the same or better for shareholders.

Friday, February 13, 2009

Everything you know about diffing files in TFS

MSDN support for TFS is ridiculous - it is essentially an automated dump of class structure with no information whatsoever. Shame on you TFS PMs/management for releasing a product with no documentation. I thought we were a developer-oriented company!

EDIT 3/12/09 This is no longer true - MSDN has pushed the new round of TFS docs and they are great. Excellent job!

Buck Hodges has a couple of great posts, however, that saved the day for me:

We're so lucky to live in these enlightened times...

...in an enlightened country!

For example, they used to burn you at the stake for witchcraft. Now - you just get expelled from college...

http://www.clearwater.edu/The_Guide.pdf

Happy 1234567890!

http://en.wikipedia.org/wiki/Unix_time

Thursday, February 12, 2009

Bi-partisanship

"As someone who appeared numerous times on the 700 Club with Pat Robertson, as someone for whom Jerry Falwell used to send his private jet to bring me to speak at his college, as an author who had James Dobson giveaway 150,000 copies of my one of my fundamentalist "books" allow me to explain something: the Republican Party is controlled by two ideological groups. First, is the Religious Right. Second, are the neoconservatives. Both groups share one thing in common: they are driven by fear and paranoia. Between them there is no Republican "center" for you to appeal to, just two versions of hate-filled extremes."

(Emphasis mine.)

http://www.huffingtonpost.com/frank-schaeffer/an-open-letter-to-preside_b_165359.html

Incidentally, below is a screen shot of commentary that appeared on a right-wing talk show's website following the news that an "anti-racist activist" (sic) was attacked by a black man. Check it out - is is enlightening.

So if you do consider youself a "moderate" Republican, think about who are the people that you rely on to get your agenda passed. Just like in the early thirties in Germany, these forces might turn out to be bigger than you...

Tuesday, February 10, 2009

Meet the new boss...

"the new administration -- the new DOJ -- had actively reviewed this case and vetted the Bush positions and decisively opted to embrace the same positions."

http://www.salon.com/opinion/greenwald/2009/02/09/state_secrets/index.html

Sunday, February 8, 2009

How much does a poor investment banker need to just get by?

I asked this question yesterday, and - lo and behold - today's New York Times has an answer! I am generally not a big fan of long quotes, but this is so surreal, that I see no way around.

The whole story is here: http://www.nytimes.com/2009/02/08/fashion/08halfmill.html?_r=1.

So these are the basic expenses:

"Barbara Corcoran, a real estate executive, said that most well-to-do families take at least two vacations a year, a winter trip to the sun and a spring trip to the ski slopes.

Total minimum cost: $16,000.

A modest three-bedroom apartment, she said, which was purchased for $1.5 million, not the top of the market at all, carries a monthly mortgage of about $8,000 and a co-op maintenance fee of $8,000 a month. Total cost: $192,000. A summer house in Southampton that cost $4 million, again not the top of the market, carries annual mortgage payments of $240,000.

Many top executives have cars and drivers. A chauffeur’s pay is between $75,000 and $125,000 a year, the higher end for former police officers who can double as bodyguards, said a limousine driver who spoke anonymously because he does not want to alienate his society customers.

“Some of them want their drivers to have guns,” the driver said. “You get a cop and you have a driver.” To garage that car is about $700 a month.

A personal trainer at $80 an hour three times a week comes to about $12,000 a year.

The work in the gym pays off when one must don a formal gown for a charity gala. “Going to those parties,” said David Patrick Columbia, who is the editor of the New York Social Diary (newyorksocialdiary.com), “a woman can spend $10,000 or $15,000 on a dress. If she goes to three or four of those a year, she’s not going to wear the same dress.”

Total cost for three gowns: about $35,000.

Not every bank executive has school-age children, but for those who do, offspring can be expensive. In addition to paying tuition, “You’re not going to get through private school without tutoring a kid,” said Sandy Bass, the editor of Private School Insider, a newsletter that covers private schools in the New York City area. One hour of tutoring once a week is $125. “That’s the low end,” she said. “The higher end is 150, 175.” SAT tutors are about $250 an hour. Total cost for 30 weeks of regular tutoring: $3,750.

Two children in private school: $64,000.

Nanny: $45,000.

Ms. Bass, whose husband is an accountant with many high-end clients, said she spends about $425 every 10 days on groceries for her family. Annual cost: about $15,000.

More? Restaurants. Dry cleaning. Each Brooks Brothers suit costs about $1,000. If you run a bank, you can’t look like a slob.

The total costs here, which do not include a lot of things, like kennels for the dog when the family is away, summer camp, spas and other grooming for the human members of the family, donations to charity, and frozen hot chocolates at Serendipity, are $790,750, which would require about a $1.6-million salary to compensate for taxes. Give or take a few score thousand of dollars."

See how much it takes in living expenses just to gamble your money away? I think I start to understand how it came to be that so many heads were chopped off during French Revolution...

Saturday, February 7, 2009

Compensation and job performance

The opinion page at WSJ was always a steaming heap of crap, but it looks like under Murdoch the rest of the - once very respectable - paper is moving fast to close the gap. Here's the recent "gem": http://online.wsj.com/article/SB123396915233059229.html.

It starts with a tearful personal story of a former investment banker.

"I was a 35-year-old, nonpartner investment banker then and was horrified to learn that my annual take-home pay would be limited to my small salary, which accounted for about a quarter of my previous year's income. Fortunately the partners decided to pay a small bonus out of their capital that year to help employees like me get by. The next year was no better. Several colleagues with good prospects left the firm and the industry for good. We learned that strong pay-for-performance compensation incentives could cut both ways."

Which actually begs an outright question - how much was this "small salary" anyway? What does it take for an investment banker to "get by"? $200k? $300k? $1m?

This idiotic idea that once you start paying less, the smart people will either leave or automatically become dumber was somehow sold to our populace, but it is belied by an every day experience.

When a person comes to a job interview and his/her answer to the question "why are you in this industry" is "because it pays a lot", the interview ends right there, right? How many companies did you see where this would not be true?

Studies upon studies show that there is NO direct correlation between job performance and the salary. There is a lot of correlation between overall level of happiness, and a dollar spent on benefits achieves far more towards that end than a dollar spent on salary.

Google is an ideal case in point. It employs some of the top engineers, but it pays slightly below the industry average. Almost everyone who works at Google could get more money elsewhere. The same is true for Microsoft.

When I interviewed at Google, I had a competing offer from Wall Street. Between the much higher cash and bonus, it was twice as much as what I'd be making at Google - and 50% more than what I was making at Microsoft. I chose Google, because while the salary was important, job satisfaction was even more so.

When I went back to Microsoft, I could have gone to Wall Street instead, and, again, get more money. I am sure that my original offer was still standing. But I went back to Microsoft, not to Wall Street.

Now, to hell with us mortals. Here's what correlates best with the CEO salary, and it's not the company performance. It's the golf scores: http://www.cerge.cuni.cz/pdf/events/papers/090112_t.pdf.

Friday, February 6, 2009

Yes... Yes! Yes!!!

We should reflect on how broken our system of government is when more and more people start saying - please, take our money!

http://www.nytimes.com/2009/02/06/opinion/06hastings.html?_r=1

And this is what Warren Buffett had to say about this a bit earlier.

http://www.nytimes.com/2006/11/26/business/yourmoney/26every.html

Malevich stats

Malevich is a code review system that I wrote initially for our team because I was missing Monrian - a Google tool that I learned to love when I was there.

We've been using it quite successfully for a month and a half now. Here are a few stats, as of right now, just from my team (a few other teams at Microsoft started using it as well):
Change lists reviewed: 217
Review iterations: 706
Files reviewed: 2386
Comments made: 2968

You can read more about the project here: http://1-800-magic.blogspot.com/2009/01/malevich-introduction.html, or get the code and documentation here: http://www.codeplex.com/Malevich.

Sunday, February 1, 2009

Customizing TouchFlo 3d on HTC Fuze device

Fuze comes with a nice shell from HTC which replaces standard Windows Mobile Today screen. It functions as a collection of shortcuts to a bunch of applications and functions of the phone. Unfortunately, it is also used by AT&T (and/or other network operators) to push a bunch of usual OEM "extensions" on an unsuspecting customer - usually, solicitations to buy more services and software.

Luckily, there is a nice customization program that helps remove this crap. It is called Diamond TF3D Config, and is available from here: http://forum.xda-developers.com/showthread.php?t=405749.

Despite the fact that it is called "Diamond", it does appear to work - at least as far as removing the tab with AT&T stuff on it - on the Fuze device as well.

Next step - I need to figure out how to configure the Internet tab in TouchFlo so it does not go to their page...

Reprogramming push-to-talk (PTT) button on HTC Fuze

This will probably work on most other HTC devices (daimond, etc).

The PTT button is I think one of the stupidest creations in Windows CE world. It allows one to program a list of people who - at an extra cost - one would be able to phone with a single button.

Without passing the judgement on the usefulness of this service, the chief annoyance of it is that it has a specially dedicated button which is often very easy to push accidentally (on the Cingular 85xx device, for example, it is right next to the power button), and - the worst part - is not remappable. Unlike all other feature-related buttons.

This last one I completely fail to understand, and I really, really, really hope that we (Microsoft) didn't make it so. Because if we did, it would probably be - for me at least - the one of the most embarrassing features to ever come out of the Redmond campus, right there with Microsoft Bob.

Anyway, this is what one needs to do make the PTT button do something useful.
(1) Disable PTT service. To do this, modify device registry value "Dll" in HKLM\Services\PTT by appending ".sav" to the value of the registry ("PTTService.dll.sav"). This will prevent the PTT service from getting loaded - and will make it easy to restore should you ever want it back (just remove the ".sav").

If you have Visual Studio, you can use Remote Registry Editor in Visual Studio Remote Tools to edit the registry. Otherwise, google for "Windows CE Registry Editor". There are plenty of tools in existence.

(2) Open File Explorer on the device. Go to \Windows directory. Click-and-hold on voicecommand (you will have to scroll down a long, long list of files). Select "Copy" from the context menu. Then scroll down to the very bottom of the screen, and click-and-hold on the empty space. Select "Paste" from the context menu. Rename the resultant shortcut to "Short_PTT".

Note: this works only because voicecommand is already a shortcut. If you want to map an application (executable file) to the button, the instructions are as follows. Click-and-hold on the name of the application. Select "Copy". Then scroll down to the very bottom of the screen, and click-and-hold on the empty space. Select "Paste shortcut" from the context menu. Rename the resultant shortcut to "Short_PTT".

(3) Reboot the device to eradicate the PTT service. You are done.

Friday, January 23, 2009

E=MO2

"When I was a kid, I used to pray every night for a new bicycle. Then I realized that the Lord, in his wisdom, doesn't work that way. So I just stole one and asked him to forgive me!"

http://en.wikiquote.org/wiki/Emo_Philips

Wednesday, January 21, 2009

Idiocracy

"What happened to 30%? Lies!" shouted customer Gabriel Ifrah, 52, at the Circuit City on La Cienega Boulevard in Los Angeles on Monday, where most items were priced at 10% off.

...

"I've been waiting in line for half an hour each day based on employees' promises that prices could come down, but they haven't," said McGinness, a TV commercial producer from Los Angeles. "It's very disappointing."

http://www.chicagotribune.com/business/la-fi-liquidation20-2009jan20,0,497931.story

Is it me, or does this really sound insanely stupid?

By the way, if you need any proof that recession has not REALLY started yet, this is it. I mean, people waiting in line for cheap electronics?

DIY: Enriching uranium

http://www.amazon.com/Uranium-Ore/dp/B000796XXM/

Monday, January 19, 2009

Redirection with query parameters bug in IE

I really, really, really hate this browser.

Today's bug: if you do meta redirection to the same path with different query parameters, it ignores and passes the same parameters that the original page had.

Suppose your page, accessed as http://foo.com/bar?baz=1&bleh=2 has the following redirect tag: <meta http-equiv="Refresh" content="5;http://foo.com/bar?baz=1" />.

The request the server gets? It's the original http://foo.com/bar?baz=1&bleh=2.

I really, really, really hate this browser...

Sunday, January 18, 2009

More people speak on leaving Google

An interesting compendium of opinions from people who worked at Google. A lot of negativity which is perhaps natural for the "former" team.

http://www.techcrunch.com/2009/01/18/why-google-employees-quit/

Still, I had a feeling that a lot of it is driven by the misunderstanding of Google psyche.

Google is not your typical software company - it is an advertising company. It is very much in the nature of advertising to tell the truth in a way that makes the advertiser looks as positive as possible.

It reflects in a way that the software is written, it reflects in compensation.

For example, when Microsoft tells you that they will pay for X (for example, moving you to/from a locale) they mean that the net economic effect to you will be X. Which means grossing up the taxes. The fact that they had to pay more is an implementation detail that is hidden from the user.

When Google tells you that they will pay for X (for example, a subsidy for buying a Prius), they mean that the net economic effect to them would be X. What you will get is really .6X - after taxes.

And so it goes ;-)... People who do not read the small script are bound to be disappointed.

It's not at all that the company is evil - far from it. Google tries to work very hard to appeal to the employees. It's just the advertising nature that show no matter what you do :-).

You have to read it all to fully appreciate the depth of stupidity of this guy...

http://www.windsorstar.com/Leamington+loses+Nigerian+scam/1173799/story.html

Thursday, January 15, 2009

Your taxes at work, Part III

The messages on Arab TV teaching children the "art of martyrdom" (AKA becoming the suicide bombers) has been dutifully covered by US media, and have become the part of the lore on why US should support Israel.

Here's the other side of the medal.

http://www.mystudydate.com/pg/blog/Martini/read/1462/message-from-israeli-children-to-arabs-die

Let's hope it is covered with an equal zeal.

Wednesday, January 14, 2009

Free code! Send email from your Exchage account - programmatically!

Note: This requires Exchange 2007.

In Visual Studio 2008, create a Visual C# Console application project. Call it SendMail for simplicity.

References->Add Service Reference->Advanced->Add Web Reference

In URL box, put a server name on which you have an account. The format of the URL should be something like this https://mail.microsoft.com/EWS/Exchange.asmx


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

using SendMail.com.microsoft.mail;

namespace SendMail
{
class Program
{
/// <summary>
/// Shows usage.
/// </summary>
private static void DisplayUsage()
{
Console.WriteLine("Usage:\n");
Console.WriteLine(" sendmail [--to emailaddress]+\n");
Console.WriteLine(" [--cc emailaddress]*\n");
Console.WriteLine(" [--from emailaddress]?\n");
Console.WriteLine(" [--subject textlineinquotes]?\n");
Console.WriteLine(" [--user username]\n");
Console.WriteLine(" [--domain domain]?\n");
Console.WriteLine(" [--password password]\n");
Console.WriteLine(" [--ews exchangewebserviceurl]\n");
Console.WriteLine(" Text to send in email\n");
Console.WriteLine("+ = one or more, * = zero or more, ? = zero or one");
Console.WriteLine("Note: username and password are required.");
}

/// <summary>
/// Processes command line args, sends one email.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string user = null;
string password = null;
string domain = null;
string ews = null;
List<string> to = new List<string>();
List<string> cc = new List<string>();
string from = null;
string subject = null;
StringBuilder text = new StringBuilder();
for (int i = 0; i < args.Length; ++i)
{
if (i < args.Length - 1)
{
if (args[i].Equals(
"--user", StringComparison.InvariantCultureIgnoreCase))
{
user = args[++i];
continue;
}
if (args[i].Equals(
"--domain", StringComparison.InvariantCultureIgnoreCase))
{
domain = args[++i];
continue;
}
if (args[i].Equals(
"--password", StringComparison.InvariantCultureIgnoreCase))
{
password = args[++i];
continue;
}
if (args[i].Equals(
"--ews", StringComparison.InvariantCultureIgnoreCase))
{
ews = args[++i];
continue;
}
if (args[i].Equals(
"--to", StringComparison.InvariantCultureIgnoreCase))
{
to.Add(args[++i]);
continue;
}
if (args[i].Equals(
"--cc", StringComparison.InvariantCultureIgnoreCase))
{
cc.Add(args[++i]);
continue;
}
if (args[i].Equals(
"--from", StringComparison.InvariantCultureIgnoreCase))
{
from = args[++i];
continue;
}
if (args[i].Equals(
"--subject", StringComparison.InvariantCultureIgnoreCase))
{
subject = args[++i];
continue;
}
}

if (text.Length > 0)
text.Append(" ");

text.Append(args[i]);
}

if (to.Count == 0 || user == null || password == null || ews == null)
{
DisplayUsage();
return;
}

ExchangeServiceBinding binding = new ExchangeServiceBinding();
binding.Credentials = new NetworkCredential(user, password, domain);
binding.Url = ews;

DistinguishedFolderIdType folder = new DistinguishedFolderIdType();
folder.Id = DistinguishedFolderIdNameType.sentitems;

TargetFolderIdType targetFolder = new TargetFolderIdType();
targetFolder.Item = folder;

CreateItemType createItem = new CreateItemType();
createItem.MessageDisposition = MessageDispositionType.SendAndSaveCopy;
createItem.MessageDispositionSpecified = true;
createItem.SavedItemFolderId = targetFolder;

MessageType message = new MessageType();

List<EmailAddressType> recipients = new List<EmailAddressType>();
foreach (string s in to)
{
EmailAddressType recipient = new EmailAddressType();
recipient.EmailAddress = s;
recipients.Add(recipient);
}

message.ToRecipients = recipients.ToArray();

if (cc.Count > 0)
{
recipients = new List<EmailAddressType>();
foreach (string s in cc)
{
EmailAddressType recipient = new EmailAddressType();
recipient.EmailAddress = s;
recipients.Add(recipient);
}

message.CcRecipients = recipients.ToArray();
}

if (from != null)
{
message.From = new SingleRecipientType();
message.From.Item = new EmailAddressType();
message.From.Item.EmailAddress = from;
}

message.Subject = subject;
message.Sensitivity = SensitivityChoicesType.Normal;

message.Body = new BodyType();
message.Body.BodyType1 = BodyTypeType.Text;
message.Body.Value = text.ToString();

createItem.Items = new NonEmptyArrayOfAllItemsType();
createItem.Items.Items = new MessageType[1];
createItem.Items.Items[0] = message;

CreateItemResponseType response = binding.CreateItem(createItem);
foreach (ResponseMessageType r in response.ResponseMessages.Items)
{
if (r.ResponseClass == ResponseClassType.Success)
{
Console.WriteLine("Message sent.");
}
else
{
Console.WriteLine("Failed to send the message. ");
Console.WriteLine(r.MessageText);
}
}
}
}
}

Tuesday, January 13, 2009

Diamonds are forever?

Yes, but only because you can't sell them!

Just read the absolutely captivating article in The Atlantic Monthly about the value of diamonds as an investment tool. The article http://www.theatlantic.com/doc/print/198202/diamond was written in 1982, but a casual Google search on "historical proces of diamonds" quickly confirms that all the basic numbers are still true, as well as its central premise, which is that diamonds is just about a notch below fertilizer as far as the quality of investment goes.

This is what they say, in short...

First, the diamonds are not actually all that rare. They were, before the beginning of the 20th century. But the since discovered reserves in Africa, Russia, and Australia are pretty big. What's more, the amount of diamonds that is held privately (extracted from these mines over the last 100 years) is astounding.

The diamond market is wholly owned by DeBeers. They fix the price by limiting production, marketing the diamonds as essentially a requirement for marriage in US and Japan (and thus attaching the "sentimental value" to them which is well beyond the real value).

Most importantly, they limit the liquidity of the market, which prevents the influx of second-hand diamonds which would certainly drop the price.

This is where it becomes really fun. How do you prevent the private diamonds from being sold? The scheme is brilliant: if you make a spread between retail and wholesale price huge, people - who paid the retail price when they acquired the stones - would find it very difficult to sell them, because when selling, they would be paying the wholesale price.

And this wholesale price is 2-3 times less than the retail.

In addition, DeBeers makes it hard for dealers to pay even the wholesale price: when dealers get their stones from DeBeers, they get them on consignment: they pay DeBeers when the diamonds are sold. There is no capital on their own involved. But if the dealer is buying the stone from the street, they have to put up their own capital.

The net result? That stone for which you paid $25000 at Costco? If you can get $7000 back for it, consider yourself lucky!

And based on the historical trends (http://minerals.usgs.gov/ds/2005/140/diamondindustrial.pdf), it will not be until the Universe collapses and is reborn in the next Big Bang that the prices will get to the levels where the investment will recoup itself :-).