All posts by Marc Durdin

What value my life?

I sometimes ponder on what I value in my life. Or to put it another way, what it would mean to lose certain things. I go through the things, the activities, the people in my life and imagine what it would be to no longer have them there. My house? It’s a lovely house… We put a lot of effort into building it and I enjoy living in it. All the same, I can imagine not having it, and it doesn’t hurt that much. It’s a place to live and I thank God for His provision and yet I don’t feel bound to it. My car? Pfft. It gets us from A to B and the thing I value most about it is that it rarely goes wrong.

So how about my bike? I certainly enjoy riding it. But it just costs money to replace, and God has blessed us with enough money to buy a bike. I don’t feel so attached to the bike itself. Ok then, how about cycling itself?

I do love riding my bike. The challenges, racing and riding with mates, pushing myself physically, the exercise and the well-being it brings. And yet… I could let it all go, and my only real regrets would be losing touch with riding buddies, and the loss of fitness. I’d miss riding, but it wouldn’t destroy me.

My job, my career? The code I create — Keyman; the business I’ve poured myself into — Tavultesoft; and the relationships built, respect earned? I derive a lot of satisfaction from creating computer software: turning a concept into a tool that can really help people. So what if that all disappeared? My position, my abilities, my reputation, my creations? Would that devastate?

It would hurt. I’m sure it would hurt more than I can really imagine. And yet, I can imagine a world without this job. I can’t imagine what I would do, but I can imagine it.

And so as we cut closer to the quick, I start to draw back. All these various blessings of God — those that I take for granted through to those that I spend much of my waking life focused on — stripped away. Like Job. What if it happened to me? Can I really comprehend the pain of the loss of all that?

That pain. It would be as nothing to the loss of my family. That’s the one place I cannot imagine arriving at. The loneliness, my tether to reality severed, my life, my soul, spinning through the void. My wife and daughters: those who are so close and can infuriate, hurt, and exasperate. Those three people who bring such joy to me, a joy that others can only glimpse a pale reflection of when I post a comment on Facebook, or drop an email to them. Even to contemplate losing them stops my heart and starts a cold, cold hurt deep within.

I can’t imagine it. I shy away from the very idea. Job lost his whole family, everything, and yet he would not curse God, nor turn from Him. Do I have such a love for Him that I could continue to worship Him in the face of such devastation? To look for comfort, probably. To be angry with Him, almost certainly! But to worship Him? I don’t know, and my prayer is that He would not allow me to suffer to such a degree that I could come to doubt His love for me.

It is a selfish prayer. So many people in this world suffer such loss that I couldn’t bear. I sit here safe and contemplate this from the safety of my warm home, with my family around me, in a safe and comfortable country. Lord God, shatter me, make me dependent on You and You alone. I am too scared to ask this of You. But this is my tiny step of faith. I am like the father who asked Jesus “help me believe!”

Freedom Ride

Snow to 800 metres. 55km/h winds. Possible hail. That was the forecast that greeted us at 4:30am as we met at Tim’s house, all psyched up for our 200km Freedom Ride over the central plateau back to Hobart. As we drove up north in the bus, 11 testosterone fuelled blokes discussed whether or not we would be so soft that we would fall back on the “easy” bad weather backup route. Or whether we would be hard men and tough out the weather.

As each rider spoke up and agreed that they wanted to do the central plateau route, and then also said that it would probably be a bad idea, I recalled the Launceston – New Norfolk race a few years back, that took the same route, when only 7 pro riders finished in similarly atrocious weather.

Amazingly, we had almost unanimous agreement to go the soft option. Still 200km, still over 2000m climbing, but via the east coast, with its generally milder weather. I was relieved: the plateau route was sounding more and more like a really dangerous choice, with freezing cold, wet and gusty descents and general misery!

We were riding for the charity Live Free Tassie, which works to help young people break free from addictions.  It’s not too late to donate — please do!

Preparing to leave Campbell Town.  Weather looks great so far.

At just after 7am, we set out from Campbell Town towards the east coast, immediately hitting the largest climb of the whole ride, which we took gently while we warmed up. The strong winds were behind us at this point, and we were somewhat sheltered on most of the climb. Over the top of the range it was cold, but it was over surprisingly quickly and with little pain.

A tight well-organised bunch, rolling smoothly

The descent into Swansea was great fun and we rolled into the small seaside town well ahead of schedule. With no sense of urgency, and warm and pleasant weather, the brief toilet stop took somewhat longer than originally intended!

Swansea Stop

We rode the whole route in a tight, well organised bunch, with smooth roll-throughs and good communication through the group. No one got left behind and I really felt like we were all working well together. I’ve never been on a ride before where the bunch stayed organised for 50km, let alone 200km!  Well done Tim (our organiser) for his success in motivating the fast boys to support the bunch!

Once we got to Triabunna, the traffic got a little heavier, and we had a couple of impatient drivers attempt to overtake on blind corners and similarly silly places. As we went through Orford, we had our one and only puncture of the day, and the local police stopped by to ask us to ride single file through the narrow gorge out of Orford to reduce the likelihood of more silly moves by drivers. The puncture meant that this became our lunch stop, and again, the beneficial winds meant we were still ahead of schedule.  So again, we were in no rush…

Lunch stop at Orford, 125km in, everyone looks happy!

Then we turned our noses towards Hobart. And hit the cross-headwinds. 50km/h with lots of gusts thrown in for good measure. Our average speed plummeted, and we were riding at less than 20km/h most of the time now. A few of the riders in the group were starting to feel their legs, and we encouraged them to spend as little time on the front as they possible.  As we rode single file through the gorge, a phalanx of probably 200-odd motorbikes roared past the other way, followed by a long line of classic cars.  There was to be no overtaking anyway…

Mechanical — rear wheel change meant a bit of waiting for the group.  Just before the rain!

We struggled against the wind, as the rain and a little bit of hail started to assault us. But we survived. The worst part, for me, was the steep descents in driving wind and rain. They were not much fun.  At this point, Simon experienced a serious cramp that put him in the bus. But only for 5 minutes! He got out again, like a madman, in time for the sketchy descent. Not sure I could have done that…

The rain let up again after the hills and we just worked our way slowly back into Hobart against the winds.  We finally and triumphantly arrived, the last group to finish for the day, all our early time gains destroyed by the ferocious winds and unexpected stops through to the finish.

Personally, I enjoyed the whole day, bar the sketchy descents and the handful of crazy drivers.  I’ve never been as well prepared for a long ride as I was for this one, despite already having done 280km in the 5 days preceding the ride.  Kudos must go out to Trev, Mark D and Mark P as the oldest in the bunch (by some margin, too), and to Tim who did a brilliant job of organising the ride.  The whole bunch was great to ride with, and I’d do the ride again with the same guys without hesitation.  Thanks for an awesome epic ride, Brendon, Dan, Simon, Ben, Tim, Michael, Ant, Trev, Mark, Mark and Mark!  Updated: And massive thank you to Henry and Henry for driving and supporting on the ride.  Sorry I forgot to mention you initially!

A more UTF-32 aware JavaScript String library

One of the hassles I regularly experience with JavaScript is that it does not have native support for supplementary characters.  Internally, JavaScript uses UCS-2 encoding (unlike most of the rest of the web, which uses UTF-8…)  While you can use surrogate pairs to represent Unicode characters between U+10000 and U+10FFFF, this makes string handling with these characters a pain.  In particular, functions such as indexOf and substr have to be very carefully used, both to account for the surrogate pairs in their index parameters, and to avoid cutting them in half when manipulating the string. Of course, when interfacing with third party services, you will need to be aware of how they handle text.  For instance, the Twitter 140 character limit counts Unicode code points, not UCS-2 code units.  But many other products, (for example, Microsoft SQL Server), use UTF-16 or UCS-2 internally and treat supplementary plane characters as 2 code units for the purposes of calculating field size.  Developer beware! Anyway, I have put together a small set of functions that treat surrogate pairs as a single code point, abstracting away surrogate pairs at the basic String level.  Adding support for surrogate pairs is not a complete solution — I haven’t done any work on regular expressions, for example, and this code also does not begin to address more complex requirements around grapheme clusters or normalisation, but this is just one less complexity to worry about. The functions do not replace any existing String functions. This code is not complete: I am missing some boundary conditions and edge cases, and I haven’t yet tested with isolated surrogate code units — but for what it’s worth, here it is.  The kmw prefix refers to KeymanWeb, which will shortly be using the functions (replacing the mishmash of code we currently use…) Some simple examples:

var str="Brave "+String.kmwFromCharCode(0x13027, 0x1314C, 0x1309C)+" world";

alert(str.indexOf("w"));    // Displays 13
alert(str.kmwIndexOf("w")); // Displays 10

alert(str.length);       // Displays 18
alert(str.kmwLength());  // Displays 15

alert(str.kmwSubstr(4,3));  // Displays e U+13027
alert(str.substr(4,3));  // Displays e U+D80C (half a supplementary pair!)

The license on this code is Mozilla Public License 1.1.  A couple of functions were lifted from the proposal Supplementary Characters for ECMAScript by Norbert Lindenberg and tweaked (back) to more closely mimic the functions they replace, warts and all.  These two functions are probably better tested than my ones! Version 1.0 of this library plus a rudimentary test script can be downloaded from http://durdin.net/blog-files/kmwString-0.1.zip. Comments, bug fixes, flames, suggestions much appreciated!

/**
  @preserve (C) 2012 Tavultesoft Pty Ltd
  
  Adds functions to treat supplementary plane characters in the same 
  way as basic multilingual plane characters in JavaScript.
  
  Version 0.1
  
  License
  
  The contents of this file are subject to the Mozilla Public License
  Version 1.1 (the "License"); you may not use this file except in
  compliance with the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL/

  Software distributed under the License is distributed on an "AS IS"
  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  License for the specific language governing rights and limitations
  under the License.

  The Original Code is (C) 2012 Tavultesoft Pty Ltd.

  The Initial Developer of the Original Code is Tavultesoft.
*/

/**
 * Constructs a string from one or more Unicode character codepoint values 
 * passed as integer parameters.
 * 
 * @param  {integer} cp0,...   1 or more Unicode codepoints, e.g. 0x0065, 0x10000
 * @return {String}            The new String object.
 */
String.kmwFromCharCode = function() {
  var chars = [], i;
  for (i = 0; i < arguments.length; i++) {
    var c = Number(arguments[i]);
	if (!isFinite(c) || c < 0 || c > 0x10FFFF || Math.floor(c) !== c) {
	  throw new RangeError("Invalid code point " + c);
	}
	if (c < 0x10000) {
	  chars.push(c);
	} else {
	  c -= 0x10000;
	  chars.push((c >> 10) + 0xD800);
	  chars.push((c % 0x400) + 0xDC00);
	}
  }
  return String.fromCharCode.apply(undefined, chars);
}

/**
 * Returns a number indicating the Unicode value of the character at the given 
 * code point index, with support for supplementary plane characters.
 * 
 * @param  {integer} codePointIndex  The code point index into the string (not 
                                     the code unit index) to return
 * @return {integer}                 The Unicode character value
 */
String.prototype.kmwCharCodeAt = function(codePointIndex) {
  var str = String(this);
  var codeUnitIndex = 0;
  
  if (codePointIndex < 0 || codePointIndex  >= str.length) {
    return NaN;
  }

  for(var i = 0; i < codePointIndex; i++) {
    codeUnitIndex = str.kmwNextChar(codeUnitIndex);
	if(codeUnitIndex == undefined) return NaN;
  }
  
  var first = str.charCodeAt(codeUnitIndex);
  if (first >= 0xD800 && first <= 0xDBFF && str.length > codeUnitIndex + 1) {
    var second = str.charCodeAt(codeUnitIndex + 1);
	if (second >= 0xDC00 && second <= 0xDFFF) {
	  return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000;
	}
  }
  return first;  
}

/**
 * Returns the code point index within the calling String object of the first occurrence
 * of the specified value, or -1 if not found.
 * 
 * @param  {string}  searchValue    The value to search for
 * @param  {integer} fromIndex      Optional code point index to start searching from
 * @return {integer}                The code point index of the specified search value
 */
String.prototype.kmwIndexOf = function(searchValue, fromIndex) {
  var str = String(this);
  var codeUnitIndex = str.indexOf(searchValue, fromIndex);
  
  if(codeUnitIndex < 0) {
    return codeUnitIndex;
  }
  
  var codePointIndex = 0;
  for(var i = 0; i < codeUnitIndex; i = str.kmwNextChar(i), codePointIndex++);
  return codePointIndex;
}

/**
 * Returns the code point index within the calling String object of the last occurrence 
 * of the specified value, or -1 if not found.
 * 
 * @param  {string}  searchValue    The value to search for
 * @param  {integer} fromIndex      Optional code point index to start searching from
 * @return {integer}                The code point index of the specified search value
 */
String.prototype.kmwLastIndexOf = function(searchValue, fromIndex)
{
  var str = String(this);
  var codeUnitIndex = str.lastIndexOf(searchValue, fromIndex);
  
  if(codeUnitIndex < 0) {
    return codeUnitIndex;
  }
  
  var codePointIndex = 0;
  for(var i = 0; i < codeUnitIndex; i = str.kmwNextChar(i), codePointIndex++);
  return codePointIndex;
}

/**
 * Returns the length of the string in code points, as opposed to code units.
 * 
 * @return {integer}                The length of the string in code points
 */
String.prototype.kmwLength = function() {
  var str = String(this);
  
  if(str.length == 0) {
    return 0;
  }
  
  for(var i = 0, codeUnitIndex = 0; codeUnitIndex != undefined; i++, 
    codeUnitIndex = str.kmwNextChar(codeUnitIndex));
  return i;
}

/**
 * Extracts a section of a string and returns a new string.
 * 
 * @param  {integer} beginSlice    The start code point index in the string to 
 *                                 extract from
 * @param  {integer} endSlice      Optional end code point index in the string
 *                                 to extract to
 * @return {string}                The substring as selected by beginSlice and
 *                                 endSlice
 */
String.prototype.kmwSlice = function(beginSlice, endSlice) {
  var str = String(this);
  var beginSliceCodeUnit = str.kmwCodePointToCodeUnit(beginSlice);
  var endSliceCodeUnit = str.kmwCodePointToCodeUnit(endSlice);
  return str.slice(beginSliceCodeUnit, endSliceCodeUnit);
}

/**
 * Returns the characters in a string beginning at the specified location through
 * the specified number of characters.
 * 
 * @param  {integer} start         The start code point index in the string to 
 *                                 extract from
 * @param  {integer} length        Optional length to extract
 * @return {string}                The substring as selected by start and length
 */
String.prototype.kmwSubstr = function(start, length)
{
  var str = String(this);
  if(start < 0)
  {
    start = str.kmwLength() + start;
	if(start < 0) {
	  start = 0;
	}
  }
  var startCodeUnit = str.kmwCodePointToCodeUnit(start);
  var endCodeUnit = startCodeUnit;
  
  if(length == undefined) {
    endCodeUnit = str.length;
  } else {
    for(var i = 0; i < length; i++, endCodeUnit = str.kmwNextChar(endCodeUnit));
  }

  return str.substring(startCodeUnit, endCodeUnit);
}

/**
 * Returns the characters in a string between two indexes into the string.
 * 
 * @param  {integer} indexA        The start code point index in the string to 
 *                                 extract from
 * @param  {integer} indexB        The end code point index in the string to 
 *                                 extract to
 * @return {string}                The substring as selected by indexA and indexB
 */
String.prototype.kmwSubstring = function(indexA, indexB)
{
  var str = String(this);
  
  if(indexA > indexB) { var c = indexA; indexA = indexB; indexB = c; }
  
  var indexACodeUnit = str.kmwCodePointToCodeUnit(indexA);
  var indexBCodeUnit = str.kmwCodePointToCodeUnit(indexB);
  if(isNaN(indexBCodeUnit)) indexBCodeUnit = str.length;

  return str.substring(indexACodeUnit, indexBCodeUnit);
}

/*
  Helper functions
*/

/**
 * Returns the code unit index for the next code point in the string, accounting for
 * supplementary pairs 
 *
 * @param  {integer} codeUnitIndex   The code unit position to increment
 * @return {integer}                 The index of the next code point in the string,
 *                                   in code units
*/
String.prototype.kmwNextChar = function(codeUnitIndex) {
  var str = String(this);
  
  if(codeUnitIndex < 0 || codeUnitIndex >= str.length - 1) {
    return undefined;
  }
  
  var first = str.charCodeAt(codeUnitIndex);
  if (first >= 0xD800 && first <= 0xDBFF && str.length > codeUnitIndex + 1) {
    var second = str.charCodeAt(codeUnitIndex + 1);
	if (second >= 0xDC00 && second <= 0xDFFF) {
	  if(codeUnitIndex == str.length - 2) {
	    return undefined;
	  }
	  return codeUnitIndex + 2;
	}
  }
  return codeUnitIndex + 1;
}

/**
 * Returns the code unit index for the previous code point in the string, accounting
 * for supplementary pairs 
 *
 * @param  {integer} codeUnitIndex   The code unit position to decrement
 * @return {integer}                 The index of the previous code point in the
 *                                   string, in code units
*/
String.prototype.kmwPrevChar = function(codeUnitIndex) {
  var str = String(this);

  if(codeUnitIndex <= 0 || codeUnitIndex > str.length) {
    return undefined;
  }
  
  var second = str.charCodeAt(codeUnitIndex - 1);
  if (second >= 0xDC00 && first <= 0xDFFF && codeUnitIndex > 1) {
    var first = str.charCodeAt(codeUnitIndex - 2);
	if (first >= 0xD800 && second <= 0xDBFF) {
	  return codeUnitIndex - 2;
	}
  }
  return codeUnitIndex - 1;
}

/**
 * Returns the corresponding code unit index to the code point index passed
 *
 * @param  {integer} codePointIndex  A code point index in the string
 * @return {integer}                 The corresponding code unit index
*/
String.prototype.kmwCodePointToCodeUnit = function(codePointIndex) {
  var str = String(this);
  
  var codeUnitIndex = 0;

  if(codePointIndex < 0) {
    codeUnitIndex = str.length;
    for(var i = 0; i > codePointIndex; i--, 
	  codeUnitIndex = str.kmwPrevChar(codeUnitIndex));	
    return codeUnitIndex;
  }

  for(var i = 0; i < codePointIndex; i++,
    codeUnitIndex = str.kmwNextChar(codeUnitIndex));
  return codeUnitIndex;
}

Updated 11 May 2012: Removed script formatting code as site hosting it was unreliable. Back to good old <pre> for now.
Updated 29 May 2014: Fixed broken script text, damaged when inserted previously.

Cruelly Tricked by the Library

So I borrowed a high brow British crime novel from the library. Ruth Rendell, An Unkindness of Ravens. Okay, maybe not very high brow. Here’s a picture.

It is sad that library technology has forced barcodes to be put on top of book titles or authors. 
I’m not very good at reading barcodes

Then I opened the book.

Note how classy that title is: the curlicue on the V is chopped off…

Um, what was that again? The Vampire’s Betrayal: Raven Hart? Trashy vampire romance? Twitch. Yeuch. The writing makes Twilight look sophisticated. I checked the cover again to make sure I hadn’t somehow, insanely, picked up the wrong book. But to no avail.

On page 27, the sloppy supernatural drivel miraculously transforms back into the erudite and innately British crime novel. But I am forever scarred.

I couldn’t bear to read page 27.  Who knows what I have missed so far?

I shall never again be able to open a Ruth Rendell novel without fear and trepidation.

Exploring with Hannah

We went exploring on Mount Nelson. We went to a little fence.

We saw some cairns.


We saw a strange metal loop hanging on a tree.

We saw rosellas.

YouTube Video

We saw a jack ant.

We walked up a steep track.

We saw some poisonous berries.

We saw the fence again!

We saw the signal station.


We saw an old hook and a great view. We might have a sorbet if they have some here!
The End

Wallaby Frogger, or how to interpret Strava performance graphs

This morning I rode to work early, leaving home before 5:30am.  This meant it was still dark and the wallabies were still out clubbing.  Hundreds of them.  It was just like riding past a cinema when a movie finishes, with the happy cinema-goers (or in this case wallabies) crossing the road oblivious to traffic, easily stunned by bright lights.

I would have seen at least 100 wallabies in the first half hour of the commute.  Most of the wallabies were on the side of the road and I just watched them warily as I rode (slowly) past.  But 5 wallabies decided to cross right in front of me.  These wallaby interactions can be seen on the graph below.

Note the spike in heart rate coupled with the sudden drop in speed.  One way of getting a cardio workout I guess.  It really felt like a game of Frogger, albeit with a role reversal.

Discovering Old Farm Road

Old Farm Road

Yesterday I discovered a beautiful road climb within a few minutes of the city centre.  I’m amazed I’ve never ridden it before.  Tucked away behind Cascade Brewery is a little road called Old Farm Road that follows Guy Fawkes Rivulet (does anyone know how it got its name?) straight towards the base of Mount Wellington.

The entrance to the climb is unprepossessing, passing the industrial complex of Cascade Brewery.  But tucked away to the left of the complex, Old Farm Rd beckons, first of all gentle rising as it passes a grassy park, ducking through some trees with Mt Wellington glowering above, and then tucking around and over the little rivulet before starting to climb in earnest.

This little road is just one lane wide, and you could be way out in the bush — there’s no sign of the city.  After the bridge over the rivulet, you ride past a few houses before a couple of fantastic hairpin bends that lead into a steep ramp.  Watch it on the descent — you’ll smoke your brakes coming into those corners!

Old Farm Road is also an access road for mountain bikers heading towards some of Hobart’s best tracks.  But this blog post is for skinny tire bikes!

It’s a steep little climb, 1.8km at 9.0%, making it a Category 3 climb in Strava’s estimation.  It has steep ramps, hairpins, a bridge, and is just one lane wide.  I love it!

Old Farm Road
Distance 1.8km
Category 3
Elevation 162m
Gradient 9.0%
Maximum Gradient 22%
Time from city 5 minutes
Traffic low
Strava http://app.strava.com/segments/1212891

How to get to the climb: Take Macquarie St, and turn right after Cascade Brewery.

There’s Old Farm Road, off to the left.  Ignore the trucks…

Mt Wellington in the distance as the road narrows to a single lane

A gentle gradient here, but enjoying the scenery too much to smash the climb!

Crossing Guy Fawkes Rivulet!

Now we start climbing.  If it wasn’t for the gum trees, we could be in Europe somewhere.

Steep hairpin bends

22% here we come!

Is that the Old Farm ahead?

Mt Wellington towers over the climb

Just for fun!

This post has been written in the style of my Top Ten Climbs Around Hobart series. Here are some other cycling climbs around Hobart:

Heroes of Yore

I suppose, if asked to name a hero from the Bible, that many people would choose David. After all, he killed giants, wrote inspiring poetry, and became a glorious king. He represents the pinnacle of Old Testament Israel. But is that really what makes him a hero?

Over the last few months, I have been listening to Charles Swindoll’s book David, A Man of Passion and Destiny, on my bike commutes, and it has been an illuminating experience. David is, like just about every significant person recorded in the Bible, all too human. His passion is his strength and his downfall. The narrative in no way glosses over these failures, which is a big part of what makes the history so compelling. However, as a historical account, it is easy to miss the emotion and the import of key events.

Buried in the middle of a paragraph are a handful of words which I think represent David’s most heroic moment. These few words are uttered by David during the height of his reign as king, after the following story is related to him:

There were two men in a certain city, the one rich and the other poor. The rich man had very many flocks and herds, but the poor man had nothing but one little ewe lamb, which he had bought. And he brought it up, and it grew up with him and with his children. It used to eat of his morsel and drink from his cup and lie in his arms, and it was like a daughter to him.

Now there came a traveler to the rich man, and he was unwilling to take one of his own flock or herd to prepare for the guest who had come to him, but he took the poor man’s lamb and prepared it for the man who had come to him.

Do you know who told this story and why? It was told to David by Nathan, a prophet, after David slept with a married woman and subsequently had her husband killed (read the full story).

Now David was at the height of his power. His wrongdoing has just been uncovered by Nathan. It would have been well within his ability to have had Nathan killed. But he didn’t kill Nathan. Instead he stopped and said: “I have sinned.” Just step back for a moment and think about that. When is the last time you heard one of our political leaders take responsibility for their actions like that? How hard is it to admit that you are wrong; and even harder when you have every facility within your grasp to avoid it?  David didn’t even have an opposition party to denounce him: he was king and could do whatever he wanted.

That’s heroism.