Tuesday, February 19, 2013

Codility Equi Task

Codility is a site that tests coders. Their demo problem task is called Equi. You can read the full description of the problem and solutions in a number of different languages on their blog post about it: Solutions for task Equi

I was somewhat surprise at the complexity of the code required in many languages. Using C#'s LINQ syntax makes it obvious and simple to write:

static int equi(int[] A)
{
    for (int i = 0; i < A.Length; i++)
    {
        if (A.Take(i).Sum() == A.Skip(i + 1).Sum())
        {
            return i;
        }
    }
    return -1;
}

It's also not difficult to modify that function to return all the equilibrium indexes: 

static IEnumerable<int> equi2(int[] A)
{
    for (int i = 0; i < A.Length; i++)
    {
        if (A.Take(i).Sum() == A.Skip(i+1).Sum())
        {
            yield return i;
        }
    }
}

Note that this solution although simple and elegant to write is not performant because the full array get summed in each loop. i.e. the time complexity of this solution is O(n^2). For a time linear solution you would increase and decrease a left and right sum variable as you moved through the index and compare those values.

Sunday, November 18, 2012

Overloading methods in TypeScript

At yesterday's presentation at Desert Code Camp I felt that I gave an inadequate explanation on how you would implement an overloaded method in TypeScript. Hopefully this code snippet will address that:

interface Thing {
    a: number;
    b: string;
    foo(x: string): string;
    foo(n: number): number;
}

function myfunction(myParam: Thing) {
    var stringResult: string = myParam.foo(myParam.b);
    var numberResult: number = myParam.foo(myParam.a);

    console.log(stringResult);
    console.log(numberResult);
}


var myObj = {
    a: 16,
    b: "My String",
    foo: (x: any) => {
        if (x && typeof x === 'string') {
            return x.length.toString();
        } else if (x && typeof x === 'number') {
            return x * x;
        } else {
            if (x) {
                throw { message: "null parameter is unsupported" };
            } else {
                throw { message: "unsupported type: " + typeof x };
            }
        }
    }
}

myfunction(myObj);

Note that in myfunction() you would not be able to call myParam.foo() with any other type of parameter than string or number because the compiler will complain about that. So even though the implementation checks for another type in the final else you cannot pass in another type (e.g. object) because TypeScript won't let you.

Friday, November 16, 2012

Toastmasters Timer in TypeScript

I recently wrote a Toastmasters Timer in TypeScript for two reasons:
  1. The other timers that I found out on the web didn't quite do what I wanted them to do and
  2. I needed a small project to learn TypeScript
All of the timers I found followed the same approximate format as this one created by Stan Birdwell (Central Toastmasters 2277-31) and Michael K. Heney (Goddard Toastmasters 3496-36):




I was looking for something that would should the time in a large font on the page and change the color of a large portion of the page at each stage. I needed this to be able to constantly monitor the time and color from a distance while I am pacing around the room practicing a speech. I didn't want to have to lean forward to see the time. This is the final result: Toastmasters Timer






Here is the TypeScript source code for that timer (the compiled JS, HTML and CSS can be pulled from the page):
/// <reference path="jquery.d.ts" />

class SpeechType {
    constructor (public name: string, public greenTime: string, public yellowTime: string, public redTime: string, public id: string) {
    }
}

class TSTimer {
    timerToken: number;
    speeches: SpeechType[];
    started: bool;
    startTime: Date;
    stopTime: Date;
    green: number;
    yellow: number;
    red: number;

    constructor (speeches: SpeechType[]) {
        this.started = false;
        this.speeches = speeches;

        $.each(this.speeches, (indexInArray: number, valueOfElement: SpeechType) => {
            var newButton = $('<span>')
                .attr('id', valueOfElement.id)
                .addClass('speech-type')
                .html(valueOfElement.name);
            newButton.click( (event) => {
                this.activateSpeech($(event.target).attr('id'));
            });
            newButton.appendTo('#buttons');
        });
       
        $(window).resize( () => {
            this.resizeTime();
        });
        // call on initialization in case the browser starts off narrow
        this.resizeTime();

        $('#btnReset').click( () => {
            this.resetButton();
        });

        $('#btnStart').click(() => {
            this.startButton();
        });

    }

    resetButton() {
        this.stop();
        $('#trafficlight').text('0:00');
        $('#body').css('background-color', '#EFEEEF');
        this.startTime = null;
    }

    startButton() {
        if (this.started) {
            this.stop();
        } else {
            this.start();
        }
    };

    resizeTime() {
        var width = $(window).width();
        var x: number = Math.floor((width < 900) ? (width / 900) * 28 : 28);
        $('#trafficlight').css('font-size', x + 'em');
    }

    setElementText(elapsedSeconds: number) {
        $('#trafficlight').text(this.formatTime(elapsedSeconds));
        if (elapsedSeconds >= this.red) {
            $('#body').css('background-color', '#FF4040');
        } else if (elapsedSeconds >= this.yellow) {
            $('#body').css('background-color', '#FCDC3B');
        } else if (elapsedSeconds >= this.green) {
            $('#body').css('background-color', '#A7DA7E');
        }
    }

    timerEvent() {
        if (!this.startTime) {
            this.startTime = new Date();
        }
        var timeNow = new Date();
        var elapsedSeconds: number = this.timeDiffInSeconds(this.startTime, timeNow);
        this.setElementText(elapsedSeconds);
    }

    // Returns the difference in seconds between
    timeDiffInSeconds(earlyTime: Date, lateTime: Date): number {
        var diff: number = lateTime.getTime() - earlyTime.getTime();
        return Math.floor(diff / 1000);
    }

    formatTime(elapsedSeconds: number) {
        var minutes: number = Math.floor(elapsedSeconds / 60);
        var seconds: number = elapsedSeconds % 60;
        return minutes + ":" + ((seconds < 10) ? "0" + seconds.toString() : seconds.toString());
    }

    start() {
        $('#btnStart').val('Stop');
        this.started = true;
        if (this.startTime) {
            // i.e. no reset since the last time it was started so adjust the start time
            // to reflect the time that's elapsed since it was stopped.
            var newStartTime = new Date().getTime() - (this.stopTime.getTime() - this.startTime.getTime());
            this.startTime.setTime(newStartTime);
        }
        this.green = this.getSecondsFromTextBox('#green-light');
        this.yellow = this.getSecondsFromTextBox('#yellow-light');
        this.red = this.getSecondsFromTextBox('#red-light');
        this.timerToken = setInterval(() => this.timerEvent(), 1000);
    }

    stop() {
        $('#btnStart').val('Start');
        this.started = false;
        this.stopTime = new Date();
        clearTimeout(this.timerToken);
    }
   

    getSecondsFromTextBox(id: string): number {
        var greenLight: string = $(id).val();
        return parseInt(greenLight.split(':')[0]) * 60 + parseInt(greenLight.split(':')[1]);
    }

    setDefault() {
        this.activateSpeech('st-standard');
    }

    activateSpeech(speechId: string) {
        $.each(this.speeches, function (indexInArray: number, valueOfElement: SpeechType) {
            if (valueOfElement.id === speechId) {
                $('#green-light').val(valueOfElement.greenTime);
                $('#yellow-light').val(valueOfElement.yellowTime);
                $('#red-light').val(valueOfElement.redTime);
            }
        });
        $('.active-speech').removeClass('active-speech');
        $('#' + speechId).addClass('active-speech');
    }
}


$(document).ready(function () {
    var speeches = [];
    speeches.push(new SpeechType("Table&nbsp;Topics", "1:00", "1:30", "2:00", "st-table-topics"));
    speeches.push(new SpeechType("Evaluation", "2:00", "2:30", "3:00", "st-evaluation"));
    speeches.push(new SpeechType("Icebreaker", "4:00", "5:00", "6:00", "st-icebreaker"));
    speeches.push(new SpeechType("Standard", "5:00", "6:00", "7:00", "st-standard"));
    speeches.push(new SpeechType("Advanced", "8:00", "9:00", "10:00", "st-advanced"));
    speeches.push(new SpeechType("Test", "0:02", "0:04", "0:06", "st-test"));
    var timer = new TSTimer(speeches);

    timer.setDefault();
});



Saturday, November 10, 2012

TypeScript template doesn't work in FireFox

If you're using Visual Studio 2012 and you've added the TypeScript for Microsoft Visual Studio 2012 extension then you will be able to jump start a TypeScript project using the template. If you click File > New > Project and look under the Visual C# templates you'll see a template called HTML Application with TypeScript. Selecting this template will generate a new project with a TypeScript file with the following contents:

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;
   
    constructor (element: HTMLElement) {
        this.element = element;
        this.element.innerText += "The time is: ";
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.span.innerText = new Date().toUTCString();
    }

    start() {
        this.timerToken = setInterval(() =>
          this.span.innerText = new Date().toUTCString(), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }
}

window.onload = () => {
    var el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};

The problem with this code is the use of the .innerText property which is called .textContent in FireFox.

Here is a replacement to the above template that will work in FireFox and other browsers to get you moving forward again.

Tested on:
FireFox 16.0.2
Safari for Windows 5.1.7
IE9
Chrome 23.0.1271.64 m
Opera 12.10

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;
    hasInnerText: bool;
   
    constructor (element: HTMLElement) {
        this.element = element;
        // Firefox uses textContent
        this.hasInnerText = !this.element.textContent;
        this.appendElementText(this.element, "The time is: ");
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.setElementText(this.span, new Date().toUTCString());
    }

    setElementText(elem: HTMLElement, value: string) {
        if (this.hasInnerText) {
            elem.innerText = value;
        } else {
            elem.textContent = value;
        }
    }

    appendElementText(elem: HTMLElement, value: string) {
        if (this.hasInnerText) {
            elem.innerText += value;
        } else {
            elem.textContent += value;
        }
    }

    start() {
        this.timerToken = setInterval(() =>
          this.setElementText(this.span, new Date().toUTCString()), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }   
}

window.onload = () => {
    var el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};


 

 

Friday, November 2, 2012

TypeScript

I'm going to be giving a presentation called TypeScript, what's all the fuss? at the Fall 2012 Desert Code Camp on November 17, 2012. I'll be adding a list of resources and my slides to this blog post.

TypeScript Resources:

Here's an example of method overloading in TypeScript which I didn't cover adequately in this presentation.

Thursday, July 19, 2012

Amazon SES and Easy DKIM

I've been using Amazon's Simple Email Service (SES) for at about a year now and it's been great. Failures are very rare and the API and libraries provided are excellent. With SES you build up trust which increases your volume quota (per 24 hour period) and your send rate (per second). My only complaint is that my send rate is not as high as I want it to be.

Today I received an email saying the I can easily DKIM-Sign my emails with Easy DKIM in the SES control panel (aka AWS Management Console). This was exciting because I'd never heard of DKIM before and it sounded so easy to do.

It turned out to be pathetically easy. In the AWS Management Console you simply select your email address and under the DKIM tab you click the "Generate DKIM Settings" button.

This generates 3 CNAME records that you have to update your DNS records with. I use Go Daddy so I launched the DNS Manager for that domain and under the CNAME section I clicked the Quick Add button and copy/pasted the 3 keys and values from Amazon. Both Go Daddy and Amazon warned that it could take up to 48 to 72 hours to complete, however, less than 2 hours later I received an email from Amazon stating that it was complete and I tested it and it worked.

The emails sent through SES on behalf of that domain (to my gmail account) now have 2 new headers:

DomainKey-Status: good

I'm guessing that GMail added this header to signal that it validated the DomainKey that Amazon had created.

DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple; t=12345;
    s=fa; d=fake.com;
    h=From:Reply-To:To:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Date:Message-ID;
    bh=fake
    b=fake

This is what the actual signature looks like. I've replaced the values to protect the innocent. The one and two letter keys stand for:

v = version
a = algorithm (signing algorithm)
q = query (default query method)
c = canonicalization algorithm for header and body
t = timestamp
s = selector
d = domain (signing domain)
h = header (a list of the signed header fields)
bh = body hash
b = body (the actual digital signature of the contents, both header and body)

If you're already using SES then this is a no-brainer. Total time is around 15 minutes at the most. The first 10 minutes is initial setup in Amazon and then with your registrar. Once you get the confirmation back from Amazon (about 2 hours later) that it's been setup then take the other 5 minutes to go back into your Amazon account and enable DKIM by clicking a link. The first 2 times I clicked the link it didn't work so keep clicking it until it does.

If you want to test it then send yourself an email through SES before you enable DKIM and then another afterwards and compare the headers.

 

Wednesday, June 27, 2012

Ad Management services versus doing it yourself

Should you use an advertising management solution such as DFP Small Business or do it yourself?

(DFP stands for DoubleClick For Publishers. DoubleClick was bought by Google in March 2008 for $3.1 billion.)

If someone approaches me and wants to advertise on one of my sites my usual response is to tell them to use Google's AdWords program and target my site or pages on my site with their adverts. In my opinion, Google does a great job and they only take 32%. I don't have to get involved at all.

Sometimes it is worth my while to split that 32% with an advertiser and I'll add their advert to a site for a given time period.

The advantages of doing it yourself are advantages for the the advertiser, not for you the publisher:

  • If you host the advert's image and text on your site then it is almost impossible for Ad Blockers to block the ads because they are integrated images.
  • Your advertiser gets a one-way follow link to their site from your page.

The disadvantage is that you have to create an embedded solution to allow advertisers to upload their adverts to your site (the sophisticated solution) or you have to do it manually. Doing this yourself requires work on your part either way and you will have to measure the Return On Investment and if that extra revenue over AdSense is worth it.