28 December 2011

Science vs Faith flowchart

A while back a friend sent me a link of this science vs faith diagram that's been going around for more than a while (e.g. here and here and here).
Couldn't help but detect a hint of malevolence in the depiction of faith, so here's my own more reasonable version in response:
Science vs faith flowchart diagram

20 December 2011

Treating most warnings as errors

A common scenario in C# I find is that I would like to:

  1. Treat most warnings as errors by default
  2. But, treat a few specific warnings as just warnings.

I still want them to show up as warnings, which is to say that I don't want them to disappear altogether. 'CS0618 Obsolete' is a good example of this.

This cannot be achieved through the C# Project Properties editor, but it can be done by manually editing the .csproj file to add these two MSBuild properties:


This is vaguely referred to in the docs here, and the list of error codes can be found here.

19 December 2011

VS Macro Problems

Setting up a new computer I somehow managed to break Visual Studio macros. Finally found this, which fixed it. (See the Workaround tab).

Basically delete this magical file, then rerun the VS installer:

%ProgramFiles(x86)%\Microsoft Visual Studio 9.0\Common7\IDE\1033\Microsoft.VSDesignerUI.dll

30 November 2011

Binding, and Templates, and ContentControls, oh my!

I'm working in Silverlight 4, and have been trying to get a particular binding scenario to work right. It seems to be the case that try to satisfy any one or two requirements is easy enough, but when things start piling up, Silverlight can get a bit tricky.

My requirements:

  1. I'm creating a UserControl
  2. The UserControl itself needs to work inside a template.
  3. I want to define properties on the UserControl.
  4. I want a ContentControl in the UserControl
  5. I want to define a ContentTemplate on the content control
  6. And lastly, I want this inner template to bind to properties of the UserControl

The problems:

  • I can't name the UserControl and use ElementName inside the template, because the UserControl itself is also going to be used in a template, and ElementName doesn't always behave well in that situation.
  • I can't programatically set the DataContext or Bindings within the template, because Silverlight doesn't give programatic access to a template.
  • Setting the DataContext of the UserControl or the ContentControl to the UserControl itself doesn't help, because the DataContext of a template is actually the Content property of the ContentControl.
  • I can't set the Content of the ContentControl to the UserControl itself, because this makes Silverlight hang (presumably due to an infinite recursive loop).

So here's what I've got working. It's disgusting, but seems to work.

<ContentControl x:Name="content"/>
<TextBox Text="{Binding Self.SomeValue}" />

And the code...

public partial class MyControl : UserControl
public class Indirection
public ValueEditor Self { get; set; }

public MyControl()
this.content.Content = new Indirection() { Self = this };

public static readonly DependencyProperty SomeValueProperty = ...
public string SomeValue { ... }


So the idea is to set the ContentControl.Content to a proxy object that is not the control itself, but with a reference to the control, which can then be accessed in the path of the binding.

If you know a nicer way to do this, please leave a comment!

29 November 2011

Default parameters

How disappointing! I've just started to dip a toe into C# default parameters. And they have to be compile-time constants. Sure, it makes a lot of sense, but how about allowing static readonly properties as well?

All I want is this:

// Not too much to ask for?
void MyMethod(Guid someId = Guid.Empty)

26 November 2011

Now I've noticed the whine, it's going to drive me crazy

Way back in the '80s I cut my programmers teeth on a Microbee 32k. Back on a classy green and black CRT, with an audio cassettes for permanent storage.

One of the things I remember noticing was that while playing a tune (1-bit square wave), if I held down a key on the keyboard that the frequency would drop slightly. This was presumably due to the poor 2MHz CPU having to stop counting how long it has spent on each waveform to go and service the keyboard IRQ. How quaint.

Now, as I sit at a PC that's at between three and five orders of magnitude more powerful, depending on the measure, I've just noticed an errily familiar behavior.

With my sound turn up, I hear a faint low-pitched hum even though no sound is playing. Now, whenever I move my mouse it turns into a faint high-pitched squeal. I actually get one of two high pitches depending on how fast I'm moving the mouse.

Not 100% sure what's going on here, but as they're USB powered speakers, I'm guessing that the transmission of mousing data is causing some interference on the power rail. I guess it shouldn't be surprising given that people have figured out how to capture keystrokes from EM emissions.

24 November 2011

INotifyPropertyChanged code snippet

I've never really worried about getting into Visual Studio code snippets. But I've been using INotifyPropertyChanged a lot recently, so it got me looking at them again.

Found this cool snippet by Brian Schroer for filling out properties. Here's my slightly customized version of it to suit personal taste. Thanks Brian!

Steps to intall:

  1. Save the following to NotifyProperty.snippet on your desktop1
  2. In Visual Studio go to Tools / Code Snippets Manager
  3. Press Import and locate the file
  4. Import the file into My Code Snippets

Steps to use:

  1. In the code editor, type propn then press TAB twice.
  2. Immediately type the name of the property. Press tab.
  3. Then type the .Net Type name of the property.

The code:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Description>Code snippet for property and backing field in class implementing INotifyPropertyChanged</Description>
<Author>Brian Schroer</Author>
<!-- http://geekswithblogs.net/brians/archive/2010/07/27/inotifypropertychanged-with-less-typing-using-a-code-snippet.aspx -->
<ToolTip>Property type</ToolTip>
<ToolTip>Property name</ToolTip>
<Code Language="csharp">
<![CDATA[/// <summary>
/// Gets or sets the $property$
/// </summary>
public $type$ $property$
get { return _$property$;}
if (value != _$property$)
_$property$ = value;
private $type$ _$property$;

Some VS macros I can't live without

Here's a few Visual Studio Macros that I can't live without at the moment. They're somewhat pieced together from bits I found online and customized for my needs. Thanks to whoever you are!

In order:

  • Locates the currently open file in the Solution Explorer panel.
  • Attaches debugger to a service.
  • Attaches debugger to an open Silverlight window.

(Ugh! VB - Whoever thought it was a good idea to write whole applications in this stuff?)

Imports System
Imports EnvDTE100
Imports System.Diagnostics

Public Module AttachDebugger

Public Sub LocateCurrentFileInSolutionExplorer()
' Suggest binding to: Alt+L Alt+L
End Sub

Public Sub DebugMyService()
Dim p As EnvDTE.Process

For Each p In DTE.Debugger.LocalProcesses
If p.Name.ToLower().EndsWith("myservice.exe") Then
Exit Sub
End If
MsgBox("Could not find ReadinowSvc.exe")
End Sub

Sub DebugSilverlight()
'Thanks to http://www.dllshepherd.net/2011/05/silverlight-attach-to-process-shortcut.html
Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger
Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")
Dim dbgeng(1) As EnvDTE80.Engine
dbgeng(0) = trans.Engines.Item("Silverlight")

For Each process As EnvDTE80.Process2 In dbg2.GetProcesses(trans, Environment.MachineName)
If process.Name.Contains("iexplore.exe") Then
For Each program As EnvDTE.Program In process.Programs
If program.Name.Contains("Silverlight") Then
End If
End If
Catch ex As System.Exception
End Try
End Sub

End Module

15 November 2011

Binding UserControl properties in Silverlight

It seems to me that the logical thing to do in Silverlight when building some sort of composite control is to create a UserControl, expose some properties, add some controls to the user control, and bind them to those properties.

For example, a custom CoffeeMug control might want to expose MugColor and PercentFull properties. However, it's taken me quite a while to figure out how to get this to work properly.

The problem is in selecting the binding source. WCF offers the FindAncestors binding helper, which can be used to locate the user control relative to the control being bound, but Silverlight offers no counterpart.

So initially I had been using ElementName:

<UserControl x:Name="thisControl" ... >
<Rectangle x:Name="mugShape"
Background="{Binding MugColor, ElementName=thisControl}" />

This works pretty well in simple circumstances, but it wasn't working for me in DataTemplate scenarios, such as in the DataGrid. I was having similar problems with using custom controls in custom ComboBox and ListBox data templates.

Well, apparently its a known shortcoming of Silverlight. When the data template is asked to make a concrete instance, it no longer carries the necessary context information to resolve element names directly. It can however be overcome by manually specifying the binding in code.

public CoffeeMug()
this.Loaded += new new RoutedEventHandler(Control_Loaded);
void Control_Loaded(object sender, RoutedEventArgs e)
Binding binding = new Binding("MugColor");
binding.Source = this;
// remember to set binding.Mode = TwoWay if applicable
this.mugShape.SetBinding(Shape.BackgroundProperty, binding);

This guy has also hacked together a behavior to make everything much nicer in an MVVM environment as well, but I don't think I'd go to that much effort personally.

10 November 2011

Debugging immediate variables

Here's a neat Visual Studio debugging trick I just stumbled on today by accident. Sometimes, it can be very handy to be able to refer back to information from another frame in the stack, or from a previous call to the same function.

Step 1

When you're at a location of interest, use the intermediate window to declare a new variable and assign a value to it. (Remember to give it a type declaration or 'var').

// Type this into the 'Immediate' window
var tmp = data;

Step 2

Now, when you're at a completely different location, either in a different method, or even at a different calling of the same method, you can still inspect your immediate variable in the watch window.

Obviously the temp variable is not going to automatically update if the source it came from gets updated, but it can still be pretty handy sometime, for example to compare two values to see if they're the same.

Presumably we're also holding onto a reference now that would prevent it from being garbage collected, so in a large environment it might be a good idea to set the temp variable back to null when you're done with it.

What happens if you set it to a variable name that is then used at a subsequent break point? Answer: it gets blown away and replaced with the new value (and type).

9 November 2011

Interface implementation and covariance

I was a bit disappointed to discover that the following code doesn't compile in C# 4. In particular, it seems that as the 'Thing' property is only a getter on the interface, any concrete implementation should be able to return a derived type, since it can be guaranteed that anyone getting the parent type from the interface will also be able to operate on the child type.

class Thing { }

class SpecificThing : Thing { }

interface IThingContainer
Thing Thing { get; }

class SpecificThingContainer : IThingContainer
public SpecificThing Thing { get; set; }

30 October 2011

Password policy

So FTP stopped working for one of the GoDaddy websites I look after. It seems they decided the password didn't comply to their password policy, so they've deactivated it until I update the password. Fair enough. Perhaps.

However, I'm amused by their new password policy regarding special characters.

1. Password must contain special characters.

2. Password cannot contain ?, ^, ', ", :, ;, $, &, <, >, ~, ` or space.

I can't help but think of XKCD 936.

26 October 2011

Enum.GetValues in Silverlight

It seems Enum.GetValues is not defined in the Silverlight client profile.
The owl says it best.
Oh well, a hacking we shall go.

15 October 2011

Why won't my Visual Studio macros run?

OK, so my macros are silently failing to run. Much Googling and experimenting rules out multiple possible options, including:
  • what if macros were disabled in the Visual Studio options?
  • what if I'd somehow corrupted my Visual Studio installation?
  • what if it's a UAC issue on Windows 7?
And more magical possibilities fill the imagination.

No dummy - my macro just has a syntax error in it. (Well, more specifically, I had another test module in the same project, which had a syntax error in it).

Still, it would have been nice if Visual Studio had told me rather than giving me the silent treatment.

P.S. Here's the handy macro I was settings up to allow me to locate an open file in the solution explorer.
P.P.S. Actually remember to press the 'Assign' button when setting up keyboard bindings. Clearly I'm having a dumb day.

20 September 2011

Non-associativity of SQL table joins

Some time back I was trying to understand how Microsoft SQL Server handled bracketing and order of operations on table joins. Here's a breakdown I put together on how it works. Possible boring, although possibly useful for not shooting oneself in the foot.

In short: the order and backeting of table joins may matter if you're mixing different types of joins together.

Background Information

This is the boring stuff. Skip down to the next major section if already familiar.

What is associativity

Consider the following equations:
(1 + 2) + 3 = 6
1 + (2 + 3) = 6
Both equations are the same irrespective of the bracket grouping. Addition is said to be an associative operation.

However, the same is not true for subtraction.
(1 - 2) - 3 = -4
1 - (2 - 3) = 2
Subtraction is a non-associative operation.

What is commutativity

Consider the following equations:
a + b = b + a
a / b != b / a
Addition is commutative, but division is not. That is, the order of the operands matter.

It should be no surprise that in T-SQL inner joins are commutative, but left and right joins are not, rather they reverse roles.
  • A inner join B is equivalent to B inner join A
  • A left join B is equivalent to B right join A

Left-hand associativity in the absence of bracketing

T-SQL parses joins using left-hand associativity. That is, there is an implicit grouping to the left.
  • A join-type1 B join-type2 C is equivalent to (A join-type1 B) join-type2 C, which implies that
  • C join-type1 B join-type2 A is equivalent to A reversed-join-type2 (B reversed-join-type1 C)
This is to say, the following discussion applies equally to join ordering as it does to join grouping.

(Non) Associativity of SQL joins

If SQL statements present their tables in the same order, and use the same join and 'ON' clauses, then they may still give different results if the second join is bracketed.

This is because left and right joins produce null values in the absence of rows in the auxillary table. These null rows survive if a second, restrictive, join is applied only to the auxillary table. However if the grouping of joins is such that the second, restrictive, join applies to the whole expression then these null rows don't survive.

In particular:

Left-Hand Bracketed Right-Hand Bracketed Equivalence
(A inner B) inner C A inner (B inner C) Equivalent
(A left B) inner C A left (B inner C) Not equivalent
(A right B) inner C A right (B inner C) Equivalent
(A full B) inner C A full (B inner C) Not equivalent
(A inner B) left C A inner (B left C) Equivalent
(A left B) left C A left (B left C) Equivalent
(A right B) left C A right (B left C) Equivalent
(A full B) left C A full (B left C) Equivalent
(A inner B) right C A inner (B right C) Not equivalent
(A left B) right C A left (B right C) Not equivalent
(A right B) right C A right (B right C) Equivalent
(A full B) right C A full (B right C) Not equivalent
(A inner B) full C A inner (B full C) Not equivalent
(A left B) full C A left (B full C) Not equivalent
(A right B) full C A right (B full C) Equivalent
(A full B) full C A full (B full C) Equivalent

The same information can be presented (row for row) as:

Forward ordering Reverse ordering Equivalence
A inner B inner C C inner B inner A Equivalent
A left B inner C C inner B right A Not equivalent
A right B inner C C inner B left A Equivalent
A full B inner C C inner B full A Not equivalent
A inner B left C C right B inner A Equivalent
A left B left C C right B right A Equivalent
A right B left C C right B left A Equivalent
A full B left C C right B full A Equivalent
A inner B right C C left B inner A Not equivalent
A left B right C C left B right A Not equivalent
A right B right C C left B left A Equivalent
A full B right C C left B full A Not equivalent
A inner B full C C full B inner A Not equivalent
A left B full C C full B right A Not equivalent
A right B full C C full B left A Equivalent
A full B full C C full B full A Equivalent

In some cases equivalence can be restored by adding a WHERE clause that removes the null rows, however this correction can generally only be made in one direction.

The instance of A left B inner C is demonstrated as follows. The first expression has no bracketing, which is equivalent to left-hand bracketing because the T-SQL parser treats joins as left-hand associativity.
A(a) as ( select 1 union select 3 union select 5 union select 7 ), -- A contains 1,3,5,7
B(b) as ( select 2 union select 3 union select 6 union select 7 ), -- B contains 2,3,6,7
C(c) as ( select 4 union select 5 union select 6 union select 7 ) -- C contains 4,5,6,7
select a, b, c
from A
left join B on B.b = A.a
join C on C.c = B.b
The query above returns

a b c
7 7 7

However, if we bracket the second join, then the result changes:
A(a) as ( select 1 union select 3 union select 5 union select 7 ), -- A contains 1,3,5,7
B(b) as ( select 2 union select 3 union select 6 union select 7 ), -- B contains 2,3,6,7
C(c) as ( select 4 union select 5 union select 6 union select 7 ) -- C contains 4,5,6,7
select a, b, c
from A
left join (
join C on C.c = B.b
) on B.b = A.a
This query returns:

a b c
1 null null
3 null null
5 null null
7 7 7

The reason here is that when C is inner joined to B first, it constrains B, but then the entire group has no impact on A, due to the left join. Therefore A can return all of its rows. However, in the previous query, the left join between A and B occurs first. Therefore the more restrictive inner join applies to the rows in A first.

18 September 2011

Rubik's Snake Combinations

I've had a long standing attraction to the Rubik's Snake puzzle for many years, including being involved with glSnake. (Scroll to the red bit for final answer)

One question of interest was how many unique configurations of the Snake are possible. The Snake consists of 24 interconnecting prisms, with each interconnect able to be in one of four possible positions. This leads to a trivial upper bound of 423 = 70,368,744,177,664 distinct combinations. But many of these are not actually possible, as they may be configurations where the snake impossibly passes through itself. And most configurations will appear twice as mirror images.

A trivial lower bound can be reached by starting with a straight snake and then only permuting every other junction. Every rotation will be along a parallel axis, giving rise to pairs of interconnects that are always on discrete planes: meaning that the snake can never bend back on itself. Halving guarantees no reflections. (Symmetrical would only be counted once, but this is a lower-bound). This gives 411/2 = 2,097,152 as a trivial lower bound.

As an enhanced lower bound, we could observe that the snake prisms exits on a 3-dimensional grid of cubes. At any time the next piece could lie in the adjacent voxel in either the x, y or z direction (but can never follow the same axis twice). If we restrict ourselves to only ever advancing in the positive direction on each axis, then we always have a choice of one of (+y or +z) or (+x or +z) or (+x or +y), depending on whether our previous move was +x, +y, or +z respectively. In any case we have 2 choices. This gives us 223, which is not any better than before. However we can extend the idea: if we partition the space into planes normal to the x axis, then any time we extend in the +x direction, without loss of generality we can start moving in in any of the four +/-y or +/-z, i.e. four choices. From there we can move +x again, or either +/- along the axis we didn't previously chose. I.e. 3 choices. In this way we never fold back to previous planes. From that point on we only have 2 choices: to continue moving in a straight diagonal, or move onto the next x plane. We can represent this as the following Markovian chain:

0020Refined lower-bound:
1110(approx 418.15)

That's about as far as I could get without using numerical methods. (I also had a complicated, slightly improved, upper bound). So I wrote a program to walk every possible combination, which back tracks whenever it found a collision. Written as a single-threaded service to run in the background this took about five months to execute. This gave the following answer:

13,535,886,319,159 = approx 421.81 combinations, including possible mirror image duplicates.
Update: 5th Aug 2022: Corrected to 13,446,591,920,995 = approx 421.81. See below.

To handle mirror images, each time it encounters a complete snake it then converts it to a normalized form as follows. The snake is represented as a string of 23 numbers (from 0 to 3). The string is then reversed. The two strings are compared lexicographically and the smaller is the normal form. If the string matches the normal form, then it is counted, otherwise it is skipped. This way potential reflections that appear twice can be counted once, but symmetrical snakes also get counted once. Note that I'm interpreting mirror images here as applying the same turns from either end, not if the volume produced is a mirror image. For example, I'm counting a left-handed corkscrew snake and a right-handed corkscrew snake as discrete. But if you just twist one piece 180 degrees at the start or the end of the snake, then I count that as the same snake.

When ignoring reflections like this, the exhaustive search gives this result. Total number of possible snake configurations that don't overlap, and ignoring mirror repeats:

Final Answer = 6,770,518,220,623 = approx 421.31
Update: 5th Aug 2022: Corrected to 6,721,828,475,867 = approx 421.31. See below.

Behold. OK, it only took another two years to getting around to writing this up any telling the world. (mainly because I started writing up a detailed paper on the method, which quickly got boring). Hopefully anyone else who has tried this got the same answer.

So table of numbers: (Update: see below for correction)

MethodNumberApprox as exponent
Trivial lower bound2,097,152410.5
Refined lower bound85,149,351,936418.15
Final answer6,770,518,220,623421.31Corrected: 6,721,828,475,867
Exhaustive with duplicates13,535,886,319,159421.81Corrected: 13,446,591,920,995
Refined upper bound30,002,572,532,736422.60
Trivial upper bound70,368,744,177,664423.00

Along the way I also discovered that there are about 64,546,391 Corrected: 63,970,851 (approx 412.97 unique cyclic paths where the head and tail of the snake connect. Also, only a relatively small number of snakes are symmetrical.

I should probably go and get some sun, or social interaction, or something now.

Update: 5th Aug 2022

It turned out that my original calculation was wrong. I wrote another Snake Calculator program while playing around with another problem - and results didn't quite match. The good thing about now having two independent programs is that each could be used to find and fix bugs in the other. The original program didn't quite handle an edge-case correctly. Further inspired by hearing about "The Soul of the Snake" I've re-run the program on a full length snake.

With both programs now agreeing for various length snakes, I now feel pretty confident about the following answer being correct for length 24:

Final Corrected Answer = 6,721,828,475,867 = approx 421.31

MethodNumberApprox as exponent
Trivial lower bound2,097,152410.5
Refined lower bound85,149,351,936418.15
Final answer6,721,828,475,867421.31
Exhaustive with duplicates13,446,591,920,995421.81
Refined upper bound30,002,572,532,736422.60
Trivial upper bound70,368,744,177,664423.00

(Yes, the difference is less than 1%, so the exponents are still pretty much the same)

The Internet is now a little bit more correct and, to offset the drop in entropy, a server somewhere in an AWS data centre has been slightly warmed.

14 September 2011

Covariance contravariance inconvenience

I love the covariance/contavariance support in C#4. That is the mechanism that lets you implicitly convert from an IEnumerable<Child> to an IEnumerable<Parent> was expected (covariance), and an IComparable<Parent> can be converted to IComparable<Child> (contravariance). Here Child obviously inherits from Parent.

One of the biggest problems though is that this only works for classes and interfaces where the type parameters are either all outbound or all inbound for covariance and contravariance respectively. E.g. no methods on IEnumerable<T> accept T, which is covariance. And no methods on IComparable<T> return T. Eric Lippert wrote a bunch of posts worth reading about while the compiler team were considering variance.

This is somewhat inconvenient. For example it means that IList<Child> cannot be assigned to IList<Parent>, or vice-versa, because IList<T> methods both accept and return T. And often one may wish to write a method using IList<T> rather than IEnumerable<T> as it allows for direct element access. It is particularly frustrating because I may well only need to use the methods that read from the list, and so if IReadonlyList<T> existed then all might be sweet.

Now I was wrestling with this kind of thing the other day when I read Lippert's post on What is this thing called a Type, which got me thinking about it all again. Here he's again talking about how various operations tranform one Type into another.

OK, to the point. Here's the idea I was thinking. What if it were possible to create modified types based on existing interfaces and classes that only present the input or output methods.

For example (and to use Lippert's Giraffe inherits from Mammal inherits from Animal example):
IList<out Mammal> and IList<in Mammal> becomes types that are declarable in code.

IList<out Mammal> exposes only the subset of IList<Mammal> that is covariant. I.e. the methods for reading from the list, but none of the methods that accept T parameters. IList<out Giraffe> could be assigned to IList<out Mammal>. As a type, IList<out Mammal> itself would be smaller than IList<Mammal>, so it is always possible to convert from IList<Mammal> to IList<out Mammal>. So, finally IList<Giraffe> could be assigned to IList<out Mammal>.

Likewise, IList<in Mammal> exposes only the subset of IList<Mammal> that is contravarient, exposing only the methods for modifying a list. By the same (but contravariant) argument, IList<Giraffe> would be assignable to IList<in Mammal>.

Some code we could then write:

static void FeedMammals(IList<out Mammal> mammals)
// mammals.Count is available as it doesn't use the type parameter.
// Using an old-style for-loop to illustrate we don't want to use IEnumerable<T>
for (int i=0; i<mammals.Count; i++) parameter
// only the getter is available, as it is outbound

// But these are illegal:
mammals[0] = new Mammal(); // setter
mammals.Insert(0, new Mammal()); // method that accepts T

And call with:

List<Giraffe> giraffes = ...;
FeedMammals(giraffes); // yay, cast from List<Giraffe> to IList<out Mammal>

Similarly we could have:

static void AddBabyMammals(IList<in Mammal> mammals)
// New baby born
Mammal baby = GetNewBabyMammal();
mammals.Insert(0, baby);

//Setter is also OK to replace a value
mammals[0] = baby;

// But these would be illegal
Mammal m1 = mammals[0]; // getter
foreach (var m2 in mammals) // call to enumarator, because its 'out'

And call with:

List<Animal> animals = ...;
AddBabyMammals(animals); // yay, cast from List<Giraffe> to IList<out Mammal>

This would be a wonderful feature. Anyway, that's my two cents.

It's been a while

...a very long long while since I made a post. One may say that after dipping my toe into the Interblag to test the water for a while, I forgot all about it. And then subsequently forgot my password, followed by the email address I used, the login for that email, and finally (or additionally) the name of the blog according to blogger (since the only thing I could remember was blog.ylett.com). Trying to remember if I used to be this forgetful.

Needless to say, the bar was slightly raised for making any subsequent posts. Over the years, every now and then an idea would pop into my mind, followed by "oh, I could blog about that... except I'll have to deal with this mess." Cue chirping crickets.

Problem now solved. Now all I have to do is to figure out how to get back down to just one Google account (since I somehow managed to get my gmail on a different account, and Google+ on a different one again). And think of something to write about.