Saturday, June 26, 2010

Friday, June 4, 2010

Terrorism...

Thirty years ago Chief of Staff Mordechai Gur observed that since 1948, "we have been fighting against a population that lives in villages and cities." As Israel's most prominent military analyst, Zeev Schiff, summarized his remarks, "the Israeli Army has always struck civilian populations, purposely and consciously...the Army, he said, has never distinguished civilian [from military] targets...[but] purposely attacked civilian targets."

http://www.chomsky.info/articles/20090119.htm

Wednesday, May 19, 2010

Can we stuff 2000 calories in one drink? Yes, we can! (via Reddit)

This is really, truly horrible: http://worldmysteries9.blogspot.com/2010/05/harmful-drinks-in-america.html

The worst-worst drink has 2,010 calories, 131 g fat (68 g saturated), 153 g sugars (this is the number of calories a human should consume in a day; and more fat than one probably should have in a week).

#4 on the list has 1,210 calories, 19 g fat (10 g saturated), and 240 g sugars! Yes, this is really 1/4 of a kg. In one "drink"!!!

Monday, May 17, 2010

Server 200x RAID tips and tricks

Since 2003, Windows Server was shipping with a very cool feature: software RAID.

Software RAID has two major advantages over hardware.

First, hardware RAID protects you against the disk failure, but not against the controller failure. The drive array contains proprietary disk allocation information that varies from manufacturer to manufacturer and controller to controller, so disks are not easily movable between different controllers. So when your controller fails - potentially, long time in the future, when the same boards are no longer available, - or a new release of OS stops supporting your older driver, you may be very much out of luck with your data.

Second, software RAID is considerably more flexible than hardware RAID. Hardware RAID operates on disks as atomic units - you RAID the whole disks together. Software RAID operates on volumes, and each volule can be configured with different level of redundancy. For example, you can have 2 constructs on two disks at the same time - an OS partition that is RAID-1 mirrored with an image on the second disk, and another volume that combines the rest of the space on two drives as a single span or RAID-0. The level of redundancy is selected per volume, not per disk.

Also, for those of us who like coding, software RAID has a very nice software interface (http://msdn.microsoft.com/en-us/library/bb986750(v=VS.85).aspx and its undocumented managed counterpart in Microsoft.Storage.Vds.dll) which allows one to code simple things like checking the health of the storage and send an email if something goes bad.

But what about performance? A while ago when we were designing Windows Home Server, we tested various hardware RAID implementations versus software RAID in Server 2003.

It turns out that both RAID-0 and RAID-1 exhibit very similar performance for both hardware and software solutions. If you think about what the system has to do (write the same data to two disks at the same time in the case of RAID-1) it quickly becomes obvious that hardware implementation does not really add anything over the software in this case: both can write the same data in two places at the same time with the same speed. Big surprise :-).

RAID-5 is a different beast though - there actually is a computation going on, and it is possible to build a specialized chip for doing vector XOR operations that would leave the general purpose x86 in the dust.

A much bigger problem also exists in the lack of integration between the formatting and the RAID code. When you format a RAID drive, the default allocation unit that the UI presents is very small.

Due to the way the software RAID is implemented, it leads to incredibly slow performance. On my relatively powerful system the writes clock at only 20-30MBps (this going to the drives that are supposed to sustain 3Gbps, or 300MBps transfers). Selecting a more reasonable allocation unit of 64k improves the write speed by a factor of four, to almost 120MBps.

The other performance problem that is format related is after creating a new RAID volume, the default behavior is that format and resync happen at the same time. I covered it in the previous blog post here: http://1-800-magic.blogspot.com/2010/05/solution-to-slow-formatting-puzzle.html.

In summary, here are the two very simple rules can make your RAID array much faster:
- Select the 64k as a default allocation unit when formatting the RAID-5 volume.
- When formatting any new RAID volume, use quick format first, wait for the volume to finish resyncing, then repeat with full format if you like (remember to keep the large allocation unit in the second format though!)

Happy RAIDing!

Solution to the slow formatting puzzle

A couple of weeks back I posted a puzzle about an experience that I just had with Server 2008 R2 software RAID subsystem: somehow the speed of formatting a new very large RAID-5 array was highly unpredictable. It advanced by a mere 12% a day for the 4 days and then suddenly sped up 4x and finished the last 50% of the formatting in just one day.

Furthermore, I expected precisely this behavior. Why?

This weirdness occurs because of the typical Microsoft phenomenon that was best expressed by an acquaintance of mine that works in Windows Mobile. He thought that the biggest difference between Microsoft's and Apple's approaches to development are these:

Microsoft attacks problems horizontally: it builds core system, then builds layers on top of it. When it's time to ship and something needs to get cut, it's the top of the stack that goes first - and more often than not it's the user experience.

Apple develops software vertically - it enables a user experience, from top to bottom. When they need to cut, they cut the entire experiences (and also maybe the bugs, judging from the fact that my iPhone seems to crash considerably more often than most of Windows Mobile phones I owned in the past). So for example, iPhone would ship with Bluetooth support just for the mono phone headset, but no stereo profile. But the experiences that are left are implemented completely, to the maximum possible level of usability.

Back to our RAID problem. The reason the formatting is so slow in the very beginning is because two things happen at the same time: RAID resync and the formatting. Resync gets kicked off immediately after the RAID system is built, and it simply ensures that the parity volume (in the case of RAID-5) has correct checksums, or the mirrored volume (in the case of RAID-1) has a correct copy of the primary volume.

The other thing that happens once you create RAID volume from the UI is, of course, formatting. One can select quick format which only creates a file system. However, most people probably prefer to run full format when a new drive is added, just to make sure that it is not full of bad sectors.

So we have two write operations that are going on - first one is creating the redundant information, the other is filling the disk with zeroes.

Writing to the disk is very fast these days, but the seek time has barely improved in the last decade, and because the two writes happen in different places on the disk, the whole thing is completely dominated by the disk head moving from one track to another. And with a seek time of 15ms you can only have ~70 of these per second.

Of course, if the format is in progress, there is absolutely no sense to do a resync at the same time - whatever redundancy data resync creates is going to be instantly overwritten by the format.

But there was no Steve Jobs standing over the devs' shoulder, and getting the (filesystem) format in sync with (block device) RAID required two different teams to do something together so it probably got punted. I am sure it's in in a readme somewhere... and now in at least one blog :-).

So to avoid unnecesary delays, select quick format option when adding the RAID volume, wait for the resync to finish, THEN format it again with the full format.

Saturday, May 1, 2010

Formatting the large hard drive - continued

As mentioned in the previous post, one of my servers is formatting a 7.5TB RAID-5 volume.

During the first 24 hours, it has completed 13%.
During the next 24 hours, the progress was at 26%.
Next day, it was at 38%, and the day after (last night) it clocked at 50%.
However, this morning, 12 hours later, it was at 75%, and tonight I expect it to be completed.

So in the very last day the system's progress was the same as for the first 4 days. Moreover, I fully expected this to be the case. Can anyone guess why?

Wednesday, April 28, 2010

Formatting a large RAID-5 drive

Old hard drives - I have had them for 3 years - on my servers at home started to fail, so I had to replace one RAID-5 array. I bought 5 2TB drives and used software RAID-5 function in Windows Server 2008 R2 to create a monstrous 7.5TB (usable space) drive out of these 5 disks.

I only use soft RAID because the disks can be read by any Windows Server, whereas hardware RAID protects well against a drive failure, but if it is the controller that dies, you may be out of luck - who knows if this model would be even available a few years down the road, and most controllers use proprietary data formats, so the disk arrays are not portable between them.

Anyway, I built the array and starting formatting it. After the first 24 hours, the format was 13% complete. Next day (today) it is up to 26%.

Puzzle for the readers - how long do you think the format is going to take in total? Do not post the reasoning, just the number :-). I will post the final answer (and why) when the format completes.

Wednesday, April 21, 2010

Dictionary attacks on my home network

I am running Small Business Server 2003 at home. One service that it provides is sending daily reports about what happened to the system in the last 24 hours.

This report includes summaries of the event logs, and in the last several days the security logs were overflowing with logon audit failures caused by what looks like a distributed dictionary attack on my server.

The attack goes as follows: for a few hours someone (a script, really) is trying to connect to the system using various "likely" account names like aloha, admin, Administrator, master, root, randy, etc. The logon attempt for every user name repeats a few dozen times, most likely with different passwords, although security logs obviously don't show them, and then the user name changes.

After a few thousand attempts the attack subsides only to resume the next day from a different IP address.

Obviously, all the passwords used by our family are rather complex, and it is very unlikely that the thing will ever guess them, but it's discomforting nevertheless, so today I decided to get rid of the attackers altogether.

Small Business Server 2003 does not include standard Windows Firewall, instead it uses filters in its routing service to block unwelcome traffic.

The UI for this "firewall" of sorts can be accessed via Routing and Remote Access MMC available in the Administrative Tools menu. From there, one can expand IP Routing, the NAT/Basic Firewall, and double-clicking on the interface name brings up a properties dialog box with the "Inbound Filters" button.

I had no idea how to access it programmatically. Luckily, most of Windows networking management is scriptable through netsh, and netsh has a very nice property called dump which prints out a script that could be used to reproduce the current configuration.

So I created a rule with the fake firewall rule with an address that I could recognize, ran netsh dump, and searched the output for that ip address. As it turns out then, to add a firewall rule on Server 2003 with routing enabled, this is what needs to be done:
routing ip add filter name="Network Connection"
    filtertype=INPUT srcaddr=xx.xx.xx.xx srcmask=255.255.255.255
    dstaddr=0.0.0.0 dstmask=0.0.0.0 proto=ANY
where xx.xx.xx.xx is the placeholder for the ip address.

Poking around a bit more, it turned out that to list the existing rules (thus determining which IP addresses have already been blocked), one could do this:
routing ip show filter name="Network Connection"
The output is a table which can easily be picked apart with a regular expression:
private static Regex presentIps = new Regex(
    @"^\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+255.255.255.255\s+.*$",
    RegexOptions.Multiline);
private static HashSet<string> GetCurrentlyBlockedIps()
{
    string filters = NetSh(
        "routing ip show filter name=\"Network Connection\"");
    MatchCollection allMatches = presentIps.Matches(filters);
    HashSet<string> blockedIps = new HashSet<string>();
    foreach (Match m in allMatches)
    {
        Console.WriteLine("This ip is currently being blocked: {0}",
            m.Groups[1].Value);
        blockedIps.Add(m.Groups[1].Value);
    }
    return blockedIps;
}
NetSh() is the function that runs netsh.exe. It's a bit complex because it reads both STDOUT and STDERR. This has to be done asynchronously or else a read operation on STDOUT might block while STDERR overflows:
private delegate string ReaderDelegate();
private static string NetSh(string arguments)
{
    Process process = new Process();
    process.StartInfo.Arguments = arguments;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.FileName = "netsh.exe";
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.UseShellExecute = false;

    process.Start();

    ReaderDelegate stdoutReader =
        new ReaderDelegate(process.StandardOutput.ReadToEnd);
    ReaderDelegate stderrReader =
        new ReaderDelegate(process.StandardError.ReadToEnd);
    IAsyncResult stdoutResult = stdoutReader.BeginInvoke(null, null);
    IAsyncResult stderrResult = stderrReader.BeginInvoke(null, null);

    WaitHandle[] handles =
    {
        stdoutResult.AsyncWaitHandle,
        stderrResult.AsyncWaitHandle
    };

    if (!WaitHandle.WaitAll(handles))
        throw new Exception("netsh.exe was aborted");

    string stdout = stdoutReader.EndInvoke(stdoutResult);
    string stderr = stderrReader.EndInvoke(stderrResult);

    process.WaitForExit();

    if (!string.IsNullOrEmpty(stderr))
    {
        Console.Error.WriteLine(
            "Failed netsh {0}", process.StartInfo.Arguments);

        if (stdout != null)
            Console.Error.WriteLine("{0}", stdout);

        Console.Error.WriteLine("{0}", stderr);

        throw new Exception("netsh.exe failed");
    }

    process.Dispose();

    return stdout;
}

We're almost there. All I now need to do is to walk the security event log, picking out the IP address of the attacker out of the relevant entries:
using (EventLog ev = new EventLog("Security"))
{
    EventLogEntryCollection entries = ev.Entries;
    for (int index = entries.Count - 1; index >= 0; index--)
    {
        EventLogEntry ele = entries[index];
...
        if (ele.CategoryNumber != 2 || ele.InstanceId != 529 ||
            ele.EntryType != EventLogEntryType.FailureAudit)
            continue;

        if (ele.ReplacementStrings.Length < 11 ||
            knownGoodIps.IsMatch(ele.ReplacementStrings[11]))
            continue;

        string ip = ele.ReplacementStrings[11];
...
If the logon failure happens, say, more then 5 times in two minutes, we simply block out the whole ip address from accessing the server, as follows (you'll notice that I maintain a queue of events for all relevant ip addresses that allows me to detect the number of failures per time interval):
if (blockedIps.Contains(ip))
    continue;

LinkedList<DateTime> queue;
if (!trackedIps.ContainsKey(ip))
{
    queue = new LinkedList<DateTime>();
    queue.AddFirst(ele.TimeGenerated);
    trackedIps[ip] = queue;

    continue;
}

queue = trackedIps[ip];
queue.AddFirst(ele.TimeGenerated);

if (queue.Count < failEvents)
    continue;

while (queue.Count > failEvents)
    queue.RemoveLast();

TimeSpan period = queue.Last.Value - queue.First.Value;
if (period.Milliseconds > failPeriod)
    continue;

blockedIps.Add(ip);

NetSh("routing ip add filter name=\"Network Connection\" filtertype=INPUT srcaddr="
    + ip + " srcmask=255.255.255.255 dstaddr=0.0.0.0 dstmask=0.0.0.0 proto=ANY");

The rest is just accounting. Here's the full program for your enjoyment:
//-----------------------------------------------------------------------
// <copyright>
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace Defender
{
    /// <summary>
    /// This implements code that scans security event log and uses Windows Server 2003
    /// ip routing filters to block dictionary attacks.
    /// </summary>
    class Program
    {
        /// <summary>
        /// Regular expression that parses existing filters out of
        /// netsh ip show filter output.
        /// </summary>
        private static Regex presentIps = new Regex(
            @"^\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+255.255.255.255\s+.*$",
            RegexOptions.Multiline);

        /// <summary>
        /// Delegate used to read process output.
        /// </summary>
        /// <returns> STDOUT/ERR stream converted to a string. </returns>
        private delegate string ReaderDelegate();

        /// <summary>
        /// Runs netsh.exe with the given argument. Throws if there is an error, else
        /// returns the output as one string.
        /// </summary>
        /// <param name="arguments"> netsh.exe command arguments. </param>
        /// <returns> STDOUT converted to one string. </returns>
        private static string NetSh(string arguments)
        {
            Process process = new Process();
            process.StartInfo.Arguments = arguments;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.FileName = "netsh.exe";
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.UseShellExecute = false;

            process.Start();

            ReaderDelegate stdoutReader =
                new ReaderDelegate(process.StandardOutput.ReadToEnd);
            ReaderDelegate stderrReader =
                new ReaderDelegate(process.StandardError.ReadToEnd);
            IAsyncResult stdoutResult = stdoutReader.BeginInvoke(null, null);
            IAsyncResult stderrResult = stderrReader.BeginInvoke(null, null);

            WaitHandle[] handles =
            {
                stdoutResult.AsyncWaitHandle,
                stderrResult.AsyncWaitHandle
            };

            if (!WaitHandle.WaitAll(handles))
            {
                throw new Exception("netsh.exe was aborted");
            }

            string stdout = stdoutReader.EndInvoke(stdoutResult);
            string stderr = stderrReader.EndInvoke(stderrResult);

            process.WaitForExit();

            if (!string.IsNullOrEmpty(stderr))
            {
                Console.Error.WriteLine(
                    "Failed netsh {0}", process.StartInfo.Arguments);

                if (stdout != null)
                    Console.Error.WriteLine("{0}", stdout);

                Console.Error.WriteLine("{0}", stderr);

                throw new Exception("netsh.exe failed");
            }

            process.Dispose();

            return stdout;
        }

        /// <summary>
        /// Gets the collection of ips that are already blocked.
        /// </summary>
        /// <returns> HashSet of blocked ips. </returns>
        private static HashSet<string> GetCurrentlyBlockedIps()
        {
            string filters = NetSh(
                "routing ip show filter name=\"Network Connection\"");
            MatchCollection allMatches = presentIps.Matches(filters);
            HashSet<string> blockedIps = new HashSet<string>();
            foreach (Match m in allMatches)
            {
                Console.WriteLine("This ip is currently being blocked: {0}",
                    m.Groups[1].Value);
                blockedIps.Add(m.Groups[1].Value);
            }

            return blockedIps;
        }

        /// <summary>
        /// Convers a comma-separated list of "known good" ips into a filtering
        /// regular expression.
        /// </summary>
        /// <param name="ipList">Comma-separated list of known-good ips.</param>
        /// <returns>Regular expression that matches this list.</returns>
        private static Regex BuildRegexForIpList(string ipList)
        {
            return new Regex(
                "^(" + ipList.Replace(".", "\\.").Replace(',', '|') + ").*$");
        }

        /// <summary>
        /// Sends notification email.
        /// </summary>
        /// <param name="mailBody"> Body of the email. </param>
        private static void SendMail(StringBuilder mailBody)
        {
            MailMessage email = new MailMessage();
            email.To.Add(ConfigurationSettings.AppSettings["NotificationEmail"]);
            email.Subject = "New attack(s) detected. IPs blocked.";
            email.From = new MailAddress(
                ConfigurationSettings.AppSettings["FromEmail"]);
            email.Sender = new MailAddress(
                ConfigurationSettings.AppSettings["FromEmail"]);
            email.Body = mailBody.ToString();
            email.IsBodyHtml = false;

            SmtpClient client = new SmtpClient(
                ConfigurationSettings.AppSettings["SmtpServer"]);
            client.UseDefaultCredentials = true;
            client.EnableSsl = bool.Parse(
                ConfigurationSettings.AppSettings["UseSslForSmtp"]);

            client.Send(email);
        }

        /// <summary>
        /// Runs one round of periodic processing.
        /// </summary>
        /// <param name="lastProcessedIndex"> Previously seen event index. Processes
        /// all events that are newer than this. </param>
        /// <returns> The new watermark for event index. </returns>
        private static int Process(int lastProcessedIndex)
        {

            StringBuilder mailBody = new StringBuilder();

            int failEvents = int.Parse(
                ConfigurationSettings.AppSettings["LogonFailuresPerPeriod"]);
            int failPeriod = 1000 * 60 * int.Parse(
                ConfigurationSettings.AppSettings["FailurePeriodMinutes"]);
            Regex knownGoodIps = BuildRegexForIpList(
                ConfigurationSettings.AppSettings["KnownGoodIpList"]);

            Dictionary<string, LinkedList<DateTime>> trackedIps =
                new Dictionary<string, LinkedList<DateTime>>();

            HashSet<string> blockedIps = GetCurrentlyBlockedIps();
            
            Dictionary<string, List<string>> auditFailures =
                new Dictionary<string, List<string>>();

            HashSet<string> newlyBlockedIps = new HashSet<string>();

            using (EventLog ev = new EventLog("Security"))
            {
                EventLogEntryCollection entries = ev.Entries;

                for (int index = entries.Count - 1; index >= 0; index--)
                {
                    EventLogEntry ele = entries[index];
                    if (ele.Index < lastProcessedIndex)
                        break;

                    if (ele.CategoryNumber != 2 || ele.InstanceId != 529 ||
                        ele.EntryType != EventLogEntryType.FailureAudit)
                        continue;

                    if (ele.ReplacementStrings.Length < 11 ||
                        knownGoodIps.IsMatch(ele.ReplacementStrings[11]))
                        continue;

                    string ip = ele.ReplacementStrings[11];

                    if (!auditFailures.ContainsKey(ip))
                        auditFailures[ip] = new List<string>();

                    auditFailures[ip].Add(ele.Message);

                    if (blockedIps.Contains(ip))
                        continue;

                    LinkedList<DateTime> queue;
                    if (!trackedIps.ContainsKey(ip))
                    {
                        queue = new LinkedList<DateTime>();
                        queue.AddFirst(ele.TimeGenerated);
                        trackedIps[ip] = queue;

                        continue;
                    }

                    queue = trackedIps[ip];
                    queue.AddFirst(ele.TimeGenerated);

                    if (queue.Count < failEvents)
                        continue;

                    while (queue.Count > failEvents)
                        queue.RemoveLast();

                    TimeSpan period = queue.Last.Value - queue.First.Value;
                    if (period.Milliseconds > failPeriod)
                        continue;

                    string msg = string.Format(
                        "{0} Adding the following ip to blocked list: {1}",
                        DateTime.Now, ip);

                    blockedIps.Add(ip);
                    newlyBlockedIps.Add(ip);

                    NetSh("routing ip add filter name=\"Network Connection\" filtertype=INPUT srcaddr="
                        + ip + " srcmask=255.255.255.255 dstaddr=0.0.0.0 dstmask=0.0.0.0 proto=ANY");

                    Console.WriteLine(msg);
                    mailBody.Append(msg);
                    mailBody.Append("\r\n");
                }

                lastProcessedIndex = entries[entries.Count - 1].Index;
            }

            if (mailBody.Length > 0)
            {
                foreach (string ip in newlyBlockedIps)
                {
                    foreach (string message in auditFailures[ip])
                    {
                        mailBody.Append("-----\r\n");
                        mailBody.Append(message);
                    }
                }

                SendMail(mailBody);
            }

            return lastProcessedIndex;
        }

        /// <summary>
        /// Main entry point, does all the work.
        /// </summary>
        /// <param name="args"> Program arguments. Not used, all configuration is
        /// through app.config. </param>
        static void Main(string[] args)
        {
            int lastProcessedIndex = 0;
            for (; ; )
            {
                lastProcessedIndex = Process(lastProcessedIndex);

                GC.Collect();

                int sleep = int.Parse(
                    ConfigurationSettings.AppSettings["SleepIntervalMinutes"]);
                Console.WriteLine("Sleeping for {0} minutes @ {1}...",
                    sleep, lastProcessedIndex);
                Thread.Sleep(sleep * 60 * 1000);
            }
        }
    }
}
It also requires an app.config with the following settings:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="KnownGoodIpList" value="192.168.,131.107."/>
    <add key="LogonFailuresPerPeriod" value="5" />
    <add key="FailurePeriodMinutes" value="2" />
    <add key="SmtpServer" value="xxx" />
    <add key="NotificationEmail" value="yyy" />
    <add key="FromEmail" value="zzz" />
    <add key="UseSslForSmtp" value="true" />
    <add key="SleepIntervalMinutes" value="2"/>
  </appSettings>
</configuration>

The whole thing took barely 2 hours, and have stopped short two attacks just this evening!

Wednesday, March 31, 2010

Printing and .NET

I needed to print a pattern to help mount a scope, which in turn made me learn .NET printing. As it turns out, it is really straightforward.

Here's an abbreviated guide.

First, add references to System.Drawing and System.Windows.Forms (the latter is for the printer selection dialog box).

Second, add the following usings:
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

Third, subclass the PrintDocument class and override OnPrintPage:
class PrintSomething : PrintDocument
{
    protected override void OnPrintPage(PrintPageEventArgs e)
    {
        ...
    }
}

The available paper space is defined by DefaultPageSettings.PrintableArea which is a rectangle with the coordinate system at (Left, Top) and extending down to (Right, Bottom). The units are in 1/100th of an inch, and the drawing coordinates are also in the same units. So for example, to draw a one inch circle in the middle of the page...
float middleX = (DefaultPageSettings.PrintableArea.Right +
    DefaultPageSettings.PrintableArea.Left) / 2.0F;
int middleY = (DefaultPageSettings.PrintableArea.Bottom +
    DefaultPageSettings.PrintableArea.Top) / 2.0F;
Pen p = new Pen(Color.Black, 4);
e.Graphics.DrawEllipse(p, middleX - 50.0F, middleY + 50.0F, 100.0F, 100.0F);

Finally, the following code displays the printer selecting dialog and renders the picture if user clicks OK.
static void Main(string[] args)
{
    PrintSomething lines = new PrintSomething();
    PrintDialog dlg = new PrintDialog();
    dlg.Document = lines;
    if (dlg.ShowDialog() == DialogResult.OK)
        lines.Print();
}

So here is the final program:
//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Sergey Solyanik">
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

/// <summary>
/// This code prints a target that can be used for scope alignment.
/// </summary>
class PrintTarget : PrintDocument
{
    /// <summary>
    /// Prints out a target.
    /// </summary>
    /// <param name="args"> Arguments array. Not used. </param>
    static void Main(string[] args)
    {
        PrintTarget lines = new PrintTarget();
        PrintDialog dlg = new PrintDialog();
        dlg.Document = lines;
        if (dlg.ShowDialog() == DialogResult.OK)
            lines.Print();
    }

    /// <summary>
    /// Renders the page.
    /// </summary>
    /// <param name="e"> Printer parameters and graphics. </param>
    protected override void OnPrintPage(PrintPageEventArgs e)
    {
        Graphics g = e.Graphics;
        Pen p1 = new Pen(Color.Black, 0.5F);
        Pen p2 = new Pen(Color.Black, 1);
        Pen p4 = new Pen(Color.Black, 4);

        float step = 100.0F / 16.0F;

        int middleX = (int)((DefaultPageSettings.PrintableArea.Right -
            DefaultPageSettings.PrintableArea.Left) / step) / 2;
        int boundX = ((int)((DefaultPageSettings.PrintableArea.Right -
            DefaultPageSettings.PrintableArea.Left) / 200.0F)) * 16;
        int middleY = (int)((DefaultPageSettings.PrintableArea.Bottom -
            DefaultPageSettings.PrintableArea.Top) / step) / 2;
        int boundY = ((int)((DefaultPageSettings.PrintableArea.Bottom -
            DefaultPageSettings.PrintableArea.Top) / 200.0F)) * 16;

        float top = DefaultPageSettings.PrintableArea.Top +
            (middleY - boundY) * step;
        float bottom = DefaultPageSettings.PrintableArea.Top +
            (middleY + boundY) * step;
        float left = DefaultPageSettings.PrintableArea.Left +
            (middleX - boundX) * step;
        float right = DefaultPageSettings.PrintableArea.Left +
            (middleX + boundX) * step;
        for (int i = 0; i < boundX; ++i)
        {
            Pen p;
            if (i % 16 == 0)
                p = p4;
            else if (i % 8 == 0)
                p = p2;
            else
                p = p1;

            g.DrawLine(p,
                DefaultPageSettings.PrintableArea.Left +
                (middleX + i) * step,
                top,
                DefaultPageSettings.PrintableArea.Left +
                (middleX + i) * step,
                bottom);

            g.DrawLine(p,
                DefaultPageSettings.PrintableArea.Left +
                (middleX - i) * step,
                top,
                DefaultPageSettings.PrintableArea.Left +
                (middleX - i) * step,
                bottom);
        }

        for (int i = 0; i < boundY; ++i)
        {
            Pen p;
            if (i % 16 == 0)
                p = p4;
            else if (i % 8 == 0)
                p = p2;
            else
                p = p1;

            g.DrawLine(p,
                left,
                DefaultPageSettings.PrintableArea.Top +
                (middleY + i) * step,
                right,
                DefaultPageSettings.PrintableArea.Top +
                (middleY + i) * step);

            g.DrawLine(p,
                left,
                DefaultPageSettings.PrintableArea.Top +
                (middleY - i) * step,
                right,
                DefaultPageSettings.PrintableArea.Top +
                (middleY - i) * step);
        }

        for (int i = 1; i < 4; ++i)
        {
            g.DrawEllipse(p4,
                DefaultPageSettings.PrintableArea.Left +
                (middleX - 8 * i) * step,
                DefaultPageSettings.PrintableArea.Top +
                (middleY - 8 * i) * step,
                16 * i * step, 16 * i * step);
        }
    }
}

And here's what it prints:


Hint: Microsoft XPS document writer is a great way to debug printer output without wasting paper.

Thursday, March 4, 2010

South Dakota legislature

Idiocracy is alive and progressing. Here's a quote from a recent resolution just passed in South Dakota declaring global warming null and void. Among the clauses:

"That there are a variety of climatological, meteorological, astrological, thermological, cosmological, and ecological dynamics that can effect world weather phenomena and that the significance and interrelativity of these factors is largely speculative;"

(emphasis mine)

Note the "astrological" (!!!) and the fact that they don't even write proper English - in a resolution, no less (http://www.yourdictionary.com/grammar-rules/affect-effect-grammar.html).

For the Nth time, the global warming "sceptics" (you don't get to be a sceptic in an area where you have no clue whatsoever) have demonstrated their true face - a bunch of ignorant anti-science idiots.

A democracy without an educated demos does not work.

Here's the link...

http://legis.state.sd.us/sessions/2010/Bill.aspx?File=HCR1009P.htm

...and the screen shot, in case they correct it...

Thursday, February 25, 2010

Like, C#

Yesterday I read absolutely hilarious post on Like, Python (http://www.staringispolite.com/likepython/) - a valleygirl/hillbilly dialect of Python. It was a big hit at work, and people immediately started asking me about the C# version.

You know our motto - we deliver!

So after 30 minutes of stitching together pieces from Malevich's Syntax Highlighter (to allow proper treatment of comments and string constants) (http://malevich.codeplex.com), and Scriptster (http://scriptster.codeplex.com), I present you with Like, C#.

Like, C# uses the same extended set of "keywords" as Like, Python, so we can write this code:

ohai

totally using System man;

uh class like Program
{
ok static void Main(string[] args) bro
{
just Console.Write("yo! what's your name?");
ok so like string name = Console.ReadLine();

if actually (name.Equals(""))
omg toootally just return;

um yeah

plz Console.WriteLine(like "Hi {0}, nice to meet you!", name);
}
}


Full list of "keywords" is here:

  • Valleygirl: omg, so, like, totally, right, toootally

  • Frat guy: friggin, fuckin, dude, man, bro, broheim, broseph

  • Internets: lol, rofl, teh, ohai, plz

  • Snoop: yo, homey, homeboy, sup, dog, shit, girl, ma, biatch, ho, shiiit

  • Local: wicked, hella, anyways

  • Misc: just, hey, yeah, ok, um, uh, ah, actually, something



And here's the full source of Like, C# compiler if you want to adopt this new, rapidly growing language:

//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Sergey Solyanik">
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LikeCSharp
{
/// <summary>
/// Implements Like, C# compiler.
/// </summary>
class Program
{
/// <summary>
/// Extension keywords.
/// </summary>
private static string[] keywords =
{
"omg", "so", "like", "totally", "right", "toootally",
"friggin", "fuckin", "dude", "man", "bro", "broheim",
"broseph", "lol", "rofl", "teh", "ohai", "plz",
"yo", "homey", "homeboy", "sup", "dog", "shit", "girl",
"ma", "biatch", "ho", "shiiit", "wicked", "hella", "anyways",
"just", "hey", "yeah", "ok", "um", "uh", "ah", "actually",
"something"
};

/// <summary>
/// Main entry point.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string runtimeEnvironmentPath =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
if (runtimeEnvironmentPath.EndsWith("\\"))
{
runtimeEnvironmentPath = runtimeEnvironmentPath.Substring(
0, runtimeEnvironmentPath.Length - 1);
}

string compilerExe = Path.Combine(runtimeEnvironmentPath, "csc.exe");

// However, if it's 2.0, there is a compiler-only extension (3.5).
// If 3.5 is indeed installed, use that.
string baseFrameworkPath = Path.GetDirectoryName(runtimeEnvironmentPath);
string runtimeName = Path.GetFileName(runtimeEnvironmentPath);
if ("v2.0.50727".Equals(runtimeName))
{
string compilerExe35 = Path.Combine(
Path.Combine(baseFrameworkPath, "v3.5"), "csc.exe");
if (File.Exists(compilerExe35))
{
compilerExe = compilerExe35;
}
}

StringBuilder sb = new StringBuilder();
bool firstArg = true;
HashSet<string> files = new HashSet<string>();

string keywordRE = "\"|'|//|/\\*|#?[a-z_0-9]+";
HashSet<string> keywordDictionary = new HashSet<string>(keywords);
Regex keywordRegex = new Regex(
keywordRE, RegexOptions.Compiled | RegexOptions.IgnoreCase);

foreach (string arg in args)
{
if (firstArg)
{
firstArg = false;
}
else
{
sb.Append(' ');
}

string newArg = arg;
if (arg.EndsWith(".lcs"))
{
newArg = Preprocess(arg, keywordRegex, keywordDictionary);
files.Add(newArg);
}

if (newArg.Contains(' '))
{
newArg = '"' + newArg + '"';
}

sb.Append(newArg);
}

Process compiler = new Process();
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.FileName = compilerExe;
compiler.StartInfo.Arguments = sb.ToString();

compiler.Start();
compiler.WaitForExit();

Environment.ExitCode = compiler.ExitCode;

foreach (string file in files)
{
File.Delete(file);
}
}

/// <summary>
/// Converts file from Like, C# to normal C#.
/// </summary>
/// <param name="fileName"> File name to process. </param>
/// <param name="keywordRegex"> Regular expression that
/// matches language elements. </param>
/// <param name="keywordDictionary"> Like keywords. </param>
/// <returns> Name of the temporary file. </returns>
static string Preprocess(
string fileName,
Regex keywordRegex,
HashSet<string> keywordDictionary)
{
string tempDir = Environment.GetEnvironmentVariable("TEMP");
tempDir = Path.Combine(tempDir, "LIKECSHARP");
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}

string target = Path.Combine(
tempDir,
Path.GetFileNameWithoutExtension(fileName) + ".cs");

QuoteType quotes = QuoteType.None;
bool inComments = false;

StreamReader reader = new StreamReader(fileName);
StreamWriter writer = new StreamWriter(target);

for (; ; )
{
string s = reader.ReadLine();
if (s == null)
{
break;
}

StringBuilder encoded = new StringBuilder();

int i = 0;
while (i < s.Length)
{
if (quotes == QuoteType.Double)
{
int endQuotes = s.IndexOf("\"", i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
if ((endQuotes > 0 && s[endQuotes - 1] != '\\') ||
(endQuotes > 1 && s[endQuotes - 1] == '\\' &&
s[endQuotes - 2] == '\\'))
{
// This is not an escaped quote.
quotes = QuoteType.None;
}

i = endQuotes + 1;
continue;
}

if (quotes == QuoteType.Verbatim)
{
int endQuotes = s.IndexOf("\"", i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

if (s.IndexOf("\"\"", endQuotes) == endQuotes)
{
// This is an escaped quote.
encoded.Append(s.Substring(i, endQuotes + 2 - i));
i = endQuotes + 2;
continue;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
quotes = QuoteType.None;

i = endQuotes + 1;
continue;
}

if (quotes == QuoteType.Single)
{
int endQuotes = s.IndexOf('\'', i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
if ((endQuotes > 0 && s[endQuotes - 1] != '\\') ||
(endQuotes > 1 && s[endQuotes - 1] == '\\' &&
s[endQuotes - 2] == '\\'))
{
// This is not an escaped quote.
quotes = QuoteType.None;
}

i = endQuotes + 1;
continue;
}

if (inComments)
{
int endComments = s.IndexOf("*/", i);
if (endComments == -1)
{
// The entire string is in comments.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

inComments = false;

encoded.Append(
s.Substring(i, endComments + 2 - i));
i = endComments + 2;
continue;
}

Match nextMatch = keywordRegex.Match(s, i);
if (!nextMatch.Success)
{
encoded.Append(s, i, s.Length - i);
break;
}

if (i != nextMatch.Index)
{
encoded.Append(s, i, nextMatch.Index - i);
i = nextMatch.Index;
}

string matched = nextMatch.Groups[0].Value;

if (matched.Equals("'"))
{
encoded.Append("'");
quotes = QuoteType.Single;
++i;
continue;
}

if (matched.Equals("\""))
{
encoded.Append("\"");
quotes = QuoteType.Double;
++i;
continue;
}

if (matched.Equals("@\""))
{
encoded.Append("@\"");
quotes = QuoteType.Verbatim;
i += 2;
continue;
}

if (matched.Equals("//"))
{
// The rest of the line is comments.
encoded.Append(s.Substring(i));
break;
}

if (matched.Equals("/*"))
{
// Comments start.
encoded.Append("/*");
inComments = true;
i += 2;
continue;
}

if (keywordDictionary.Contains(matched))
{
// Keyword. Eat it.
i += matched.Length;
}
else
{
encoded.Append(matched);
i += matched.Length;
}
}

writer.WriteLine(encoded.ToString());
}

reader.Close();
writer.Close();

return target;
}

/// <summary>
/// Types of quoted string.
/// </summary>
enum QuoteType
{
None,
Single,
Double,
Verbatim
}
}
}

Sunday, January 17, 2010

William Buckley on General Franco

"General Franco is an authentic national hero. It is generally conceded that he above others had the combination of talents, the perseverance, and the sense of righteousness of his cause, that were required to wrest Spain from the hands of the visionaries, ideologues, Marxists and nihlistis that were imposing on her, in the thirties, a regime so grotesque as to do violence to the Spanish soul, to deny, even Spain's historical identity."

http://www.slate.com/id/2185301/pagenum/2

While we're at it, another quote, this time from Richard Nixon: "General Franco was a loyal friend and ally of the United States."

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

When I am saying that in the US we're having a choice between fascists on the right, and the center-right party on the "left", people are thinking that I am exaggerating. I am not.

Friday, December 4, 2009

Sr Oracle needed at Bank of America

...presumably so that they could predict the bursting of the next bubble ahead of time.

30000 new troops for Afghanistan - yeeeeeeehaw!

Tuesday, December 1, 2009

Web applications!



2 browsers (IE and Firefox) running Ajax.org demo are maxing out T9300 on my laptop - a Core 2 Duo running at 2.5 GHz. This is plugged in.

Try it yourself:

http://www.ajax.org/public/presentation/tae/presentation2.html#home

Wednesday, November 25, 2009

Modern capitalism

The top 5 execs at Lehman Brothers made $1B from 2000 to 2008. The top 5 execs at Bearn Stearns made $1.4B during the same period.

"The whole idea of capitalism is that the people provide the capital and the executives take care of it for us. In this case, the people provided the capital, and the executives took it."

http://crooksandliars.com/susie-madrak/lehman-bros-bear-stearns-ceos-walked

Tuesday, October 20, 2009

Matt Taibbi on naked short selling, or what killed Bear Stearns

Really awesome article on naked shorts. There are only a handful of news organizations who do real journalism these days, and, surprisingly, Rolling Stones seems to be one of these few. I am also very impressed with Bloomberg reporting recently - it looks like after Murdoch bought WSJ the best people went to Bloomberg.

http://www.rollingstone.com/politics/story/30481512/wall_streets_naked_swindle/print

Here's the really scary thing - we've lost the integrity of our financial markets.

The reason US was so successful in attracting capital was the transparency with which financial markets here used to operate, and strong regulations that ensured this transparency - for the most part, the legacy of Great Depression. However, over the last couple of decades these regulations have been weakened, and now we're back to the turn of the century stock swindles and off the books accounting (http://www.creditloan.com/blog/mark-to-market-accounting-changes-favor-banks/).

All this - coupled with the dollar fall - can mean only one thing: US will certainly lose its status as a financial capital of the world within the next decade.

In other news, marijuana legislation has been relaxed (http://finance.yahoo.com/news/AP-Newsbreak-New-medical-apf-4109207182.html?x=0&sec=topStories&pos=main&asset=&ccode=). So instead of worrying about markets, botched wars, and disappearing industrial base, the population can just get stoned! Finally, a government that knows how to rule...

Friday, October 16, 2009

Government rationing of health care

There were all these inane reports in the press about "Obama's suicide panels" and government rationing the health care - all widely covered.

Let's see what they say about this beauty - an insurance company says that they will only cover a woman if she gets sterilized - because she had a C-section in the past.

Under the hill

Check out where you can deliver a birthday present on 1-800-flowers.com!

Monday, October 12, 2009

Think pay for performance works? Think again...

http://www.ted.com/talks/dan_pink_on_motivation.html

This TED talk alludes to a number of social experiments where adding monetary incentive has significantly decreased the productivity of creative work.

Extremely enlightening, a must watch for every manager - and yet another proof that the claim that the Wall Streeters need to be paid exorbitant amounts of money to retain "the best and the brightest" is just a crapload of bull.

United States v. 8,800 Pounds, More or Less of Powdered White Egg Product

"United States v. 8,800 Pounds, More or Less of Powdered White Egg Product, et al., No. 07-3671, 2008 U.S. App. LEXIS 26098 (8th Cir. Dec. 24, 2008)(defendant’s importation of egg whites from Peru without first obtaining a pasteurization certificate from the Peruvian government and without obtaining authorization from the Food Safety Inspection Service violated the Egg Products Inspection Act, which could result in the condemnation and destruction of the egg whites; trial court’s granting of summary judgment upheld)."

It's hidden somewhere around the middle of the list on this page: http://www.calt.iastate.edu/aglawrl.html

I kid you not!

Wednesday, October 7, 2009

Big Pharma and research

I love it when they say that the pharmaceuticals are so expensive in the US because the companies need this money to finance research. In reality, if you look at a typical big pharma income statement, you will discover that only ~20% of its expenses are research - the rest is mostly sales and marketing.

But here's another proof beyond any reasonable doubt that what big pharma is all about is sales - its donations to political parties. Researchers giving money to repubs? Basically, the same people who say that Earth is 10,000 years old, and dinosaurs walked the planet alongside people, radiocarbon dating be damned?

This political contributions profile is more in line with what you expect from used car salespeople than the research and development organizations...

http://www.goodguide.com/contributions#sector=Pharmaceuticals



By comparison, here's how the software sector looks like:

http://www.goodguide.com/contributions#sector=Computer/Internet&sort=total&query=

Tuesday, October 6, 2009

Bravo, Apple! You have class!

Apple quits the U.S. Chamber of Commerce over its ‘frustrating’ global warming denialism.

http://thinkprogress.org/2009/10/05/apple-quits-chamber/

Friday, October 2, 2009

TFS 2010 will be much easier to install

I've spent a week trying to get TFS 2008 installed on my home infrastructure. It was the most painful piece of software that I've ever installed. And it looks like I was not the only one who felt that:

"Installing TFS has been a pain point for years. Although it’s gotten better, 2010 represents a quantum leap. The TFS installer now has 3 wizards: Basic, Standard and Advanced. The big innovation is the new “Basic” install wizard. It is a Next, Next, Next install experience that allows you to install and configure TFS in about 20 minutes or less (assuming .NET and SQL Express are already on your computer – a little longer if TFS has to install them for you). Both will already be there if you’ve installed VS 2010. The Basic wizard will install and configure IIS (if it’s not already there), install and configure SQL Express (if it’s not already there), and install and configure TFS. The only thing that really pains me is installing .NET 4.0 requires a reboot :(."

http://blogs.msdn.com/bharry/archive/2009/10/01/tfs-2010-for-sourcesafe-users.aspx

Sunday, September 20, 2009

Guns and morons

I am slowly building up my collection of World War II and former communist block firearms. This involves scouring the internet with searches on certain keywords.

Recently, I had a sad realization - most of the US gun-owning community is not mentally qualified to operate a fork, rather than own a gun. Which may explain an extremely high rate of gun-related death in the United States - 30896 in 2006.

Here's a representative post (http://answers.yahoo.com/question/index?qid=20080920100316AAlVQMH):

"Should i buy an SKS, AK-47 or AR-15 rifle?

the cheaper the better, but not if it is total garbage. The purpose of owning one for me is to have a assault rifle before they are illegal. i want something that can pack a punch when the world goes to crap so i can defend my family. yes, the purpose would be to kill, so go somewhere else hippies, this is a hypothetical situation i would like to be prepared for if it ever happens."

"Tactical advantage", "firefight" are the terms that litter the gun boards, so instead of answering these questions in dozens of places, I decided to come up with one meta-response here.

"Dear Moron,

Let me try to answer your question from the liberal/hippie point of view, since this angle is typically not covered on the gun boards you are frequenting.

First of all, this is the stupidest thing I read on the internet today! Why, you might ask? Well, there are multiple reasons.

First, do you really expect that hippies will attack you and your family? Hippies? Seriously? Like this guy?


Second, if us hippies/liberals wanted to attack you, surely we wouldn't storm your house? Did you know that there is a strong positive correlation between education and liberal views? You might not realize that, but one skill that they teach in college is thinking. So if we really did want to get you, we would surely be able to devise a better approach.

For example, we could just wait outside your house, and spray you with bullets from a safe distance when you come out to buy groceries. Or if we wanted to force the events, why not setting your house on fire and then just shoot everything that comes out?

But in reality, we wouldn't even bother with this at all. We'd just send a black helicopter (http://en.wikipedia.org/wiki/Black_helicopter). As you know, being government/UN freaks, we have plenty at our command. It would take one missile to have your house look like this, from a safe distance:


Do you really think that AK-47 vs AR-15 would make a material difference here?

Finally, even if you really did get into one of the "firefights", I bet you would not have much luck in a "tactical combat situation". I've played with your kind in Halo. You will be the biggest target, in the center of the field, collecting all the bullets.


I bet like with any other profession, it takes years of hard work to train a soldier - not an act of buying a gun.

So take my advice - instead of wasting money on something that you aren't mentally qualified to operate and won't be able to use, buy a book. Or sign up for a history or biology class at your local community college. You really could use the extra IQ so that next time people won't look at your writing and say - Geez, this is the stupidest thing I've seen on the internet today!"

Sunday, September 13, 2009

Beowulf

The Queen Anne Blockbuster is going out of business, and, among other things, I picked up a copy of Beowulf (http://www.beowulfmovie.com/) for a couple of dollars.

Boy was this a weird experience! So weird that, in a sense, it deserves to be a cult movie - a la Striptease. For most of the movie we couldn't tell if these guys were being serious, or trying to spoof something. I am still not entirely sure. We laughed throughout big part of it.

From a really overdone dialog ((think of Gimili’s boasting in The Lord of the Rings, multiply that by 800, and you have the most humble of Beowulf’s pronouncements), to nude Angelina Jolie's feet, that seamlessly merged lack of shoes with high heels, to the way they were hiding - in a really conspicuous way - Beowulf's genitalia as he was fighting Grendel in the nude (this was very, very similar to the scene where Bart rides through the streets of Springfield in Simpsons The Movie) - it looked more like a comedy than what the movie was trying to present.

As a comedy it might have been mildly funny, though not hysterical. If you enjoyed Striptease, watch this one. Otherwise, it’s probably not worth the hour and a half.

Saturday, September 12, 2009

If you don't like George Bush, you should take a look at his voters

http://www.telegraph.co.uk/news/worldnews/northamerica/usa/6173399/Charles-Darwin-film-too-controversial-for-religious-America.html

"The film was chosen to open the Toronto Film Festival and has its British premiere on Sunday. It has been sold in almost every territory around the world, from Australia to Scandinavia.

However, US distributors have resolutely passed on a film which will prove hugely divisive in a country where, according to a Gallup poll conducted in February, only 39 per cent of Americans believe in the theory of evolution."

Friday, September 11, 2009

An oldie...

...but a goodie!

"Cheney Waits Until Last Minute Again To Buy Sept. 11 Gifts"

http://www.theonion.com/content/news/cheney_waits_until_last_minute

Sunday, September 6, 2009

How much for shipping, again?

Check out the Fedex Next Day rate on this $10 battery...


Think this is egregiously expensive? It is, but it still comes out way ahead of our local Best Buy where a similar (actually, less powerful) battery retails for $39.99 plus tax...

Wednesday, August 26, 2009

Classy Sarah Palin fans react to Kennedy's death

Just look at the spelling in these gems. The correlation between the level of education and political views is very real...

"thank you for maintaining my belief in you as a real american, however this country is now much better off, one less socialist, anti freedom senator."

"Now if we could just talk God into taking Arlin Spector, Harry Reid,and Nancy Pelosi America would be Eutopia!"

"good riddens"

"If he makes it into Heaven (& I doubt he will with his stance on abortion) I hope that God makes him babysit all the aborted children for eternity. God have mercy on his soul."

"Ted Kennedy dying has made my day...."

"He cannot fillibuster God. Good ridencance to a sorry person."

"It's about time, we can only hope Pelosi and Ried will be joining him very soon. All 3 of them should be buried in Moscow for whom they work so tirelessly."

http://www.alternet.org/blogs/peek/142220/sarah_palin%27s_facebook_%27friends%27_celebrate_ted_kennedy%27s_death%3A_%22one_less_socialist%2C%22_%22good_riddens%22/

"Brilliant" marketing

I love it when people say that Microsoft has terrible products which only succeed because of our amazing marketing.

http://www.pcworld.com/article/170820/microsoft_apologizes_for_racially_charged_image_alteration.html

Friday, August 21, 2009

Introducing Black Square, a MS Word document review system

Black Square is to specs what Malevich is to code. This release is 0.1 - treat it as a preview, it has not been field-tested at all. There are known important bugs. But if you do feel adventurous, give it a whirl!

http://blacksquare.codeplex.com/

Big thanks to Eric White (http://blogs.msdn.com/ericwhite/) who coded the most important part of the system - the document merger.

For more background on Black Square, see the blog post where it was originally introduced: http://1-800-magic.blogspot.com/2009/07/lockingunlocking-word-doc-files.html

Physics lessons in Alabama

Driving to work today I was listening to yesterday's Marketplace podcast on my Zune.

They were reading a piece about schools in Alabama having big trouble finding teachers in math and sciences. Imagine recruiting a biology teacher who'd have to teach "alternatives" to evolution! - must be super hard indeed.

So they are importing teachers from Philippines. One school mentioned in the show had its first physics class in several years(!) and it had just 17 people enroll. No wonder such a staggering percentage of US population is convinced that Earth is 6000 years old...

Wednesday, August 19, 2009

Just say no to political correctness

'Something strange has happened in America in the nine months since Barack Obama was elected. It has best been summarised by the comedian Bill Maher: "The Democrats have moved to the right, and the Republicans have moved to a mental hospital."'

http://www.independent.co.uk/opinion/commentators/johann-hari/johann-hari-republicans-religion-and-the-triumph-of-unreason-1773994.html

We liberals brought this upon ourselves. We should stop acting like the idiots' point of view is entitled to respect and equal treatment in the media. It's not. The cooks, the crooks, and the cons - including the neocons - need to be called for what they are, loud and clear.

In particular, we should stop trying to prove that science and religion are somehow compatible. They are not.

Science requires preponderance of evidence. For science, if something is unobservable in principle, it does not exist.

Religion requires faith in tales written thousands of years ago by semi-literate shepherds, claiming the miracles that have never been observed in practice.

Most Americans are big boys and girls - it's OK to have them face the hard choice.

Either you believe in science and enjoy the fruits of labor of these godless scientists (http://people-press.org/report/?pageid=1550) - including antibiotics, computers, jet travel, and sanitation. Or choose supernatural and go back to the middle ages and 40 years average life span.

Trying to have it both ways is like trying to lose weight without a diet. It simply does not work.

Tuesday, August 18, 2009

Dude, have you heard of Netflix?

I am channeling the Fake Steve Jobs now, but damn it, two insane press stories a day...

> That's largely because they're not a darn thing worth watching or
> playing that uses Moonlight/Silverlight. Go ahead visit the
> Silverlight site; let me know when you find something compelling. I didn't.

http://blogs.computerworld.com/14570/moonlight_2_arrives_and_falls_flat_on_its_face

Netflix of course uses Silverlight for streaming the part of its content that's available online.

A pundit tries OpenGoo. Hilarity ensues.

OpenGoo is an open source counterpart to Google Docs. The idea is that you download and host it on your own servers. This C|Net reporter gets really, really confused by this concept:
http://news.cnet.com/8301-13505_3-10310817-16.html

In case they remove this article, here's the screen shot (click on it to read):


This is even funnier than Investor Business Daily's "Steven Hawking would have no chance under British health care" piece... I am wondering, what kind of college degrees do these people have? And what kinds of colleges graduate these "journalists"?

Friday, August 14, 2009

British healthcare

Apparently there's been a bunch of ads on TV feeding US populace the horror stories about how horrible NHS is.

This is what real British - you know, the ones actually living there and using British health care (where doctors by and large are government employees and the health care costs are almost entirely paid by the government) - have to say about it:

"Watching these debates is like reading National Geographic. It's just impossible, from a European perspective, to understand what these people are on about. Their political views seem as backwards and removed from the world we live in as a shaman casting magic spells."

http://www.politics.co.uk/analysis/health/comment-i-ll-never-understand-americans-$1318652.htm

Also, from Investor's Business Daily editorial on 8/3: "People such as scientist Stephen Hawking wouldn't have a chance in the U.K. where the National Health Service would say the quality of life of this brilliant man, because of his physical handicaps, is essentially worthless."

After much ridiculing on the interwebs, they removed the sentence. Pity, it was yet another proof that money != brain.

Monday, August 10, 2009

Bingin' Malevich

I've done a bit of egosurfing today (http://en.wikipedia.org/wiki/Egosurfing) and found that my code review system (http:/malevich.codeplex.com) shows up as #2 when searching for Malevich in Bing.


Frankly, I was surprised.

By comparison, Google search has it towards the bottom of the second page. Mondrian - Google's own code review tool which inspired Malevich - is at the very bottom of the first page.

Now, there was a fresh batch of conspiracy theories fomented by the "technical" pundits that claim that Microsoft uses Bing put down Apple (http://www.pcworld.com/article/169750/bing_search_reveals_promicrosoft_results.html). To me this is like a toddler pushing a nuclear submarine to help it go faster: Windows certainly does not need help from Bing to compete with Apple.

I think there is a very simple explanation for both Malevich and Mac vs PC phenomena. The search engine certainly uses clickthrough data to guide its searches. Bing's biggest market share in the geek population is Microsoft employees - there are tens of thousands people here that use Bing. The overall market share of Bing is small enough so that a few thousand users can swing the search results considerably - bringing Malevich-the-code-review-system (which is also extremely popular here) and articles critical of Apple on top.

Admittedly, this is not nearly as juicy as the "evil Microsoft adjust the search results to favor itself" conspiracy theories, but it appears to be the simplest one :-).

http://en.wikipedia.org/wiki/Occam's_Razor

Saturday, August 8, 2009

GI Joe, the movie

No, I have not seen it. But I've read Roger Ebert review of it, which is a masterpiece on its own.

http://rogerebert.suntimes.com/apps/pbcs.dll/article?AID=/20090807/REVIEWS/908079997

"The two teams also each have a skilled Ninja fighter from Japan. Why is this, you might ask? Because Japan is a huge market for CGI animation and videogames, that's why. It also has a sequence set in the Egyptian desert, although there are no shots of dead robots or topless pyramids. And Cobra headquarters are buried within the miles-deep ice of Arctic. You think construction costs are high here. At one point the ice cap is exploded real good so it will sink and crush the G. I. Joe's submarine. We thought ice floated in water but, no, you can see big falling ice chunks real good here. It must be only in your Coke that it floats."

Tuesday, August 4, 2009

Developer Connection

If you thought (like I did) that TFS documentation was bad, check out this beauty:

http://developer.apple.com/documentation/appleapplications/Reference/WebKitDOMRef/DOMSelection_idl/Classes/DOMSelection/index.html

As a mental exercise, try to guess what does empty() do? Does it empty the selection? Or does it return true if the selection is empty?

Monday, August 3, 2009

Obama's birth certificate - a Kenyan angle!

FROM: Mr. James Thambo,
Email:jamesthambo@workmail.co.za
jamesthambo@msn.com
TO: Orly Taitz, Esq (Orly.Taitz@aol.com)

KEEP AS CONFIDENTIAL

Dear Mrs. Orly Taitz:

I am Mr. James Thambo, a Barrister to US President Barrak Hussein Obama's great great uncle Matimor Thambo Hussein. He has died 3 days ago after being sick with Cancer, and now I am in Charge for Executing his In-heritance. Before he died he gave me a Birth Certificate for US President Barrak Hussein Obama issued By Kenian Republic in 1961 that Proves that Barrak Hussein Obama is not a US Citizen. It is Numbered 47O44 and Executed with All Appropriate Authority. I will give you the name of my bank where said Certificate is stored and other important information if I receive a positive reply from you.

I want you to be my partner, to secretly transfer the Certificate of Birth to the United States where it would be Sold on ebay. My business Partners here has estimated the Auction Value for the US President Barrak Hussein Obama's Birth Certificate to be $100,000,000 (U. S. Dollars!). I cannot Sell it myself Because I am a Barrister to the late US President Barrak Hussein Obama's great great uncle Matimor Thambo Hussein and they would suspect me if I sell it myself. All you would have to do is place this Item for bid on Ebay, and receive the money into your account.

Your share will be 30% which is $30,000,000 (U. S. Dollars!). My own share will be 69%, which is $69,000,000. We shall keep 1% which is $1,000,000 for expenses. Reach me immediately by mail so that I can give you further details. Also provide me your direct tel/fax to reach you, and your bank account number, and your credit card so I can Certify the Authenticity.

Thank you and God Bless,

Mr. James Thambo.

------------------------

http://www.huffingtonpost.com/2009/08/03/kenyan-birth-certificate_n_249850.html