The Calgary Marathon – Not what I had hoped for

My dreams of a sub-3:00 run were dashed. I made the split at 1:29:44 but finished at 3:26:07. Somewhere after the 2hr mark I started getting spasms in my calf muscles. This came immediately after the long downhill, and was undoubtedly related to it somehow. The odd thing was that it was perfectly balanced… I'd have spasms in one leg, and a few km later, I'd have spasms in the other. Every single time that I tried to increase my pace, my muscles felt like they were trying to tear themselves apart, first I'd have twitching at the top and it would slowly dance around from one muscle fibre to another. Kind of like my muscles were having a little bout of arrhythmia… maybe I need a pacemaker for my legs!

Needless to say, my calf muscles were useless after that. I found that I could still run flat-foot, with my quads doing all of the work. Yeah. Now that the race is done, my quads feel like they ran 50km and my calf muscles, well, they feel peachy keen, ready to conquer the world. The bastards. They let me down!

So, what to do? I need strength training for my quads and calves, plus maybe more trail running to build stability. They say that cramps are related to sodium deficiency, but I had half a bag of potato chips last night in addition to a good dinner, and I drank Gatorade at every station in the race. Another thing I need to do is book a marathon for the fall… Kelowna is Oct 9th, the Mad Moose in Prince George is on Sept 25 (it's a trail run), Regina is Sept 11, and Edmonton is Aug 21.

Finally, a plea to the Calgary Marathon organizers: please rent a few more potties next year! The lineups were crazy and I couldn't empty my bladder before the race, I even had to toss half a bottle of water because I knew it would put me over my limit.

Keeping up the pace

The Calgary Marathon is only two weeks away! Today was my last long training run, I decided to play it by ear because my knee has been bothering me for a few days. It feels like it is bruised or something, though it has no discoloration. If it got bad, I was going to cut my run in half. Much to my surprise, it didn't bother me at all unless I overextended, e.g. by landing straight-legged while going downhill. In fact I bet that is how I injured it in the first place.

So, not only did I complete the 39 km that I had set out to do, but thanks to my slow, cautious running at the beginning I never ran out of steam. I kept up a pace of around 4:40 per km the whole way and finished in three hours. If I had run a marathon distance, I would have finished in 3:15. That is just… wow! Calgary Marathon, here I come! Hopefully my knee will still agree with me about this in the morning.

Wrapping C++ operators in Python

Ah, the VTK Python wrappers… my favorite little programming project when there are other things that I should be doing instead. My goal for the Python wrappers is to make it possible to do anything with VTK that can be done in C++, also doable in Python. And bit-by-bit, I think that I'm getting there.

My most recent mini-project was to wrap the C++ "[ ]" operator in python. Not too difficult, because last year I had already enhanced vtkParse so that it pulls all operator methods from the C++ header files. One thing that I don't like about how I did it, is that the range checking (i.e. to make sure that a bad index does not crash the program) uses a hard-coded range. There is no mechanism in VTK to add a hint to the header files so that the wrappers know what the valid index range for the "[ ]" operator is. Eventually, there will be… but that is a battle for another day.

There is another wrapping project that I've been working on, one that is slightly more ambitions: wrapping templated VTK classes. Templates are also something that I added to vtkParse last year, but hadn't added to the wrappers. Without going into the details of the implementation, the idea is that in Python, a template class like vtkVector<float,3> will be specialized and instantiated like so:

v = vtkVector['f',3]()

where 'f' is the Python type character for 'float'. So more or less, the syntax is very similar to C++ except that square brackets are used instead of angle brackets. Of course the trick is that, unlike C++, new template specializations cannot be created on-the-fly… only the template specializations that are wrapped are available. So what I'm working on is a method to figure out what template specializations will be needed so that they can be wrapped ahead of time. This can be done by looking at how templated types are used within VTK, i.e. looking at where the templated types appear either as method arguments or as superclass types of other classes.

Definite progress

I finished an 11 km training run in 44 minutes today. This is the first time that I've kept up a 4:00 pace over 10 km during training, it is as good a time as during any of my 10k races. Even more impressive, this comes just two days after I completed a monster 29 km training run at a 4:50 pace. My marathon goals are looking more realistic all the time.

The next step: Marathon!

How does one keep running, week after week? By always having a goal in mind, of course! Well, this might not be necessary for me anymore… now that the winter is finally over, running has become quite enjoyable and I hardly need an excuse. But knowing that I have a race coming up helps me push just a bit harder.

My current goal is the Calgary Marathon on May 29th. I actually registered for it in January but did not tell anyone until this week, because I didn't want people to think that I was crazy. Am I? Well, even last year I was doing some 30+ km training runs to test myself on marathon-like distances, so I'm not exactly going into this cold.

I was worried that I would become injured during my training or my half-marathon, but fortunately I'm still running pain-free. Much better than last year when I had several minor injuries. Let's go through the list: 1) Shin splints soon after I first began running, these can be very dangerous but new shoes and reduced training fixed them. 2) A pulled calf muscle, which righted itself after 6 weeks of reduced training. 3) Iliotibial band syndrome (ITBS), which is more of an inflammation than an injury i.e. not very dangerous but incredibly painful.

So what about injuries this year? Nada. So I must be doing something right. It's time to step up to the next challenge!

I’m a running fool

The April Fool's Run, the "little half that rules". A half-marathon event and nothing but. And my best run to date, at 86 minutes 3 seconds for 21.1 km.

Even though I had an excellent time (better than my expectations), my run was far from flawless. I ran the first half in 40 minutes and the second half in 46 minutes… not the even splits that I was aiming for! But I felt so good running that first half that I really did not want to slow down, too bad that my body eventually made that decision for me! Honestly, though, I think it would have been possible for me to keep that pace for the whole race. So why did I slow down? The main reason was lack of hydration. I only hit one water station (at 12 km) and it was too little, too late. Those flimsy paper cups are so difficult to drink from while on the run. When training I never carry fluid unless I'm going for more than 20 km because I can easily run that far without water and suffer no ill effects. But now I know that if I want to keep up a 4 min per km pace, it is a different story! Sure, I can run 20 km without a drink, but I cannot do so without slowing down… a lot!

The interesting thing is, even though I was very low on energy, I had no trouble accelerating to a sprint as soon as I saw the finish line. So I'm guessing that in addition to the dehydration, there was also a psychological barrier that was keeping me from running at my best… a barrier that didn't come down until the goal was within my physical sight. This is another thing for me to work on during my next race: even if the race is only half-over, the finish line should be mentally in sight and you should be doing everything within your power to keep up your goal pace.

Springtime in Calgary

This is the winter that won't end, it is the end of March but the snow keeps falling. The humidity is increasing, however, and this leaves a nice layer of hoarfrost on the trees. I dearly hope for warmer weather, but the snow has not stopped me from training. I still hit the snow-covered pavement four times a week.

Exactly one week from today I am driving to BC's Sunshine Coast for the annual Fool's Run. My goal is to run the 21.1 km distance in under 90 minutes. Wish me luck!

B-Splines for VTK

This is the conclusion of yet another of my long-delayed projects. I started writing some classes for doing image resampling with B-splines in VTK way back in 2007. After working on them for a while, I realized that even after I finished them, I would have to do a lot of testing before I could safely use them. Recently I was reading up on B-splines again, and happened to come across some code from the authors of one of the important B-spline papers. That gave me enough incentive to go back to my old code and finish it off. See a short paper that I wrote here.

Optimized floor() for cross-platform software

My work in computer graphics software involves a lot of float-to-int conversions, the majority of which require a floor() operation rather than a simple truncation. The problem is that floor() is a math library function, and quite hefty one at that. To conform to the IEEE-754 standard, it must correctly deal with overflow and with NANs, in addition to the fact that the function call itself introduces some overhead.

Since the days of the Pentium III, I had been using a fast inlined floor() approximation based on the bit-twiddle trick suggested at stereopsis.com, but that fix was specific to the FPU on the Pentium III and earlier x86 CPUs, and was completely irrelevant to other CPU brands or to newer x86 CPUs with SSE2 instructions. Also, the code not only ugly but was, as I noted in the first sentence of this paragraph, an approximation. Float values that were only slightly less than an integer (on the order of 1e-6 less) would sometimes be rounded up instead of down.

So, a few months ago, I decided to get rid of the obsolete bit-twiddle trick in favor of something more general. I came up with the following, which gives exact results as long as it doesn't encounter overflow or NAN, and which does not require any branching or other expensive CPU operations:

inline int FastFloor(double x)
{
  int i = (int)x;
  return i - ( i > x );
}

The trick here is very simple. If truncation to an int caused the value to increase, then we have to subtract one. We rely on the fact that conditionals such as ( i > x ) evaluate to one or zero.

As I mentioned earlier, this trick does not give correct results on overflow or underflow: it will counter-intuitively return 2147483647 on underflow and -2147483648 on overflow, though this is hardly worse than static_cast<int>(), which returns -2147483648 on both underflow and overflow in most implementations. A check for over/underflow could be included without adding any conditional branches, but that is left as an exercise for the reader.

Don't worry, I didn't forget about the ceil() operation:

inline int FastCeil(double x)
{
  int i = (int)x;
  return i + ( i < x );
}

Like FastFloor(), this code requires as a preconditon that the floating-point value is not NAN and lies within the range [-2147483648,2147483647]. If either of these is likely to occur, then an isnan() check and/or a range check on the float value can be performed. The same is also true for the math-library floor().