My Rambling Thoughts

Quote:

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

Brian W. Kernighan

Quote:

If you ride a motorcycle often, you will be killed riding it. That much is as sure as night follows day. Your responsibility is to be vigilant and careful as to continue to push that eventuality so far forward that you die of old age first.

Unknown source

News:

Date: . Source: .

Soon, strangers in their homeland

News: Immigration – A resentful citizenry

Date: 26 September 2009. Source: Littlespeck.com.

Singaporeans are not annoyed by their arrival, but the overwhelming numbers and sometimes having to play second fiddle to them.

AN ELECTRONICS firm that advertised last week for a "preferably non-Singaporean" engineer has added fuel to a worsening controversy in this migrant city.

It particularly stipulated that "permanent residents are welcome" to apply for this "mid-career job (salary negotiable)".

Population Trends 2009 report just released by the Department of Statistics: 3.2 million Singaporeans, 0.53 million PRs, 1.25 million non-residents.

A tale of upgrading Windows past and present

I posted this in a forum:

Whether you have good experience or not with upgrading Windows depends a lot whether you have the HW drivers, I think.

When I upgraded from Windows 3.11 to Windows 95, I didn't have drivers for my graphics and HD. So, no accelerated graphics. I needed a driver for my HD because my BIOS did not support partitions over 504MB natively. (I can't remember the details.)

So it wasn't a pleasant experience. However, Windows 95 looked way cooler than Windows 3.11 and was clearly the way to go. We are still using the same interface today, 14 years later.

I upgraded to Windows 98 without a problem. By then, I had gotten new hardware that had Windows 95 drivers. (Windows 95/98 are in the same family.)

A few years later, I watched a colleague struggle with Windows 2000 on his P-II (233MHz?) with 64MB RAM. It swapped to the HD constantly. I had a better experience — on a P4 2.4GHz 512MB. I remembered it as a slimmed-down XP. (W2k was considered bloated when it came out. Now it's slim. Same for XP.)

IMO, Windows 2000 came out at the wrong time. It was too resource intensive and required new drivers. Anything that requires new drivers will usually spoil your day.

When Windows XP came out, most HW already had Windows 2000 drivers — even though W2k itself wasn't popular for home use — so it was much less problematic . For example, I got a notebook a few months before XP came out. It was running Windows ME, which I found okay. I was able to upgrade to XP by using the W2k drivers. (I'm still using the notebook today. :-O)

Then we were in an unusual period of stability for 5 years. (Actually less, because XP SP2 was a substantial change too.)

Vista, everyone knew the story. It's not just that it isn't ready. It has a new architecture, which means new drivers. Guess what happens when you try to upgrade an existing PC?

Two years down the road with W7, everything is fine again. I'm able to upgrade my notebook from Vista to W7 using the Vista drivers.

Rule of thumb

  • Don't upgrade old PC across OS architectures (XP->Vista, not okay; Vista->W7, okay)
  • Don't expect discontinued HW to have new drivers
  • For PC, just upgrade the HW. For notebook, buy a new one or forget about upgrading!

Just a note. It used to be very simple to upgrade a PC part-by-part. Now, the technology obsoletes so fast that you can never get parts for your old PC.

HD-remastered DVD to BluRay?

Is it worth upgrading from a HD-remastered DVD to BluRay for an anime show?

A HD-remastered DVD, as its name implies, is remastered from film (or the best source) at HD resolution, cleaned up and only downsized to DVD's resolution at the last step. It also generally maxed out the DVD's video bitrate (~9.3Mbps).

In the past, I was skeptical when HD-remastered DVDs came out. A DVD is a DVD: it is still constrained by MPEG-2 at 9.3Mbps. Then I watched a few such DVDs and was impressed — the DVD format can be pretty impressive, just that it is not generally used to its full capabilities.

Personally, I'm skeptical that BluRay has any advantage over a well-done HD-remastered DVD for a simple reason: the animators stopped drawing at a certain size. Even on DVD, it is already obvious some fine details are not drawn.

How about the bitrate? DVD is limited to MPEG-2 at 9.3Mbps (actually 9.8Mbps, but good audio needs a little more space). BluRay allows H.264 at a whopping 40Mbps.

It is well-known that MPEG-2 does poorly at low bitrates. And when it runs out of bits, it shows blocks all over the place. This is easily seen in a paused fast-moving scene. However, 9.3Mbps is not low bitrate. It is hard to believe blocking can be seen in normal viewing — for a clean source. (I also suspect there are many poor MPEG-2 encoders out there, so never say never.)

Also, it cannot be claimed outright that 40Mbps is better than 9.3Mbps. DVD is a mere 720x480. BluRay is 1920x1080 — it has 6x data to encode (1.98MB vs 337.5kB). That means it really only has 6.67Mbps at DVD resolution! Luckily, the H.264 is a very efficient codec. Also, when it run out of bits, it blurs the image. It is much less obvious than blocking.

Having said this, I've seen poorly done DVDs compared to their BluRay counterparts for the same show: EE (edge enhancement) and window-boxing. It is like they expect you to watch the DVD on a CRT TV.

The last word

Now, I'll say that the BluRay release will look more crisp. The DVD, having to resize by over 2x, will look soft. However, it will have all the details.

Right-to-left in CSS: not as trivial as it should be

programming

Recently, I had to add right-to-left language support for a website. I added the dir='rtl' attribute to <html> and... (almost) nothing changed.

I was surprised to find that floats, borders, margins, paddings and text-alignments are not reversed, which means we got to override it ourselves:

.left { float: left; }
body.rtl .left { float: right; }

.pad-left { padding-left: 1em; }
body.rtl .pad-left { padding-left: 0; padding-right: 1em; }

This is tedious, error-prone (easy to miss out) and a maintenance nightmare (got to update two styles).

(We generally want all horizontal styles to reverse, save for a few that we use another style, say .fixed-left.)

Minimizing the overrides

After applying the changes by hand, it struck me a few guidelines go a long way in minimizing the number of overrides:

  • Don't set the default text-alignment. This is obvious, but people still do it "just to be sure". It's just more work.
  • Avoid asymmetric horizontal borders, margins and paddings.
  • Apply float to elements separately.

I call out float specially because we need to float our DIVs a lot. An example:

.cls { float: left; ... }

<div class='cls'>...</div>

Change it to:

.cls { ... }

<div class='cls left'>...</div>

It will simplify right-to-left support, but it may complicate other handling — we are used to using just one class per element.

Sidenote: if IE 6/7 support inline-block properly, we could have reduced float usage by 90%.

Ideal CSS right-to-left support

First, it should automatically flip the horizontal styles. It should also support fixed variants:

.cls { text-align: left; }

.cls2 { text-align: left !fixed; }

Or, it should allow an easy way to flip:

.cls { text-align: left !rtl; }

Lastly, it should allow flipping without respecifying the values:

.rtl .cls { margin: flip-rtl; }

Another approach

We can actually go through the stylesheets and reverse all the styles, except those prefixed with body.rtl.

This can be done on the server or by using Javascript. The Javascript approach will increase the initial page load time and the page will look wrong initially — that is why I'm not keen on it, despite it being a very clean solution.

Logging instead of debugging

programming

A colleague of mine was looking for a probe so that he could do some debugging. He was not amused when I suggested he do without one.

While a debugger is useful, people come to rely on it so much that they forgot other debugging techniques.

The plain old printf()

The simplest is to put some log messages. It may be something as simple as printf().

I prefer to use log messages for a few reasons:

  • It is always present. We don't have to setup the debugging environment every time.
  • We can trace the execution path. We can even do simple profiling if we like.
  • We can log messages in the field. This is very useful for timing issues which are very hard to duplicate.

The next step is then to design the log messages properly so that we get all the necessary information with the least messages. (There may be timing or space constraints.)

During coding

A debugger is useful during code development, when your code is full of logic errors. It can show you the expected data types and values quickly. It can increase productivity if you have a short compile-and-run cycle — such as in an interpreter environment.

However, I find it inferior to plain old logging when trying to track down a defect, especially one that is not always reproducible.

CB400F new battery/rectifier

transport

My CB400F battery has failed me — for the last time. It was on its deathbed, and now it is dead for sure. The rest voltage was 12V. Thinking it was a simple case of disuse (I didn't ride the bike in a fortnight), I charged it and rode the bike to work. However, the battery was flat again in the evening. (Which meant I had to catch a bus home.)

When I got the bike two years ago, the battery was already only showing 12.2V. There is only so much life you can wring out of an old battery.

A new battery

So, I got a new battery. It is simple to DIY. Just got to be careful with the sulphuric acid. :-D

Activating CB400F battery

It measured 12.7V out-of-box. After charging, it measured 12.9V. This was done on a biker's advice. He said this would maximize the battery's life.

Rectifer blues

The story is not finished yet. After plugging in the new battery, I did a sanity check to see if the bike is charging the battery correctly. To my surprise, it showed 17.5V! The rectifier was clearly spoilt.

So, another trip to the parts shop to get the rectifier.

CB400F rectifier

The old and the new rectifier

The old rectifier is SH633-12, the correct one is SH693-12. They are for various Honda bikes. I don't know what's the difference.

The new rectifier clips the charging voltage at 14.1V. Perfect!

(Interestingly, it charges at 14.3V at 2.5k RPM and 14.1V at 4k RPM. Higher RPM does not mean higher charging voltage.)

Postmortem

My guess is that the rectifier died around May. The battery remained usable for a while until it died in June. It was not able to hold its charge since then — because it was not being charged. The rectifier had to be working before that because the battery remained at 12.2V for two years.

Afterwords

The low charging voltage got me thinking. The charging voltage on my YBR is 15.5V. Perhaps the rectifier is spoilt as well — even though I've changed it once before. Maybe that's why the batteries are always spoilt.

This time, I'm going to DIY for sure.

Update (29/9): I remembered wrongly. It is my CB400F rectifier that used to clip at 15.5V. The new YBR rectifier clips at 14.2V.

The haze

The haze is back!

The haze

Nice sunset, but terrible air quality

It so happened that this is also a time of much burning:

Burning away

Some people feel we should have the common sense not to make the air worse. I think that it is not so easy to stop a traditional practice.

Check before entering expressway!

transport

I used to think the EMAS displays at the expressway entrance are a waste of money, but now I find them quite useful — when they are working.

For example, I was on Adam road, at the PIE entrance towards the east, when I saw the estimated time to Eunos was 45 minutes — the usual time is just 13 minutes!

The EMAS display will also show heavy traffic or massive jam, but I find it more useful to have a quantitative measure.

In addition to the time, the EMAS display should show these status:

  • optimal (minimum time at speed limit)
  • good (<1.25x optimal)
  • acceptable (<1.5x optimal)
  • slow (<2x optimal)
  • avoid (>2x optimal)

This will help drivers who are not familiar with the route.

Will the AA Traffic GEM be useful?

The AA came out with a new product, the AA Traffic GEM, that can show the traffic condition.

I tried the device at Comex. It is so slow that it is unusable. I'm not talking about updating info from the server. I'm talking about scrolling. Move your finger to scroll the map (like almost every input device today, it is a touchscreen), and the map starts to move five seconds later.

The selling point of this device over a generic GPS is that it can show real-time traffic conditions. But the design is flawed. First, it does not show you the severity of the jam. Second, you need to zoom in very deeply to see which direction the jam is in.

And a member in a car forum puts it best: all roads are jammed during the morning and evening peak hours. There's no need to check. :lol:

The Irresponsible Captain Tylor

transport
Captain Tylor R1 remastered DVD box

I recently bought the R1 remastered DVD version of The Irresponsible Captain Tylor.

How many times have I bought it? Let's see:

  • The original R1 version (+OVA +OST) [2002]
  • R2 remastered version (+OVA) [2003]
  • R1 "ultra" version (+OVA) [2005]
  • R1 remastered version [2009]

You can probably tell I like the show very much. :lol:

I'm not going to buy the OVA this time, because it is not good at all. I may buy the BluRay version in the future, but I'm not convinced the show will look better in BluRay — TV anime just don't have fine details.

What is Captain Tylor?

Captain Tylor is a very niche show. It's a shame because it has a very good plot and drives one single message across 26 episodes — and does it well. It has a very retro and low-budget look (but it is still nicely animated), so many people don't like it.

Also, the first two episodes are quite boring, so most people made up their mind by then and stopped watching. I always advise people to watch the third and fourth episodes first (they form a 2-episode story) to get a real feel of the show.

Video quality

The original R1 release is from a composite master. It has very strong EE (edge enhancement) and is very clean looking (meaning it is filtered). It would have looked pretty good if not for the EE.

I believe the R2 release is about as good as it gets. It is remastered from film and is grainy as hell. It looks perfect after a little filtering, though. It, like all other R2 releases, is unbelievably pricey.

The "ultra" release has the exact same video quality as the original R1 release, except it has bonus clips and OST. IIRC, it was supposed to have the remastered video, but things fell apart. It was really a waste of money to get this edition, but I couldn't resist.

The R1 remastered release is based on the R2 release. It looks exactly the same, with grain and splice marks. However, it is encoded for NTSC (with the 3:2 pulldown) instead of progressive (24 fps)! I wonder why.

Captain Tylor and TRSI

Captain Tylor is released by TRSI in the US. TRSI is an online anime store. It seems strange until you found out the CEO is a fan of the show — that's why he picked up the show. I don't think the original license was expensive (being a niche show), but TRSI needed to secure 750 pre-orders before they licensed the show. The 750 people were acknowledged in a credits section in the DVD.

Data analysis with Cross-Domain AJAX

programming

The only way to do cross-domain AJAX (XDR) today is to use the script tag. The result must be in a Javascript form. No text or HTML. And you must trust the server — the script has access to your DOM.

(Stylesheets and IFRAMEs can be cross-domain too, but you can't read their contents from Javascript.)

Right now, there is a XDR proposal. It requires the server to opt-in. I think it is silly from a programming POV. IMO, GET requests should be allowed.

Today, we can actually do XDR using IE. Add localhost to the Trusted Zone and enable cross-site scripting for this zone. FireFox 3 has no such option — leading me to think a future virius/worm attack could be to "patch" the browsers to allow XDR which will then enable malicious scripts.

What to do with XDR?

We can use XDR to massage data the way we want it. Don't like how eBay or Amazon displays or searches? Do a XDR and extract the data!

Let's try it on a forum's member list:

Joined 2003 2004 2005 2006 2007 2008 2009
Active 1,549 899 548 603 1,025 3,418 1,393
2003 -212
2004 -187 -362
2005 -87 -115 -228
2006 -61 -33 -58 -240
2007 -73 -51 -35 -70 -391
2008 -165 -68 -51 -63 -197 -2,207
2009 681 249 159 196 398 1,071 1,352

Most forums show only the total number of members. I'll rather know the number of active members. Here, we can see the breakdown of members who joined and dropped out over the years. Although there are 9,435 members, only half are still active today.

Also, notice the spike in 2008? They are likely to be bots as the number of inactive members is also very high.

Age/ #Posts <6M <1Y <2Y <3Y >3Y ?
<1 3,717 379 399 78 126 292
<5 934 202 214 86 206 13
<10 195 88 110 43 111 5
<50 168 137 245 86 368 17
<500 37 57 146 89 531 26
<2,000 0 5 19 11 202 15
else 0 1 2 5 63 7

(Note: the age here is the difference between the last visit and joined date. It is not today minus joined date.)

So many inactive members! Zero-post members are likely to be bots (but not always; we need to check whether they are active or not). Even after filtering it, we see many "lurking" members. We can also see that only a small fraction of members post a lot.

(Quantity doesn't mean anything. I'm member 100+ and I made just 200+ posts over the years.)

To sum up

This is just an example what we can do with XDR: the ability to process the original HTML in new ways.

Last word on XDR

It is actually possible to do XDR today with a server-side XDR proxy. If I need to do XDR more often, I may use this solution. It is the most transparent solution now and always.

A new webpage analyzer on the block: Page Speed

programming

Google has its own webpage analyzer: Page Speed (released in June; still in beta). It does essentially the same thing as YSlow, but is more comprehensive. The best is to use both as they interpret the Best Practices differently.

Category Score
Leverage proxy caching 100%
Minify JavaScript 99.4%
Combine external CSS 89%
Remove unused CSS 79.7%
Combine external JavaScript 78%
Specify image dimensions 70%
Use efficient CSS selectors 69%
Leverage browser caching 50%
Optimize images 7%

Leverage proxy caching: set "Cache-Control: public" so that the files (usually images) can be shared by all users once they are cached by a proxy server. This makes sense, actually. Will consider this.

Minify JavaScript: Page Speed goes one step further and analyzes inline scripts as well. My main page has a little page-specific code that can be minified. Not going to care.

Combine external CSS: under consideration.

Remove unused CSS: 33% unused CSS! Well, that's because I use the same CSS file for almost the whole site.

Combine external JavaScript: under consideration.

Specify image dimensions: not going to do it.

Use efficient CSS selectors: Page Speed is very harsh towards overqualified and descendant selectors. I think it is wrong: we should use such selectors.

Example of a overqualified selector: div.class. Should we use just .class? That means we need to search the whole DOM. Is that more efficient? (I prefer not to overqualify too, but that's due to specificity — qualified selectors are difficult to override.)

Example of a descendant selector: ul.class li img. (Two levels is considered inefficent. One level is considered good-to-fix.) Page Speed suggests adding a class to the IMG element directly. That makes the HTML very verbose and we are not making full use of CSS.

(I suspect Google's Chome optimizes for a single-level class selector.)

Leverage browser caching: Page Speed requires the static files to expire one month away to avoid this warning. Google expects you to change the URI when the file changes. Personally, I think this is abusing the caching system.

Optimize images: 52.2kB images can be shrunk by 62.7% to 19.5kB! This is because I requested larger thumbnails than I displayed them. Maybe I should request for exact-sized thumbnails.

YSlow: from F to... not so F

programming

YSlow is Yahoo's webpage efficiency analyzer. It is their Best Practices for Speeding Up Your Web Site in a code form. Out of curiosity, I ran it on my main webpage:

Category Before After
Use a Content Delivery Network (CDN) F -
Make fewer HTTP requests B -
Add Expires headers F F
Compress components with gzip F A
Put JavaScript at bottom C -
Minify JavaScript and CSS F A
Configure entity tags (ETags) F A

Use a Content Delivery Network (CDN): ignored.

Make fewer HTTP requests: will be done in the future. I have several JS and CSS that should be combined. However, it will be difficult to achieve just one file per type.

Add Expires headers: I hesitate to add it because the browser will not send the request at all — I'm afraid of stale files. However, I found that a reload (F5/Ctrl-R) will force the browser to conditional-get, so it is still not too bad.

I set images to expire in 3 days and JS/CSS to 3 hours, but I still get the same grade. (YSlow needs around 2 days and 10 seconds to avoid this warning.) However, I don't want to set JS/CSS any longer than that, although I'll see if it is possible to set longer expiration on selected JS files. jquery.js is almost never changed, for example.

Compress components with gzip: I thought I enabled gzip! Somehow, my Web Host Provider has removed the older mod_gzip module. Now I configured it properly for mod_deflate.

Put JavaScript at bottom: all my JS files are loaded on top. Not going to change this.

Minify JavaScript and CSS: I finally added auto-JS minification (with caching). I'm not convinced it is necessary to minify CSS files — there is not much deadweight to remove. Gzip should be sufficient.

Configure entity tags (ETags): YSlow gives this warning if it finds what it thinks is the inode value (based on the server's ETag format). This warning can be safely ignored, but I disabled the inode value anyway. Size plus timestamp is a strong-enough validation.

To sum up

The danger of using a grading tool is that we may blindly optimize for it to get grade A. No, a tool is a tool. Use its recommendations only when it make sense.

25 cents per GIRO top-up

transport

With the move to the new ez-link card, there will be a $0.25 charge per top-up. The maximum top-up amount is $50, so a daily user will probably top-up twice or thrice a month. It still isn't much, but a precedence is set: paying for convenience. (When it should be the other way round, since they are saving manpower costs.)

Judging from the online reaction, most people will choose to cancel this service. That's what they claim. Whether they'll actually do it or not is another matter.

Even when this service was free, I never found the need to use it. Topping up once or twice a month is almost effortless.

The setTimeout() minefield

programming

Who would have thought that this simple piece of code contains four problems?

var value;

function update(v) {
  value = v;
  ...
  setTimeout("update(value + 1000)", 1000);
  }

Use function, not string

setTimeout() takes in a function. Most people don't realize we can wrap complicated code inside an anonymous function:

  setTimeout(function() { update(value + 1000); }, 1000);

What is the difference? One word: google.

Local/external references

We use both the variable as-is and a local copy of it. This is dangerous because we may not know we are referring to the same variable!

It does not matter for simple functions, but we are likely to follow the same style for bigger functions and that's where mistakes happen.

Avoid this pitfall in all cases.

Explicit counting

setTimeout() is non-deterministic. We request to delay 1s, but that does not mean we will actually delay 1s. However, we always increment by 1,000. As time passes, we will lose sync.

Non-stopping

This function calls itself endlessly and it does not provide a way to stop. This may or may not be necessary, depending on the usage.

In this particular case, this function is not designed right. Whenever this page is fetched (via AJAX) and run, a new instance is started — but the old one is still running!

(The code should either stop the old timer or reuse it.)

Conclusion

Sometimes, I'm amazed just how difficult it is to write code that is correct.

Rental is a good source of income

finance

News: Is simply banning them enough?

Date: 3 September 2009. Source: TNP.

Cheats abuse cheap HDB rental flats by subletting units for cash

HOW'S this for a money-making scheme?

Rent an HDB flat, and instead of living there, sublet it for nearly 40 times as much as you pay for it.

As these poor folks will attest to, rental is a good source of income in Singapore. Many foreigners, at least the first few years they come to Singapore, rent. There is definitely demand.

For the middle-class, there can be three common scenario:

  • Renting to help defray the cost of living
  • Renting to help with parents' allowance
  • Renting the HDB flat out while living in a condo

The difference between the first and the second is whether it helps you or your parents.

The third is illegal, but HDB doesn't care. IMO, this is a pretty good move (cavets apply). Keep the HDB flat when you upgrade. Think twice if you need to sell to upgrade.

How common is the third scenario? Just think. If the HDB flat is wholely rented out, where do you think the owner is living? (You can only "own" one HDB flat at a time.) And whole-unit rentals are very common.

Sign with your eyes opened

transport

News: 'This is how we market our products'

Date: 3 September 2009. Source: TNP.

Cabbies take legal action against cab company because 'bonus' is not 'real bonus'

FIRST, he had a problem when he wanted to upsize the compressed natural gas tank of his taxi.

Then, he had a run-in with the cab company over the National Day celebratory stickers he put on his vehicle.

Now, Mr Henry Ho, 53, is embroiled in yet another scrape with the Prime Car Rental and Taxi Services.

I can imagine the taxi drivers' faces when they realized they had been tricked.

There is no free lunch in the world. You have to ask where the money comes from. Sadly, it often comes from the client. I find it amusing that the company actually admitted there is no actual bonus at all.

Quote Of The Year: "We are actually helping the cabbies to save." :lol:

The real big OPC news

transport

News: Pre-empted by the Internet

Date: 1 September 2009. Source: ST.

Christopher Tan discusses the frayed nerves surrounding a recent news leak.

NEWS of the enhanced off-peak car (OPC) scheme is probably talk of the motoring town now.

But an unfortunate incident leading up to the announcement of the new and improved scheme — designed to persuade car owners to convert their rides to red-plates — proved to be far more exciting to the newsroom last Friday night.

This is big. Will the Government slap the leakers with the OSA? Don't play play.

There were already rumours a week earlier. Then, speculations trickled in over the week — some turned out to be dead accurate, I must say.

Even on Friday, the news appeared on LTA's website only to disappear shortly later. Too late, someone saw it and the news spread like wildfire.

Expenses for August 2009

finance

TBD.

Fined 5 times for the same offence

transport

In a local car forum, one car driver said he was fined on five occasions — $70 each time — when he parked in front of the Queenstown library. He was fined on the 4th, 5th, 14th, 15th and 17th, but the letters were delivered in one batch on the 21st.

I passed by the library once in a while (Margaret drive is a nice "longcut" between Alexandra road and Farrer road) and there is always a line of cars parked in front of the library.

Is there a lack of parking? No. There is an open-air carpark just opposite and a MSCP within a stone's throw (literally; you can really throw a stone and hit it).

Rather, I think people rather risk the $70 fine because it is so much rarer than the $6-$30 carpark fine — the parking wardens make their rounds almost daily.

Someone must have been sick of all the cars and complained. Otherwise I don't see why the TP will come to such a rustic place to book the cars.

Anyway, could this be a new tactic by the TP — delay the notification to catch repeat offenders? This gives them the flexibility to ignore isolated offences, if they wish to do so.

I'll know that it works when I don't see any more cars outside the library.

Enhancements to OPC scheme

transport

Fresh from LTA! The key changes:

  • e-Day Licence to replace the current paper licence, which offers greater flexibility with more channels for purchase and post usage payment.
  • Revisions to the OPC scheme to allow unrestricted usage on Saturdays and on the eves of 5 public holidays.
  • Cash Rebates for conversion of normal cars to the new OPC scheme of up to $1,100 for every six months' registration as an OPC.

e-Day Licence

Motorists now have the option of buying the e-Day Licence after using the OPC during restricted hours, as long as they do so before 2359 hours the next day.

2359 hours the next day? The driver has one day grace period? That's mighty generous of LTA. But then, it also means that they will be less forgiving.

Road tax

With the extended usage, the annual road tax discount will be correspondingly reduced from $800 to $500. The minimum annual road tax will be $70, instead of $50 under the existing OPC scheme.

$300 in exchange for 52 (to 57) half-day coupons ($10 each). It's worth it.

My take

I love the e-Day licence idea! Now you can't display a fake, tampered or almost-fully torn coupon and try your luck. You can be "caught" and not know it. And you have no more excuses because you are given two days to pay up.

And my advice to cheaters: don't drive through any ERP gantries. They are active even when "Not In Operation". If you are paranoid, avoid all ERP/cashcard carparks as well. HDB carparks are confirmed unsafe. GLC-linked malls may be too...

(I suspect the reason LTA gives two days grace period is because it takes them so long to retrieve the data from all their sources!)

Even roads with bus lane may not be safe — the traffic wardens may be required to look out for OPC cars too.

In one word: game over for OPC cheats.

As for the full-Saturday usage, it'll cause more traffic (OPC is 7.7% of the cars' population). More jams and more ERP, I'm sure of it.

The buzz word today: Cloud computing

computer

Every few years, a new fad will emerge and everyone and his uncle could not get over themselves fast enough to get on the bandwagon. Ten years ago, it was the Internet. Then it was Web 2.0. Today, it is Cloud computing.

Just what is a "Cloud"? I looked it up, and surprisingly, it means the Internet! Because the Internet craze is over, they got to invent a new name to catch people's attention again.

Anyway, fads are good. They bring in the money and projects. They are not necessarily good for end-users (which can be companies) — especially if they have a perfectly working system already. Unfortunately, most people want to keep up with the Joneses, or to be state-of-the-art. Fine, whatever floats their boat.

There is one thing I dislike about Cloud computing already. It is that you lose control over your data. Which inspires me to improvise this Dune "proverb":

He who controls your data controls you!

So you want to roll your own blog comments system

programming

Originally I wanted to roll out my own blog comments functionality. The first thing to do is to design the database schema. We need a table to store the comments, that's obvious. We also need to remember the commenters, so we need two tables at the minimum.

The comments table

Field Type Size Description
Id Int 10 An id that uniquely identifies this comment.
Post Id Int 10 The id of the blog entry.
User Id Int 10 The id of the commenter.
Time Int 10 The time the comment was posted, in seconds.
Flags Int 10 General-purpose flags for various purposes.
Comments String 65,000 The comments.

The user table

Field Type Size Description
Id Int 10 The user id.
Name String 30 The user's name.
Email String 50 The user's email address.
Password String 20 The user's password.
Flags Int 10 General-purpose flags for various purposes.
Key String 16 The user's logged-in key.

Many other fields can be added, but these are the essential fields.

Querying

Getting the comments for a particular blog entry is a matter of issuing this call:

SELECT * FROM comments_tbl WHERE post_id = $post_id;

We can add filters to limit the search when the blog gets too big, but this suffices for now.

What if you want the commenter's name as well? It can be done using a nested query:

SELECT *, (SELECT name FROM user_tbl WHERE
  user_tbl.id = comments_tbl.user_id)
  FROM comments_tbl WHERE post_id = $post_id;

I never really understand the SQL syntax. I usually need to experiment until I get it right. For example, I'm wondering if this works or not:

SELECT comments_tbl.*, user_tbl.name FROM
  comments_tbl, user_tbl WHERE post_id = $post_id
  AND comments_tbl.user_id = user_tbl.id;

Registering

Does the user already exist?

SELECT COUNT(*) FROM user_tbl WHERE
  name = $name OR email = $email;

Add a new user:

INSERT INTO user_tbl (name, email, pw)
  VALUES($name, $email, $pw);

Logging in

SELECT * FROM user_tbl WHERE name = $name AND pw = $pw;

If we have a match, we get the user id and generate an authentication code:

UPDATE user_tbl SET auth = $auth WHERE id = $user_id;

Authentication

We need to authenticate the user for every logged-in operation. User id better be indexed.

SELECT COUNT(*) FROM user_tbl WHERE
  id = $user_id AND auth = $auth;

Posting

INSERT INTO comments_tbl (post_id, user_id, time, comments)
  VALUES($post_id, $user_id, $time, $comments);

There!

It's not that difficult, is it?

COE to go up, or down?

transport

News: $10,000 deposit halved

Date: 25 August 2009. Source: ST.

THE Land Transport Authority on Tuesday halved the $10,000 deposit bidders need to fork out for each car and truck COE bid.

The authority said the move is temporary, and will be effective for the October 2009 to September 2010 tender period. It came after years of clamouring from the motor industry.

Many people think this will cause COE to go up even more. I doubt so. Even though this will encourage more bidders, they are speculators who are hoping for a cheap COE — say between $5k to $10k. Prices won't go up, but they won't crash either. In other words, LTA is hoping to put a floor on the COE prices. We are not likely to see $2 COE again.

I definitely hope more people will bid on their own. If they have to come up with the money upfront (the deposit at least), they'll definitely be more prudent. Right now, what's $20k if you can pay it over 10 years?

This LTA action shows what's really happening in the car market — it's not doing well.

A road encounter: how to cut queue

transport

Yesterday while on the road, I saw a Toyota Crown SMRT taxi trying to squeeze his way into a wall of right-turning car queue. No one let him in, of course.

I thought about Dr Taxi's experience in his blog. Could it be him? :-D

Anyway, he's doing it wrong: by turning on the right blinker and trying to join the stationary queue by appealing to the drivers' courtesy. It'll never work in Singapore. In fact, he tried a few places. No luck.

The only reliable way is to take advantage of any opening when the queue starts to move and cut in. Some people say you should not turn on your blinker, or the car behind will speed up. However, I find that the success rate is still acceptable.

Yesterday was a bad day to get to work. I left home half-an-hour later than usual and the roads were very jammed. I usually take 25 minutes to reach office, but yesterday it took me over an hour!

Allowing comments on every page

programming

After allowing readers to comment on blog entries, the next logical step is to allow viewers to comment on every page. I want to add the link by modifying the common JS code. I don't want to touch the pages at all.

There are 4 parts to it:

  1. Create a new forum for these pages
  2. Modify phpBB to fill in the post body
  3. Modify .htaccess to allow simplified URL
  4. Modify the JS footer code to add the comment link based on the page title

Unlike the blog entries, I want the body text to be pre-filled with the link back to the page, hence the need to modify phpBB. This is simple as it is just an extension of modification done to support the subject. (The hard work was done then.)

It is a bit more involved to add the JS footer code. You see, I have three concurrent JS architecture — I never migrated the old pages. (Not unnecessarily, at least. My philosophy is to let sleeping dogs lie.)

The pages can be classified into four generations:

  • v3: like v2, but redesigned and using jQuery.
  • v2: page will setup common header and footer.
  • v1: page calls JS function to get email address only.
  • v0: no common JS or only page-specific JS.

As an aside, it is unlikely to have v4 as v3 is designed to scale because all the functionality are namespaced properly.

v3

I have no problem modifying the v3 code. It is very easy to get the page's title:

var title = $("h1:eq(0)").html();

I assume the first <h1> is the page title. Ah, how jQuery spoils me.

v2

But it is not enough to just allow comments for v3 pages. I estimate only 25% (or fewer) pages are v3! No choice, have to modify the older pages too.

v2 pages can be modified easily as they call a common function to setup the footer. However, there is no jQuery, so I have to use DOM functions. Can I still remember how to do it?

Let's try:

  var elems = document.getElementsByTagName("h1");
  if(elems.length == 0)
    return "";

  var title = elems[0].innerHTML;

Wooh, it's not so difficult after all!

v1

v1 pages represent a bit of a problem. The footer code is built into the page, but it calls a JS function to get the email address. The output is "rendered" to the HTML page using document.write(). That should tell you how old these pages are.

The original code looks like this:

document.write("<a href='mailto:" + gEmail() + "'>" + gEmail() + "</a>");

The only way to add in the link is to hack it. When gEmail() is called the second time, we emit this:

email@server.com</a> <a href='url'>Comment</a> <a>

The last open <a> tag closes the </a> in the page.

This is not recommended at all because gEmail() is not intended to be used this way. However, it is safe to make the change because the code is frozen (only legacy pages use it) and this is the only usage of gEmail(), even though it was meant to be a general-purpose function.

v1, part 2

There are some v1 pages, typically the sub-pages, that include the common JS file, but do not issue any JS calls. For these pages, I set a timer and on timeout, append the link at the bottom of the page.

var divObj = document.createElement("div");
document.getElementsByTagName("body")[0].appendChild(divObj);
divObj.innerHTML = url;

If jQuery is available:

$("body").append("<div>" + url + "</div>");

v0

It is not possible to add a link using JS unless I modify the pages. These pages are very old (2003 and before) and often have no JS code at all.

Technically, I can use a PHP script to inject the JS script on-the-fly. (I'm serious when I say I don't want to touch these pages.) However, there are only a handful of such pages, so I don't think it is worth the trouble.

Open for business!

With this in, viewers will be able to leave comments on any (well, almost any) page of my website! (If there are viewers... if there's a center to the web, my website must be furthest from it.)

Big Mac is really expensive!

finance

For some reason, I thought it was more worthwhile to order just a Big Mac with the large fries. I ended up paying S$6.85 — I was expecting around $6.30.

The large fries cost $2.55. That means the Big Mac costs $4.30! (For such a small burger? It's more worth it to buy 2 McChicken/DoubleCheese instead.)

Nowadays, McDonalds simplifed their menu to show only the set meals. Thus, I wasn't able to compare the prices beforehand. I'm all for simplification — the current design trend is to be focused and leave lots of breathing spaces — but not to the extent that information is lost. There should still be a full pricelist somewhere. (It's actually available upon request at the counter, but it's troublesome.)

The Big Mac meal with standard fries is S$6.80. For 50 cents more, I can get a medium-sized drink and large fries. Not that I think it's worth it. For $7.30, I'll rather go Burger King instead.

Update (27/8): updated to the correct prices.

Parking and the pace of life

A colleague forwarded me this:

The first time I was in Sweden, one of my colleagues picked me up at the hotel every morning. It was September, bit cold and snowy. We would arrive early at the company and he would park far away from the entrance (2000 employees drive their car to work).

The first day, I didn't say anything, neither on the second nor third. One morning I asked, "Do you have a fixed parking space? I've noticed we park far from the entrance even when there are no other cars in the lot."

To which he replied, "Since we're here early we'll have time to walk, and whoever gets in late will be late and need a place closer to the door. Don't you think?" Imagine my face.

How gracious of him. :thumbsup:

I'm so ashamed of myself. :blush: Last time, when I still had the decal to park at the coveted red lots, I parked very close to the entrance. Well, it was one of the few parallel lots, which means people won't bang the side of the car — an inevitably in Singapore. (Non-drivers usually swing the car doors wide-open in Singapore. Drivers are usually more careful.)

Speaking of parking, because my company has more drivers than parking spaces alloted by the landlord, we hold a draw every six months. Fair enough, until you realize employees past a certain rank are excluded...

Recently, the landlord increased the parking charges. Because of that, I decided to rejoin the season parking draw. I'm sorry, fellow colleagues and countrymen: it's every man for himself.

But I promise, if I ever work in Sweden, I'll park far away from the entrance. :lol:

I Am Indexed!

Good news: Yahoo, Google and Baidu bots have visited my forum!

MSNBot shouldn't be far behind, given that it just visited my website yesterday. I know the bots visit my website periodically. Yahoo seems to be doing it every minute.

To all forum members: keep up with the postings!

Right now, if you type smallapple (one word) into Google, Yahoo or Bing, my website comes up as the first match. ^_^

Not that it helps with my PageRank — I have a PageRank of 0. I believe it is because I don't have any inbound links. Anyway, I believe PageRank doesn't really work after the web became interactive: people seldom create standalone webpages anymore. Instead, we have blogs and forums that allow viewers to post comments.

We can quickly increase viewers by posting our link as a comment. We need to come up with some original (relevant) content, of course, but once it is done, we get a multipler effect.

This is nothing new. It is usual to find that the comments or trackbacks are full of spam links in a poorly maintained blog.

I just want to mention this because my bandwidth usage shot through the roof after I posted two links in two medium-volume forums.

Smile!

Something I've always wanted to use, but kept putting it off: smilies!

  • :-) ^_^ :-P :lol: :-D ;-)
  • O_O :cry: :-/ :duh: :mad: :-O :hmm: :-( :phew: :sweat:
  • :clap: :cool: :drool: :kowtow: :nod: :thumbsup:
  • :angry: :bash: :blush: :???: :devil: :love: :sly: :ninja: :rant: :shake:

They are taken from a few places, so there is a mix of styles.

I share these images with the forum for ease of maintenance. phpBB3 comes with oval smilies. I don't like that — smilies should be round! :lol:

If you don't have Javascript enabled, you'll just see the textual representation. Sometimes, I wonder if I'm offloading too much processing to Javascript...

An empty forum

So my forum is devoid of users. I don't care. I created it to allow readers to comment on my blog — which nobody reads anyway. :-D

I don't really want a high-volume forum. After all, I'm on a budget and I have only so much disk space and bandwidth. (But I don't have to worry about that for now! :lol:)

Anyway, after thinking about it, I decided to allow guests to post in the Blog and Feedback forums — posts are more important than members. It makes sense to allow guests to post now because they have no incentive to sign up as members.

I enabled moderation, of course. I don't want delete spam after they are posted. I don't want them to be posted in the first place.

Right now, I'm still flushing bugs out of the forum. It's not easy to configure right. For example, a colleague accused me of talking to myself in the forum. Well, I did not. Nevertheless, I decided to mark the system users explicitly.

I have five system users:

  • root: for admin stuff
  • admin: reserved
  • moderator: to manage the forum
  • tester: for testing purposes
  • blogger: reserved

The others are genuine users, or bots... :ninja:

Looking for a camera?

photography

The Panasonic Lumix DMC-LX3 is a pretty good camera for $700:

  • 24/2 Leica lens (24/2 to 60/2.8 zoom)
  • 10Mp (3648 x 2736)
  • ISO 6400
  • 1/2000" shutter speed
  • Macro: 1cm focusing distance
  • 3" LCD
  • 265g

It doesn't look like it has anti-shake. Also, it is just a tad too big — it is not suitable for a carry-everywhere camera.

Other than that, it looks like a very capable walkabout camera. A 24mm f/2 lens is even faster than most wide-angle SLR lenses. And the Leica name assures that you can actually use it wide open.

This camera got my attention because of its f/2 lens. If it is also f/2 at 28mm and the camera allows preset zoom (so that it zooms to 28mm on powerup) and the quality is good wide-open, I can buy this and throw away my Minolta AF 28/2 lens.

Now, for $400 more, you can buy the D-Lux 4, which is the Leica branded version. Some people have posted comparsion photos online. Unfortunately, these are useless — because the photos are resized.

I believe that the D-Lux 4 is better, just not $400 better. I won't recommend a P&S photographer buy it — it would be a waste of money.

Or maybe not.

Here's the money quote from a friend: "If I buy the LX3, I will think about the Leica. However, if I buy the Leica, I won't think about the $400."

Windows 7 desktop slideshow stops working

computer

I am very pleased with the Windows 7 desktop slideshow. It is the icing on the cake and makes me feel I'm using a new OS — even though it is just a new skin on top of Vista's architecture. (Vista is Windows version 6.0. Windows 7, despite its name, is version 6.1. The version number does not lie.)

It's not perfect, though. The images are supposed to fade gracefully, but they don't always do that. Perhaps it is due to my virtual desktop manager.

I got a more serious problem on my PC. The slideshow doesn't work. After a long day of troubleshooting, I found that it works exactly once after I restart the PC. Google didn't turn up anything, until I saw a defect logged against the UI that the slideshow cannot be easily paused.

After reading the article, I realized I had accidentially set the slideshow to pause mode in the power settings! It worked after I re-enabled it.

This is so frustrating. The submitted defect is right. The personalization tab should show whether the slideshow is paused or not.

Pending OPC changes

transport

News: Six months since Govt say they'll tweak off-peak car scheme

Date: 18 August 2009. Source: ST.

It has been six months since the Government said it would tweak the off-peak car (OPC) scheme to make it more attractive for people to own cars with restricted hours of usage.

The motor industry, OPC owners and would-be owners are not exactly on the edge of their seats.

How can the system be made more attractive when the $17,000 tax break accorded to an OPC is tied to an assumed monetary worth of unused hours in a typical car's lifespan?

I can feel changes coming to the OPC scheme. Some of the ideas are really not feasible — be careful about the "law of unintended consequences".

Half-day license? It will lead to more cars, not less, during peak hours.

And a more attractive OPC scheme will lead to higher COE, not less. First, it will increase demand slightly. Second, whatever savings will be dumped on the COE so that the total cost remains the same.

Parking woes at malls

transport

News: Malls in a jam

Date: 3 August 2009. Source: ST.

Many Singaporeans are all too happy to join a queue, be it for food, flats or freebies.

One queue they unanimously hate, though, is the ubiquitous one waiting to enter or exit the carpark of popular malls.

The latest shopping centre to suffer from traffic problems is Ion Orchard, which opened two weeks ago.

It has been like that since a few years ago. There are just not enough parking lots — anywhere in Singapore — to handle the local demand. I hate to drive for this reason.

And the reason is very simple: parking is simply too cheap ($1/hour outside CBD and $2-3 per hour inside CBD). Per-entry charges makes parking even more affordable.

(I'm speaking from an economist point-of-view. As a driver, I feel the pain when I pay for parking.)

I just have to lose my company badge...

I budgeted $50+ to buy a graphics card for my office PC. The next day, I lost my company badge, which costs $50 to replace.

My budget is gone. Is it a coincidence, or fate?

Lost my bookmarks again!

computer

So I reinstalled Windows... and I forgot to save my bookmarks again. I backed up the Favourites folder, but that saved the IE bookmarks only (which is next to useless, because I seldom use IE). Opera doesn't use this folder, so I lost most of my bookmarks.

Why does every browser use its own bookmark folder? They should all use the same one.

More Windows 7 first impressions

computer
My Windows 7 Desktop

It is faster than Vista by a mile. It starts up pretty fast and hibernates in acceptable time. Everything feels slick, polished and snappier. Well done! It has been said, "this time, Microsoft gets it right".

Some other impressions:

  • The UAC isn't intrusive at all.
  • Apple's mighty mouse works out-of-box.
  • The slideshow desktop background is very slick.
  • The shortcut arrow overlay for desktop icons is still as large as Vista. Luckily, the same trick to "remove" it still works (by changing it to a transparent overlay).
  • I'm not able to disable ClearType totally. This is pretty irritating, but not a deal-breaker.
  • I'm not able to remove drop shadows from the desktop icons totally. This is pretty annoying.
  • The classic start menu is gone. Oh well, I can get used to the new one.
  • The taskbar is new. I'm indifferent to it.

I expect most apps to run as-is, since it shares the same underlying architecture as Vista. However, system utilities may not work. I tried two:

Glass2k works even better than on Vista: it only makes the background transparent, the foreground remains crisp. This is transparency done right! Unfortunately, admin command prompts (and other high-security windows, such as RegEdit) still cannot be made transparent. But I'm happy with it. In fact, Glass2k works even without Aero. This is wonderful!

My favourite utility JS Pager (a virtual desktop manager) does not work right. More on this in another entry.

PhD required to drive a taxi

transport

News: PhD holder now a taxi driver

Date: 19 August 2009. Source: ST.

Online buzz over former researcher's blog on his exit from A*Star unit

COULD this be Singapore's most well qualified taxi driver?

Dr Cai Ming Jie became an SMRT cabby last November after spending 16 years as a researcher at the Institute of Molecular and Cell Biology (IMCB) of the Agency for Science, Technology and Research (A*Star).

His career switch has become a talking point online after he started a blog earlier this year. Alongside his experiences as a cabby, he takes issue with the circumstances of his departure from IMCB last May.

Most qualified or not, Dr Cai is definitely now the most famous taxi driver in Singapore.

I read his blog and I think he made two fundamental errors:

  • He was not familiar with Singapore's landmarks. Okay, can't blame him.
  • His meter doesn't show the CBD nor ERP surcharges. That's a big no-no.

Wasted 3 hours on an IE 6 bug

programming

A colleague told me that one particular page didn't display properly in IE 6. No problem, just dissect the problem area, isolate the bugs, and apply the fixes individually. I expected this one to be slightly harder to fix because it looked like a nested bug.

After trying the usual suspects and then just plain throwing everything I knew at it, I still couldn't get it to work.

I couldn't find anything because I googled "ie6 float margin bug" and got the usual double-margin bug for floated elements.

Finally, I thought about the nature of the bug and googled "ie6 float inherit margin bug". Bingo! I found it and was able to fix it within a minute.

This shows that when it comes to IE 6 CSS bugs, there's no need to think about them. Smarter people have solved them, you just need to know how to google. :-) My mistake was that I did not translate my observations into search words properly.

This also marks the first CSS bug in our code that requires special JS handling. It cannot be "hacked" using CSS at all.

First impressions of Windows 7

computer

I installed Windows 7 RC on a 2004-era PC (Xeon 2.8GHz HT, 2.5GB RAM, 67GB SCSI HD).

It installs pretty fast — in about half-an-hour. And it only takes up 10 GB, including the swap space and hibernation file. (It is still a lot compared to XP, but it is a lot better compared to Vista.)

It looks and feels a lot like Vista. No wonder people are saying this is Vista Service Pack 3.

I'm stuck at 1024x768 due to my unsupported video card. I'm able to install the XP driver to get 1600x1200, but I also get the BSOD (Blue Screen of Death) from time to time.

What's next:

  • After I installed it, I found that Windows 7 has gone gold! I will need to reinstall it.
  • I may buy a low-end graphics card (at my own expense) so that I can use the Aero interface.

Buying a graphics card for such an old PC is not easy — I need to get an AGP card. Most graphics cards are now in the PCI Express form factor. I don't think the PC supports it.

Also, since Windows 7 is out (for corporate users at least), I may install it on my notebook instead of updating to Vista SP2.

Update: I found that I can set the graphics resolution to 1600x1200 using the standard VGA driver, but only in 16-bit color.

File structure re-org

computer

My PCs at work:

CPU RAM HD OS Primary use
P4 2.4 GHz 512 MB 33.8 GB XP Office apps
Xeon 2.8 GHz HT 2.5 GB 67.7 GB XP Test machine
Core2Duo 1.2 GHz 2 GB 74.5 GB Vista Used in meetings
Xeon QuadCore 3.0 GHz (x2) 4 GB 447 GB Linux File Server

I want to install Windows 7 RC, but I don't want to install it on my notebook, so the only other choice is the Xeon PC. But first, I need to offload its files elsewhere.

I am running low on HD space on all my PCs, except for the Linux PC. After thinking about it, I decided to have just one master repo on the Linux PC. This will free up a lot of space on the other PCs.

The Linux PC is used for compilation, but I seldom need to do that. I use it as a staging server for the device's website instead. It runs Apache, PHP and even MySQL.

I have used the P4 PC for 7 years. It is pretty reliable, although the HD may fail any time — it has already well exceeded its MTBF. The only thing I can't run on it is FireFox with FireBug (which bloats to 700+ MB RAM after running for a while).

After installing Windows 7 RC on the Xeon PC, I will resume using it as my test machine. I need to run all the browsers on it simultaneously, that is why it has to be a pretty powerful machine (not really, it just need lots of RAM).

Linked up the blog and forum, phew!

programming

At last, I've linked up the blog and the forum. There is a comment link at the end of every blog entry that will bring the reader to its thread if it exists, or he'll be prompted to start one otherwise. (Or be prompted to log in to do so.)

For a long time, I don't have a way for readers to comment. I never thought of using a forum either, until I encountered one website doing so. The owner had both a blog and a forum. He didn't want to check both, so he disabled the blog comments and used the forum for comments (one thread per entry). I like this method, so I copied him.

There are a few reasons to put comments in one place. Blog comment systems are usually less secure — thus easier to spam. And readers need to register and log in twice (due to lack of integration between the blog and forum).

A few compromises:

  • Because I use the search function, I need to allow guests to search the Blog forum.
  • There is a search flood control. I'm not going to do away with it. It doesn't affect logged-in users much, but all guests share the same flood counter.
  • The search does not return the correct post if the title is not unique enough. phpBB does not support searching exact phrases.

Modifications

I modified part of phpBB to achieve this.

search.php now takes in a new command that handles this special behaviour.

posting.php allows the subject to be specified in the query string.

The mighty mouse, part 2

computer

Some comments after using Apple's mighty mouse for a few weeks:

  • It is hard to trigger the right button properly.
  • It will lose connection with my notebook from time to time. I need to reset the Bluetooth to recover the connection. The bad news is, my wireless connection is reset too. (They are tied together.)

I almost got the Logitech V470 mouse (S$72), but then I goggled and found that Logitech has just released a new mouse, the M555b! It's pretty slick looking and has a very interesting hyper-fast scrolling feature: you spin the wheel and it'll keep going. I'm likely to buy it.

Lowered visual confirmation settings

phpBB allows four levels of visual confirmation settings:

  1. None
  2. Simple
  3. Advanced
  4. Advanced with noise

I was using L4, but a user complained it was impossible to read, so I lowered it to L3.

An estimate of their complexities: I needed an average of 3 tries to get past L4, but only 1.5 tries for L3.

It is an arms race between the bots and websites. L2 is already offering no protection. L3 will be cracked sooner or later.

Surprisingly, an effective anti-spam technique today is to add custom questions so that a generic attack will not work for your forum. Unless you have a high profile forum, hackers will not target you specifically, hence you will be safe.

I like that idea, so I did that and lowered the visual confirmation settings to L2. Let's see how it goes.

Spice and Wolf 2 economics

finance

The second season of Spice and Wolf continues with its economic theme.

First deal

Lawrence sold an assortment of nails to his old friend, Mark Cole, in the town of Kumerson. Mark's initial offer was 10.5 Lumione (exchange rate at 1 Lumione to 34 Trenni). It was lower than Lawrence's purchase price! Lawrence wanted 16 Lumione. After some haggling, they finally settled at 14.67 Lumione.

Suppose Lawrence bought the nails for 12 Lumione, his profit is 22.3%!

Lawrence had scaled down his business somewhat. The last we saw him, he had 63 Lumione with him. Okay, maybe he dared not risk all again.

The nails consist of 120 three-patte, 200 four-patte and 200 five-patte nails. On average, each nail costs almost one Trenni (0.959 to be exact). These are very expensive nails!

Second deal

Amati issued a challenge to Lawrence that he would come up with 1,000 Trenni to "redeem" Horo in two days time. Lawrence did not believe Amati could do it, so he accepted the challenge.

If Lawrence had dismissed the challenge, we could have skipped 75% of this arc.

After some legwork, Lawrence determined Amati paid 200 Iredo in tax, which means he had around 800 Trenni.

10 Iredo is about 0.25 Trenni, so Amati paid 5 Trenni in taxes. That's a tax rate of just 0.625%. That's very low. (Even if the tax starts from 500 Trenni, that's just 1.67%.)

Third deal

Amati's scheme to earn 200 Trenni was through the auctioning of pyrites. Lawrence said the market value should be 10 Iredo, but by the time Mark told Lawrence about it the next day, the price was 270 Iredo — and it was still rising. Lawrence then bought 4 pyrites worth 30 Trenni from Mark (300 Iredo per pyrite).

Lawrence was confident he could make the price of pyrite come down. He made a deal with Amati: that he would sell Amati 500 Trenni worth of pyrites, payable now, but collectable the next evening (after the challenge).

Amati paid him 200 Trenni immediately and 14 Lima gold coins worth 280 Trenni the next morning. (Lawrence said the exchange rate was 1 Lima to 20 Trenni.) I found it strange that Lawrence, rather than Amati, absorbed the exchange loss.

Fourth deal

That night, Lawrence tried to buy 400 Trenni worth of pyrites from Deanna, but she had to check if the earlier customer would cancel his order first.

Suppose the market value was 500 Iredo per pyrite (it was not stated), 400 Trenni worth works out to be 32 pyrites.

Lawrence then bought 370 Trenni worth of pyrites using Mark's connections. Lawrence now has 34 pyrites.

The next day, Lawrence sold off all in one shot in the open market after securing another 250 Trenni worth through Mark. The selling price was not stated, but it had come down slightly from its high average selling price of 800 Iredo per pyrite.

Suppose the selling price was 700 Iredo per pyrite, Lawrence sold 34 pyrites worth 595 Trenni and bought 14 pyrites.

Later, acting on a hunch, Lawrence sold off his pyrites. He was joined by Horo, who had bought Deanna's pyrites. Together, Lawrence had sold 80 pyrites (worth 1,000 Trenni). That caused the market to crash. The final price wasn't revealed, but it should be around its original market value.

Outcome

Lawrence

How did Lawrence fare? It was not stated.

Qty Unit Cost Cost
4 300 Iredo -30 Trenni
30 500 Iredo -370 Trenni
-34 700 Iredo 595 Trenni
14 700 Iredo -250 Trenni
-14 550 Iredo 193 Trenni
Final 0 -138 Trenni

The final selling price of 550 Iredo per pyrite is a pure guess. Lawrence was starting to force the price down.

Lawrence still made a gain because he short-sold 500 Trenni worth of pyrites to Amati.

Horo

Horo bought her pyrites on credit from Deanna. She should have suffered a loss.

Qty Unit Cost Cost
32 500 Iredo -400 Trenni
-32 250 Iredo 200 Trenni
Final 0 -200 Trenni

The final selling price of 250 Iredo per pyrite is a pure guess. The price was free-falling at this point.

Amati

Amati should have suffered a huge loss. The novel said he did not make a loss. That's impossible.

Amati paid Lawrence 500 Trenni upfront. He got his pyrites in settlement, now worth say 5 Trenni. That's a loss of 495 Trenni. Plus he still had some 200 Trenni worth of pyrites on his hand. His realized profits could not have been more than 200 Trenni, so his net loss was 500 Trenni.

Fast food: the last holdout

Since 3 years ago, Chinese Nationals started to "colonize" the food courts. However, they were absent from fast food joints for a long time, due to the English barrier.

Not any more. I'm now starting to see more and more of them — the younger generation who knows English. Their English is only marginally passable, but it'll do.

Working part-time at a fast food joint is an avenue for poor young Singaporeans to earn pocket money. Now they'll have some competition.

When costs increase, people start to buy cheaper substitutes. This happens to people too. When wages go up, bosses start to hire cheaper people. This is especially doable for manual labour and technical fields.

When wages are stagnant, it keeps a cap on housing prices. But this does not mean that housing prices cannot increase. It can — if the mortgage period is increased.

How old is Earth?

A creationist will tell you that the Earth is only 6,000 years old — recorded history on the bible is only that old. (Young Earth) Creationism is also known as Intelligent Design.

I, on the other hand, subscribe to our current theory that Earth is old — incredibly old.

Some milestones:

4.5B years ago Our solar system was created
4.4B YA An asteriod crashed into Earth and broke it into two
3.8B YA Late Heavy Bombardment stops
3.8B YA First lifeform; oldest rocks on Earth
2B YA Earth gains oxygen in its atmosphere
540M YA The Cambrian explosion
475M YA Land plants
360M YA Amphibians
300M YA Reptiles
200M YA Mammals
150M YA Birds
65M YA Very famous event
2.5M YA Genus Homo
800k YA Use of fire
200k YA Homo sapiens
12k YA Agriculture
5.5k YA First recorded human history

The alternative story of God and the Devil

Most of us are vaguely familiar with the Adam and Eve story from Old Testament:

  • God created them and put them in the Garden of Eden.
  • They were told not to eat the fruit from the Tree of Knowledge of Good and Evil. (It's not an apple, as commonly depicted.)
  • A serpent told them to eat it anyway. (Not the devil; the link was established in the Middle ages.)
  • They did, and were banished from the garden.

God is good, the Devil is bad. In the Christian mythology, unlike many other mythologies, the Devil is not the opposite of God. He is merely a fallen angel. Fallen in the sense that he uses his powers for evil.

Now, what if the Devil is good? In the Middle ages, saying such things will get you burnt as a heretic. Thankfully, we live in much more relaxed times — at least for Christianity.

Recently, I came across a show that ran with this premise:

  • The angels want to create a perfect world.
  • When they fail, they simply kill off the existing lifeforms and start again. This accounts for the mass extinction events.
  • One angel is opposed to that. He prefers to correct the existing lifeforms slowly. In the last mass extinction event, he gave early humans the knowledge of fire to survive the Ice Age.
  • Obviously, the other angels do not like him.
  • He is known as the Devil to the current humans, due to propaganda from the other angels.
  • However, lifeforms from the past eras (what's left of them) revere him, because he took their side when the angels tried to kill them off.

I like such alternative takes on traditional pure Good-and-Evil tales. (Of course, after a while they get old too.)

Mass discount

finance
Fried Fritters

I bought one piece of fried fritters for my breakfast today. It is extremely profitable — $0.70 for two slices of fried dough. (Most single breakfast items cost $0.70.)

The person in front of me bought two items. The store lady asked if she wanted to order another. You see, it costs $0.70 per item, but it is only $2 for three items. She declined to do so.

I almost wanted to jump in and join her, but then I thought how should we split the 10 cents savings? And will I still be welcomed at the store?

Analysis

The store opens only in the morning (6 am to 12 pm). This is quite common. Hawkers can make enough money to survive by serving just the morning/lunch or dinner crowd. There is no need to open the store the entire day — unlike retail.

The store sells four or five kinds of food, all variation on fried doughnuts. (I don't know their names in English.)

The store employs three people, one to sell and two to prepare the food.

What is the store's daily profit?

My WAG (wild-ass guess) of their costs:

  • Rent: $100/day
  • Labour: $105/day
  • Raw materials: $0.20/unit

In other words, the store needs to sell around 400 units per day to break even. Can they do it?

The store has good business, so I believe they sell around 150 to 200 units per day from retail alone. They also supply to other stores, which is easily 1/2 to 2/3 of their volume.

Update: changed the analysis due to a calculation mistake.

Motor insurance fraud

transport

News: Police investigate workshop over insurance fraud

Date: 3 August 2009. Source: AsiaOne.

A WORKSHOP suspected of being involved in fraudulent motor insurance claims is being investigated by the police.

In a recent case, the workshop is believed to have even helped in arranging for a false witness to make a claim.

Police have confirmed that they had hauled up three parties involved in a case of fraudulent motor claims for questioning.

Premiums will continue to rise. I won't be surprised if it hits $1.5k in another ten years time.

The not so all-powerful admin

I find it interesting that the all-powerful administrator cannot create arbitrary users in phpBB. (Without mods, that is.)

Also, he cannot violate the email and password requirements. If a unique email is required, he cannot modify two accounts to use the same email address.

I find this quite surprising.

Pricing anomaly

finance

The canteen at my office sells plain bee hoon (and other noodles) at $1. I always order that because I like plain mee for breakfast. $1 is expensive; the going rate at a hawker centre is $0.70.

After a long time, I found that it costs just $1.40 for the mee and one item. Let's see. I'm paying $0.70 for the egg for lunch. If I get the egg during breakfast (and leave it until lunch), I'll save $0.30!

Javascript and ActionScript: which comes first?

programming

I attended a Flash course provided by my company. It is targeted at designers. I suspect they wanted to use Flash to prototype their designs after seeing it in action. (We recently had a replica of the UI done in Flash.)

The course was opened to all, so I signed up for it. I don't really need to use Flash, but it has its uses (see later). If someone wants to teach me, why not?

The course is made up of two separate courses: the first course teaches you how to create animation and the second course teaches you how to achieve interactivity. Programming is required to achieve interactivity. Programming... I bet the designers did not expect it.

I'm struck by the similarity between ActionScript (Flash's programming language) and Javascript. It is not surprising that the syntax is similar, as both are C-based. However, the event handling reminds me of the HTML DOM. So, I googled and found that AS is actually based on Javascript! (The design, that is.)

What Flash can do that Javascript can't

I'm pro-HTML/JS, so I have no intention of using Flash — but never say never. While it is possible to achieve 90% of Flash's capability in HTML/JS today, it is the remaining 10% that Flash is potentially useful.

For example, the web is mostly silent so far. Some people say it is a blessing, but sometimes you want simple sound effects. It has been possible to do that since 2004 — by using Flash. There is no need to wait for HTML 5, which is still WIP and will take some years to become widespread.

Flash also allows you to access the local storage, up to 100 kB (more if the user allows). Some Javascript frameworks use it. This is preferable to Java applet and Google Gears because (i) Flash is almost always pre-installed, (ii) no permission is needed from the user.

And lastly, if I'm not wrong, Flash also has decent socket programming. It should allow persistent connections, so the server can send data back as and when needed and the client will know it immediately. For Javascript, even with AJAX, it is a hack to keep the connection open. And even then, the client must still poll periodically.

Flattening forum paths

I don't like how phpBB shows explicit PHP filenames, so I tried to remove them.

Transformation

Index

http://website/forum/index.php

becomes

http://website/forum/

When was the last time you saw index.php?

Forums

http://website/forum/viewforum.php?f=x

becomes

http://website/forum/x

Originally, I wanted to use /forum/f/x, but phpBB requires the folder to be at the same level.

Topics

http://website/forum/viewtopic.php?f=x&t=y

becomes

http://website/forum/x,y

This works as well (the forum id is not needed):

http://website/forum/-y

Members

http://website/forum/memberlist.php?mode=viewprofile&u=x

becomes

http://website/forum/member=x

I don't intend to shorten it any further.

Attachments

http://website/forum/download/file.php?id=x

becomes

http://website/forum/download/x

Avatars

http://website/forum/download/file.php?avatar=x

becomes

http://website/forum/avt=x

Difficulty

The paths are replicated in several places, so it is not a trivial change. I modified only the main pages, so the original paths are still used in some places. I don't want to modify all of them as I want to minimize the changes.

This is an example of a bad coding practice — it violates the DRY (Don't Repeat Yourself) rule. The paths should be defined in just one place.

No inspection needed

transport

So my CB400F road tax is due soon. I have to pay the insurance and get the bike inspected first.

But when I rode to the inspection centre, I was told no inspection was needed! I got the timing wrong — it was due in 6 months time.

I'm getting very forgetful these days. Well, at least I remembered to pay the road tax — I forgot about it the last time!

Setting up a forum

After a long time, I finally decided to set up a forum for visitors to comment on my blog. (It is currently under beta-testing.)

My web host provider has phpBB and SMF. Both are written in PHP and requires around 55 MB. I believe I can use other forum software, just that I need to upload it myself.

I chose phpBB because I'm more familiar with it — it is very popular. phpBB 2.x used to be quite vulnerable and can be root'ed easily. Version 3.x seems to be fine so far.

After I installed and setup phpBB, I googled and found that SMF appears to be more secure. Oh well.

Security configuration

You can never be too paranoid about security on the Internet.

  • Users must enter a CAPTCHA code to register. I started with the most complex one, but after some feedback, I reduced the complexity. The most complex one is very hard to read even for humans. But what to do? Bots have very good OCR these days.
  • Passwords must be of mixed-case, have a digit and a symbol. This is to make it harder for brute-force guessing.
  • Users are only given a few tries to register/login.
  • Only one user per email address.

Customization

  • I removed the Who's Online list and forum statistics. They are useless anyway.
  • The member list is only viewable by members. Some security folks suggest removing it entirely, but most forums just leave it in.
  • Only members can search.
  • Flood control for searching and posting.
  • Local avatars only.

Usage

  • I use the admin account for admin purposes only.
  • I use a normal account to post (not even mod access).

Conclusion

Other suggestions are welcome.

First impressions of Singapore

Very typical first impressions of Singapore:

  • It is safe
  • It is clean
  • It is boring
  • It is expensive!

Safe

Singapore is safe, but never let your guard down. The police's message to the public a few years ago was, "low crime doesn't mean no crime". That was before the huge influx of foreigners.

Clean

Singapore is clean — only because of the cleaners. For a true picture how Singapore would be like without them, just take a look at the venues after an event is over.

Boring

Being a tiny city-island state, Singapore cannot escape the fact that it has very few (obvious) attractions to offer.

If you don't mind getting hot and sweaty, there are actually many places to go — many of them free to boot.

Unfortunately, many Singaporeans are pampered, so they won't consider places without air-con. It's no wonder it seems all you hear is food and shopping.

And a favourite Singapore past-time? The annual overseas trip — because there are no places to go in Singapore.

Expensive

Singapore is expensive. It is worse if you visit tourist areas. But believe it or not, you still can get by with $2.20 per meal; no drinks. (Most likely it'll cost you $3, though.)

Final thoughts

It is very difficult to correct a person's perception, especially when he relies on public transport. For now, I use Neil Humphreys' Final Notes from a Great Island as a guide to the less well-known attractions of Singapore.

What's missing

I need to add these functionalities to my website:

  • What's new
  • Allow selective image linking
  • Allow comments

What's new

I need to tell visitors that there are new contents on the homepage. I would like to do it automatically, of course. However, a script to go through the entire website will pick up edits as well — not to mention an overkill because I add new contents so rarely.

I think I'll maintain a list manually, but with automatic expiration. That is, the items will disappear automatically after, say, 2 months.

Allow selective image linking

When I tried to add my page as a link in FaceBook, I found that I could not use an image as a thumbnail, because I blocked image linking. I'm not allowed to specify an image from another location (that is allowed to link).

I need to allow one representative image per page.

(This affects pages that do not have embedded thumbnails; thumbnails are allowed to be linked.)

I can define a rule in .htaccess to allow all .*-link\.(jpg|png), for example. However, this means I need to rename the image file.

Another way is to embed a hidden thumbnail into the webpage.

In the end, I found that only the preview fails to work. After adding the link, the thumbnail shows up fine.

Allow comments

Visitors have requested this feature for a long time. To keep things simple, I may create a forum and direct all comments there.

It is not very difficult to create a simple database to store users and their postings, but why reinvent the wheel?

Economics from Spice and Wolf ep 7

finance

Episode 7 is a DVD-only episode. I believe it is not in the light novel as well. Some things we learn from this episode:

  • One Lumione gold coin is equal to 34 to 35 Trenni silver coins
  • One Trenni can last 7 days if you live frugally
  • The clothes Horo was currently wearing was worth 2 Lumione
  • The handling fee for exchanging Lumione to Trenni is 10 Luuto coins or 30 Torie coins (exchange rate to Trenni is not stated)
  • Cheap clothes cost 40 Luuto coins
  • Lawrence's budget was 2 Trenni coins. He bought some ragged clothes with it
  • The clothes that Horo like costed 10 Trenni. The merchant lowered the price to 8 Trenni himself, but Horo drove it down to 7 Trenni
  • Lawrence planned to sell the unused ragged clothes he bought for 1 Trenni

Lawrence claimed that the shop sold him both sets of clothes at a loss.

Lawrence's strategy: first, he bought a set of cheap clothes to make the merchant to think that he could be a regular customer. He also dropped Milone's name left and right. This caused the merchant to throw in some extra stuff.

Then, Lawrence bought the expensive clothes for Horo. He said the merchant wanted to leave a good impression on Horo, whom people mistook to be wealthy due to her high-grade clothes.

Will it actually work? I don't think so.

Value of one Trenni

One Trenni silver coin is of quite high value. From the light novel, we know that 1/3 Trenni can cover a person's daily living expenses. Here, it is 1/7 Trenni per day if you live frugally.

Does it include lodging? Perhaps only a simple shelter — sleeping in the common area in the church, for example.

Converted to present day terms, 1/3 Trenni would be around S$13, enough for 2 to 3 meals and public transport. One Trenni is thus S$40. (One Luminoe is then $1,400. Horo is wearing a set of clothes worth almost $3,000!)

Expenses for July 2009

finance
Category Jan Feb Mar Apr May Jun
Basic 1,061.55 985.02 2,574.12 685.04 987.35 869.82
Cash 213.60 241.50 172.10 151.00 160.00 144.90
Vehicle 258.35 307.97 577.60 1,886.78 146.74 836.51
Others 116.30 875.00 308.15 391.94 66.00 28.99
Total 1,649.80 2,409.49 3,631.97 3,114.76 1,360.09 1,880.22
Category Jul
Basic 2,927.51
Cash 200.00
Vehicle 1,732.34
Others 110.84
Total 4,970.69

July is typically a high-expenditure month due to several annual expenses. Basic expenses is high due to tax. Vehicle expenses is high due to my car's and bike's insurance ($1,356.28 and $155.28).

My tax this year exceeded my monthly savings. This was a surprise to me. I don't recall earning so much.

Insurance goes up every year without fail. The insurers have no incentive to keep their cost down.

In the future, I may pay my tax and insurance by installments, since there is no interest charge to do so.

The Beagle Mospeada is cancelled!

Beagle Mospeada (Stick)

(From toywizard.net.)

Originally, I wanted to write this entry after I got the Houquet figure, but Beagle has just cancelled their Mospeada releases.

There are four main Mospeada riders in the show Mospeada:

  • Stick (the leader)
  • Rand
  • Yellow
  • Houquet

In the past 2 years, three toy companies have come out with Mospeada toys:

  • CM, in 1/18 scale
  • MegaHouse, in 1/15 scale
  • Beagle, in 1/10 scale

All three exhibit solid engineering, but the Beagle is the best — at S$350 a figure. At this price, I can only afford to buy one, hence Houquet. I was crossing my fingers the whole time that the series won't be cancelled. Unfortunately, this was as far as Houquet got:

Beagle Mospeada (Houquet prototype)

(From MacrossWorld; taken at the Tokyo International Anime Fair in March 2009.)

For niche market items like this, usually only the principal character gets produced. It was speculated that sales were poor; the initial run of 3,000 Stick figures were not sold out — and he was expected to be the best selling figure, being the principal character.

IMO, Beagle should have come out with Houquet right after Stick. Houquet, being a girl, should be very popular as well. Beagle went with Rand because it shared 99% of the parts with Stick (effectively, only the head is different).

In any case, S$350 is a crazy price for a toy. IIRC, this would have been my most expensive toy. I guess that there are not enough crazy Mospeada fans in the world.

The mighty mouse

computer

I wanted to get a bluetooth mouse for my notebook — so that I can get rid of the chunky USB receiver!

Just nice, a friend of mine was not using his mouse, so he lent it to me.

Apple Mighty Mouse

Some things that may not be immediately obvious:

  • It is a 2-button mouse
  • The scroller can scroll in x and y directions (y-only in Vista)
  • The scroller acts as a third button
  • It has a "squeeze" function
  • It can work on one battery
  • It requires a passcode of "0000"

I thought the AMM is a one-button mouse — the seamless clam-shell design means that it has only one mechanical sensor. However, it uses capacitive sensors to differentiate between left and right click. That's high tech.

But it doesn't work perfectly. The mouse gives precedence to left click, so to right click properly, you got to lift your finger from the left-click position.

Also, if you drag-and-drop and now want to cancel it... you can't right click to do it. You got to use the escape key.

The "squeeze" function is not very useful in Vista. It does nothing most of the time. However, it acts as the back button in Opera.

My old mouse can work on one battery too. I didn't realize that until today.

Design philosophy

I can't help but think about the design philosophy behind the mouse.

First is the looks. Apple's mice traditionally use the same seamless clam-shell design, but they were also one-button mice. Right-click is more and more prevalent (context-sensitive menu), so even Apple has to bow towards the inevitable. Now the technology exists to support two buttons and still look traditional.

Second, Apple ships the mouse with Lithum AA batteries because they are lighter. Wow, Apple actually places user experience over cost. That's something we can learn from. (Of course, you also pay a premium for Apple products.)

Spice and Wolf economics

finance

Spice and Wolf is a rare anime that uses economics as a central theme — our protagonist Lawrence Craft is a merchant. However, it doesn't quite pull it off.

The show is slow, very slow. The show is 12 episodes (with one extra filler episode) and is made up of two arcs comprising of 6 episodes each. That covers two of the light novels the show is based on. Personally, I feel each arc can be done in 3 to 4 episodes. That would help with the slow pacing.

First deal

Note that this is merely Lawrence's first shown deal in the show. He has been a travelling merchant for seven years.

Lawrence sold his merchandise of 70 furs to the Milone Company in the town of Pattio. He was offered 132 Trenni silver coins initially, but he managed to up it to 140 coins. (We are not told his cost price.) Then, his partner Horo — she called herself the Wise Wolf of Yoitsu — took over and finally concluded the deal at 210 coins!

Now, Milone Company got ripped off. The trader Lawrence liased with was willing to pay a premium because of something Horo did to the furs. However, the effect was only temporary. The trader later realized it and wasn't very happy about it, but he said the company would still do business with him. Go figure.

I don't think ripping off your business partner makes good business sense.

Note the explicit mention of Trenni silver coins. There are many currencies around. Some of the currencies that are mentioned:

  • Bishop Radeon (later period)
  • Faram
  • Firring
  • Landbart Bald King
  • Luuto
  • Mariine
  • Midsfing Cathedral
  • Saint Midsfing

Is this realistic? Perhaps, but it sure is confusing. Luckily, this region uses Trenni as its major currency, so unnamed coins are implied to be Trenni silver coins.

Second deal

Zeren told Lawrence he knew of a currency that was going to increase its silver purity. Of course, he would tell him the currency only with a deal of 10 coins upfront and 10% of the profits. And he would return the ten coins if Lawrence made a loss.

This is the first instance of the economic mumbo-jumbo, but the jist of it is that you hoard the coins before the increase, because the currency will be worth more after the increase and it applies to all the coins.

(Why should it? The ones with lower purity is going to be worth less, that's for sure.)

I feel this is a bad deal for Lawrence, due to the high upfront cost. Lawrence only has around 200 coins to play around. It is unlikely he can commit everything to this currency speculation. Suppose he commits 100 coins, he gains only 10 coins even if the currency appreciates by 10%.

The currency turned out to be Trenni. The strange thing is, Lawrence and Horo found that — thanks to Horo's extremely sharp ears — the new coins had lower purity!

Lawrence couldn't profit from this knowledge, but he knew someone who could — the Milone Company.

Third deal

Lawrence sold this knowledge to the Milone Company.

We are told how the scheme works:

  • Some guy spreads the news that the currency is appreciating
  • Causing other merchants to hoard it
  • When the currency is devalued, everyone dumps it and the mastermind is able to buy them at a discount.
  • Plus, everyone will keep quiet for their reputation

How exactly does one profit from a devalued currency? It will be revealed later. For now, we just know that you need lots of it and only a big company, such as the Milone Company, can pull it off.

Lawrence expected the Milone Company to gather 300,000 coins. Assuming a 10% margin, Lawrence expected to get 2,000 coins as his share — enough to open his dream shop. His share of the profit is thus 6.7%.

(I don't see why the merchants will make a big loss. The market value may be low, but the king should be willing to buy back the coins at a higher price to remint them.)

Fourth deal

Things didn't go well, of course. Lawrence and Horo were pursued by the mastermind, the Medio Company, almost immediately. Horo acted as decoy so that Lawrence could reach the Milone Company and ask for help.

Unfortunately for Lawrence, the branch chief Marlheit told him that they had all the information they needed from him, so they couldn't help him — to avoid jeopardizing their operation. However, he found it strange that the Medio Company went after Lawrence because they would have known Lawrence was of no value anymore.

The reason is then revealed: the Medio Company was after Horo! They threatened to turn Horo to the Church unless the Milone Company kept still until the deal was over.

Marlheit and Lawrence then discussed a few possibilities:

  • Rescue Horo, but there's nowhere safe to hide her
  • Report to the Church that the Medio Company has Horo

Lawrence then said the biggest profit from the devalued currency was that the holder could extract special rights from the king. He told Marlheit to negotiate quickly with the king and then sell the rights to the Medio Company.

At the end of it all, the Milone Company managed to get the rights from the king and sell them to the Medio Company. Also, they collected 307,322 coins and got 350,000 coins (of lower purity, I presume) in return.

(It looks like the Milone Company took just 3 to 4 days to collect the coins. Impressive.)

Lawrence's share is then revealed to be 5% of the profits, which works out to be just 120 coins. The reason? Transport fee, tariff and contract handling fee — especially the last one.

The Milone Company only made a profit of 2,400 coins. That is very little, considering that they got an extra 42,678 coins.

Marlheit then rewarded Lawrence with 1,000 coins from the profit of the special rights, which he didn't do anything at all. Is 1,000 coins a lot? That depends on the special rights. Is it worth 10,000? 50,000? Or even 100,000?

We were not told, but my guess is at least 50,000, because the king tried to minimize his loss by clawing back around 35,000 coins through the contract handling fee. That means Lawrence got a cut of 2% for free!

Fifth deal

Lawrence requested his payment in pepper, which he then transported to the town of Poloson for sale.

The pepper was weighted at 45 weights, one weight being worth one Lumione gold coin, which was in turn worth 32.83 Trenni silver coins. He would have gained 1,477 Trenni. Assuming he bought 900 Trenni worth of pepper (1,000 + 120 - 140 - some loose change), he would have made a profit of 64%!

(Horo spent 140 Trenni at the Milone Company. That's a lot of money to spend in one go!)

Horo detected that the weighing scale was rigged. Lawrence then forced the merchant at the the Latopeiron Company to sell him goods not just on credit, but with 1x margin as well.

I don't see why Lawrence would not know the true weight of the pepper. After all, he bought and sold it in one transaction.

Sixth deal

Of all goods, Lawrence chose high quality armour to sell at his next stop, the Church City of Rubinhaigen.

Lawrence bought 20 armours at 100 Lumione — which was already half-priced. We were also told he owed 47.75 Lumione, which means that the pepper was worth 52.25 Lumione instead of the 45 that the merchant offered. That's almost 15% difference.

(One set of armour costs 10 Lumione. We are not told how much Lawrence expects to sell one for, but he said the profit margin is very slim.)

On the way there, Lawrence took a detour and bumped into Nora Arent, a shepherd. Nora asked to be hired as an escort. Her skills, together with her dog, Enek, impressed Lawrence enough to strike a deal for two days' escort to Rubinhaigen for 40 Torie, extra if attacked by wolves. Lawrence eventually paid her an equivalent of 45 Torie.

(We are not told the Torie-Trenni exchange rate.)

At the city, Lawrence had to pay 10% in tax, either in cash or goods. The tax seems pretty high. Lawrence chose to pay using the armour. The counter person said "A wise decision". (I don't see why it matters; see later.)

Unfortunately for Lawrence, when he went to the Remerio Company, he was told that that the armour market had crashed and armours were now worthless. Basically, Lawrence got ripped off big-time by the Latopeiron Company when he tried to take advantage of them.

(If only he chose anything but armour... the anime didn't explain it, but the armour market crashed because there was no military expedition this year.)

Also, the Latopeiron Company transferred Lawrence's debt to the Remerio Company. I feel this is more for convenience of the plot than anything.

I don't see why it makes a difference whether Lawrence paid the tax in cash or goods. The armours were worthless anyway, so the tax couldn't be very high even if Lawrence paid in cash.

Seventh deal

Lawrence only managed to raise 3.29 Lumione, shortfall of 43.71 Lumione. This is a terrible showcase of his business connections.

At his wits end, Lawrence made a deal with Master Remerio, the chief of the Remerio Company, to smuggle gold into Rubinhaigen.

You see, cities under the influence of the Church have a heavy tax on gold. It is very profitable to smuggle gold in. However, the punishment is also very severe, so no one dares to try it.

Lawrence's proposal: the Remerio Company to raise the money, then buy the gold from the heretic town of Rumtola, and finally, sell it through their connections. Lawrence would then get a cut of the profit.

(Heretic because Rumtola is not under the influence of the Church.)

The Remerio Company raised money to buy 600 Lumione worth of gold, which could then be sold at 6,000 Lumione. (I don't see how they can sell at 10x. Lawrence earlier said the price is only doubled in the Church cities.)

Lawrence's share is 150 Lumione. (In the light novel; it is not stated in the show.)

Lawrence then sought out Nora and offered her 20 Lumione for her role. Nora was a shepherd for the Church's sheeps, so she was not searched. Nora was a very quiet and gentle girl, but she had no love for the Church. The real reason why she was working for the Church was that they were placing her under observation — they suspected she was a witch. The Church asked her to go to dangerous places to tend her sheeps. When she didn't lose any, they thought she used heretical magic. In reality, she was just a very good shepherd.

20 Lumione is a lot for Nora, but she should get more as she is the principal means to bypass the inspection.

Eighth deal

Needless to say, Lawrence got double-crossed again. The Remerio Company, in dire straits itself, could only raise money to buy 100 Lumione worth of gold. 1,000 Lumione was just enough to pay off their debts and start afresh. They had to get rid of Lawrence and Nora.

Lawrence managed to turn the situation around with Horo's help. He forced Master Remerio to buy the gold from him at 500 Lumione — payable over ten years.

Ninth deal

Lawrence then brought the deed to his guild, the Lowen Trading Guild. The person-in-charge bought the deed from him for 30 Lumione, plus another 100 if the gold was successfully smuggled (which it was, obviously).

In other words, Lawrence sold a 500 Lumione 10-year "junk" bond for 130 immediate cash. He ended up with 63 Lumione after paying Nora and his debt — 10 Lumione more than before his misadventure. Not bad.

(Lawrence's guild could have bailed him out. But then, we would have no story, so they invented a reason why the guild didn't do so — Lawrence got into this plight due to his greed.)

Conclusion

I feel Lawrence made his profits too easily. The price differential should not be so much, especially when the towns seem like they are neighbouring towns.

On the currencies

We are only exposed to two main currencies: Trenni and Lumione.

A Trenni silver coin can buy around 40 apples (at least), or pay for a night's lodging and meals. It is mentioned in the light novel that 1 Lumione gold coin can cover a person's living expenses for 3 months. Given that the exchange rate of 1 Lumione to 33 Trenni, that means a person can live on just 1/3 Trenni per day. To spend one Trenni in one go is a treat.

A third currency, the Torie, is used just once. Lawrence paid Nora 45 Torie for her escort service. How much is one Torie? My guess is that 40 Torie is just slightly more than 2/3 Trenni — no one will be willing work for less than 1/3 Trenni per day, because they won't be able to feed themselves. (At that time, Lawrence didn't know about Nora's circumstances.)

If the exchange rate is 0.75 Trenni to 40 Torie, then the exchange rate is 1 Trenni to 53.3 Torie.

GW-BASIC Trucker tips

GW-BASIC Trucker game, fixed

I made so many bug fixes that I decided to modify the title screen too.

Trucker is a very simple game, but gives quite a realistic trucking experience, I'll say.

Starting

You can choose any cargo and route. Oranges will typically net you $1,200, freight forwarding $800 and US mail $600.

You can make it to New York in time on all three routes — some just barely, though.

Load just 39,350 pounds to avoid problems with weighing stations.

Don't buy tires for the northern and middle routes. You have a spare and flat tires are a rare occurence. Wait for one to blow first. It is worth buying a retread tire for the southern route.

The most expensive fines

Don't run out of fuel. That's an extra $145 (assuming $1/gallon is a fair price).

Don't be overweight. That's at least a $200 fine.

Always have one spare tire. Calling a tow truck is $400.

Journey

Always travel at 55 MPH. It consumes the least fuel.

Top-up to full when fuel is cheap (< 95 cents). When it is expensive, skip or just top up a little.

If you are shipping oranges, don't top up after resting. That takes an extra hour.

Rest one or three hours in the day time. Resting in the day time is only half as effective, but these two times give you one and two hours rest.

Rest 5 to 6 hours at night to recuperate.

Rest opportunistically when tired. Rest when fatigued. You must rest when you are exhausted! (In the later half, it is difficult to be better than tired if you want to make it in time.)

Race against time

If you travel at 55 MPH, it is usual to reach New York at night. You need to wait until the next day. That'll cost you an extra $85, and 10% penalty if you are transporting freight forwarding. You need to plan your rest stops well.

A burger alone is not always cheaper

finance

My father went to McDonalds to have his breakfast. He told the counter crew he just want a burger, so he was recommended the Sausage McMuffin burger — at $2.95.

That's a combination of ignorance and bad advice there.

For $2.50, you can have the same burger and a cup of "premium" coffee.

Dissecting GW-BASIC Trucker, part 2

Speed

Your speed can range from 20 MPH to 1.5x the speed limit. The usual speed limit is 55 MPH, sometimes 35 MPH due to road works.

Speed affects three things: time, fuel efficiency and speeding tickets.

Time is not really a concern. You can make it to New York under 4 days at 55 MPH if you time your rests well. You can earn over $500 easily if you avoid the most costly mistakes.

Also, you get the best fuel consumption (FC) at 55 MPH: 12.22 gallons/hour. At 65 MPH, you consume 26 gallons/hour! At 45 MPH, 18 gallons/hour. So, drive at 55 MPH.

(The formula is SP/(4.5-MIN(ABS(55-SP),12.5)/5).)

Speeding offences

You are given a speed allowance using the formula SL-RH+10.

If you exceed the speed allowance, you'll be checked against this formula: RND <= (SP-SL+2*RH-5)^2/900!, which is equivalent to RND <= ABS(SP-SL+2*RH-5)/30!.

(SP = Speed, SL = Speed Limit, RH = Road Condition.)

Route RT RH SP Speed ticket formula Min %age
Northern 1 4 61 RND <= ABS(SP-52)/30! 33.3%
Middle 0 2 63 RND <= ABS(SP-56)/30! 26.7%
Southern 2 1 64 RND <= ABS(SP-58)/30! 23.3%

The chance of being caught is pretty high.

Speeding fine

You are fined RT*5+NT*RND*20 dollars and NT*RND*5 dollars for every mile over the speed limit.

You are also delayed NT hours for your NTth offence.

It's game over on your 4th offence.

Truck stops

There are truck stops every fourth stop. If you give it a miss, you'll see a truck stop on every third stop.

You can refuel, buy tires and rest at a truck stop. A stop is always an hour, whether you do anything or not. (And it is not counted towards your rest time.)

Refueling

Fuel costs 85 cents + a randomized 35 cents/gallon (min $0.85, max $1.20).

Keep in mind that your truck can only hold 200 gallons. Any extra is spilled! The display deliberately fudges a random -4 to +5 gallons.

Buy tires

If you don't have any spare tires, you will be prompted to buy one. A new one costs $200 + randomized $50, retread $100 + randomized $70.

Resting

If you rest in the day time (6 am to 5 pm), you only rest an equivalent of half the time + 0.6 hours due to day-time noise.

If you rest four or more hours, your tiredness factor (HL) is reset, otherwise it is halved.

Note that oranges still consume 7 gallons of fuel per hour when you are idling. Because of that, the game will prompt you to buy fuel again. (Answering yes here will cost you an hour. I don't know if it is by design or by accident.)

On the road

Road condition

There are 5 possible road conditions (CR): 1 (clear), 3 (wet), 5 (rain/light snow), 10 (fog), 50 (blizzard).

Road condition is based on this formula: (3000+MF)*RND. Each route has its own mapping table, but generally, lower is better.

Driver condition

There are 6 driver conditions (CD): 1 (rested), 2 (fine), 4 (bored), 8 (tired), 25 (fatigued), 100 (exhausted).

Rules:

  • If HL > 19 (hours) or your ratio of rest-to-total-hours <= 25%, then you are exhausted.
  • If HL < 4 and your ratio of rest-to-total-hours > 43%, then you are rested.
  • If HL < 8 and your ratio of rest-to-total-hours > 40%, then you are fine.
  • If HL < 12 and your ratio of rest-to-total-hours >= 33%, then you are bored.
  • If HL < 16 and your ratio of rest-to-total-hours >= 33%, then you are tired.
  • Else, you are fatigued.

You start the game with 7 hours of rest and HL = 3.

Accidents

Accidents are determined by the formula RND < SP^2*CD*CR/1E+07.

(SP = Speed. CD = Driver's Condition. CR = Road Condition. 1E+07 = 10 million.)

Some common combinations:

SP CD CR AF
55 1 1 0.031% Best case at 55 MPH
55 8 3 0.73% When tired
55 25 10 7.56% Bad conditions
55 100 50 151.3% Worst case at 55 MPH
45 25 5 2.53%

Drive slowly when you are tired or when the visibility is low.

An accident is a one-time occurrence. The game shows different accidents based on CD, CR and SP, but they are all the same: your trip has come to an end.

Others

Out of fuel

If you run out of fuel, you'll be charged $200 for 55 gallons ($3.64/gallon). You'll waste up to 5 hours too.

Needless to say, don't run out of fuel.

Flat tire

The road condition determines how likely you are to get a flat tire. The formula is RND < SQR(MF+100)*TC/(RH*25000!).

(MF = Miles Finished. TC = Tire Condition. RH = Road Condition.)

With the default tire condition:

Route RH 500 1000 2000 2500 3000
Northern 4 0.24% 0.33% 0.46% 0.51%
Middle 2 0.49% 0.66% 0.92% 1.02%
Southern 1 0.98% 1.33% 1.83% 2.04% 2.23%
S (with one retread) 1 0.69% 0.93% 1.28% 1.43% 1.56%

If you blow a tire, your TC is lowered by 2*TS and you are penalized one hour plus a randomized 2 hours.

If you don't have a spare tire, you'll spend $400 for the tow-truck and lose 4 hours.

(Minor bug: oranges do not consume fuel here.)

To sum up

Trucker sure packs a lot of stuff for a 394 line game!

A real McDonalds value meal

finance

McDonalds calls their standard-sized meal a value-meal. A burger, a pack of fries and a drink at $6.75? That's not much value for me. In comparison, you can get a fish-n-chip meal (no drinks) at Botak Jones for $7. Now, that's value.

I had a craving for fries today, but I didn't want to get a value-meal, especially when it is not filling enough for a dinner. So, I got two $2 burgers and a large fries instead. The total is $6.55, 20 cents cheaper than the meal — and is more filling too.

What about the drink? I bought a 1.5L bottle of Coke-light a few days ago for $1.70. That just gets you a medium-sized drink at McDonalds.

(Interestingly, Coke-zero is 10 cents more expensive than Coke-light, but they taste exactly the same.)

I remember there was a time McDonalds offered the Big Mac burger for $2. That was during the time of the Mad-Cow diesase, iirc. I often bought the burger and the fries for $4, skipping the drink.

A value-meal is what the consumer makes it so, not what McDonalds says what it is.

Are your papers in order?

News: School peddles fake RMIT degree

Date: 19 June 2009. Source: Asiaone.

GET a degree from the Royal Melbourne Institute of Technology (RMIT) in just one year, for as little as $12,000.

Better yet, spare yourself the trip to Australia. Just head to Beach Road for evening classes once or twice a week, and hand up minimal assignments. No examinations required.

Sounds too good to be true?

The real deal from SIM: $30,000 for a three-year course.

The same degree at the Melbourne campus costs at least $100,000 in fees and living expenses over three years.

Polytechnic diploma holders are usually given a year's exemption, but still have to devote two years to full-time study to obtain a degree.

Education is like selling hope. Parents are willing to spend for their children's education until they graduate. People who did not do well in their studies often do part-time studies to improve themselves after they started working. This is all very good, except the hopeful is easy prey for conmen.

Finally, the rating agencies

finance

News: US pension fund sues rating agencies over $1bn losses

Date: 16 July 2009. Source: The Independent.uk.

Toxic products were given AAA rating, says Calpers

Calpers, the California state employees' pension fund and one of the most powerful fund managers in the US, is suing the three main credit rating agencies, saying they were negligent when they gave gold-plated ratings to mortgage derivatives that have since turned toxic.

The lawsuit adds to the growing pressure on the agencies – Standard & Poor's, Moody's and Fitch – over their role in inflating the credit bubble that turned spectacularly to bust.

About time!

Why we should fix bugs asap

programming

So that we can find other bugs!

A colleague of mine finally fixed some long standing bugs in his code. So the code works now? Nope, it immediately revealed two more bugs, one in my code and one in his.

Programmers will know this is not a rare situation: a trivial bug can mask other more serious bugs.

This brings up a more serious issue. When doing web development, some people just target the two big browsers: IE and FireFox. In the bad old days, many developers just use IE, that's why there are so many sloppy sites. Thankfully, those days are over, to be replaced by an almost-as-bad practice: they use just FireFox! Luckily, it is possible to follow the standards and "hack" IE separately — most of the time.

While FireFox is much closer to the standards, it is not perfect. It is still necessary to check in other browsers: Chrome, Opera and Safari.

The Web App that I'm working on has a long standing bug that causes it not to load at all in Opera and works only partially in Chrome and Safari. To say it is a long standing bug is an understatement — it has never worked since day one.

Is it time to fix it? There could be other bugs hiding behind it.

Dissecting GW-BASIC Trucker, part 1

You are a truck driver prying the Los Angeles to New York route. Your objective is to maximize your profit, of course. You may choose between transporting US mail, freight forwarding and oranges.

The problem with the game is that it doesn't come with any instructions. You certainly don't need any to play, but you won't be able to play optimally if you don't know the rules of the game.

This post will expose the game rules.

Cargo

US mail: the most straightforward. You are paid 4.75 cents/pound; no time pressure. Unfortunately, that means you can only earn $2,375 before expenses. ($1,900 if you stick to legal weight.)

Frieght forwarding: you are paid 5 cents/pound, but you must deliver the cargo within 95 hours, or you will slapped with a 10% penalty. You can earn at most $2,500 ($2,000 with legal weight).

Oranges: you are paid 6.5 cents/pound (the code uses 6.500001), so you can earn $3,250 ($2,600 with legal weight). The catch?

Oranges are fragile

Oranges require refrigeration. You'll consume fuel (at 7 gallons/hour) even when you are stopped.

This implies that if you run out of fuel, your oranges will suffer damage. Indeed, they do suffer a random 10% damage (at 5% granularity).

Even worse, your refrigeration unit may fail along the route. When that happens, your oranges suffer a random 0 to 20% damage (at 5% granularity).

If you take more than 4 days to deliver the oranges, they will suffer a random 10% damage (at 5% granularity) multiplied by the additional days.

Finally, the final damage must be 30% or less, or the whole batch has to be discarded and you have to pay $50 to dump them!

Reaching New York

If you made it to New York from 6 pm to 5 am, you have to wait until 8 am for the warehouse to open.

Fixed cost

You have a fixed cost of $85 + $85/day. For example, your cost is $340 if you complete the journey in less than 4 days.

Profit expectations

You are expected to make a profit of at least $200/trip and an average of at least $250 over time. As a consolation, the game says you do a good job if you earn over $100. (I think it is wrong, it should be $1,000.)

Routes

You have three routes to choose from:

  • northern (2,710 miles over 18 zones)
  • middle (2,850 miles over 21 zones)
  • southern (3,120 miles over 25 zones)

What's the difference? Road condition and speed allowance Personally, I'll just pick the shortest route. There is no real advantage in picking a longer route.

Entering a new zone

When you enter a new zone, you are subjected to one of the following actions:

0 NOP
1 Time zone changes, add 1 hour
2.x Toll; 100*RND*x dollars
3.x RND>=x Road works; speed limit 35 MPH for an hour
4.x RND>=x Speed radar; Speed+/-2; speed allowance +2 MPH
5.0 RND>=.5 Detour 200 miles; speed limit 45 MPH until the next zone
5.x RND>=x Weighing station; $200 + 2-5cents/excess pound
6.x RND>=x Rock slide; randomized 6 hours delay
7.x RND>=x Refrigeration failed (orange only); $100 + 2 hours

The actions are hardcoded.

Weight

You can haul between 25,000 to 50,000 pounds; 40,000 being the legal limit. How much should you haul?

There is only one reason to haul less: weighing stations along the route restricts your total weight to 60,000 pounds. More cargo does not consume more fuel.

Your truck weighs 19,000 pounds, fuel is 7 * gallons and a random 250 pounds (granularity of 10 pounds) is added, so you can carry 40,000 pounds if you are willing to drive with fuel at 25% capacity. If not, then just haul 39,350 pounds — you will always be under the limit.

The fine is $200 + randomized 2-5 cents/excess pound. If you are carrying 50,000 pounds of goods, you will be fined at least $380. Ouch, there goes your profit!

Tires

Your initial tire condition (TC) is 10. If you get new tires ($200 each), your TC is lowered by 4*T. If you get retreads ($100 each), your TC is lowered by 3*T. (T = number of tires.)

You can buy up to three tires (of the same type). The third one is treated as a better spare tire (TS = 2) — you start the game with one spare tire (TS = 1).

There is no need to buy any at the start. Wait until you use your spare tire before you buy one — you rarely get flat tires.

Press S-T-A-R-T

Now you can start your journey!

New parking charges!

transport

It now cost $0.02/min to park in my office carpark. That's $1.20/hour, a huge increase from the previous $1.02/hour. Now 10 hours of parking will cost me $12 instead of $10.20.

And now it seems there is no more grace period at the 5 pm transition point. I was charged $13.08 when I exited at 5:08 pm instead of the expected $11.

Why users won't upgrade from IE 6

computer

The real reason why users don't need to upgrade: because Web developers always cater to them! The users don't see what's wrong, so they don't upgrade.

It is time to put in small and minor inconveniences, and they need to upgrade to get the "full functionality". An example could be the Date Picker. In IE 6, they have to enter the dates manually. In other browsers, they can choose the date from a calendar.

In truth, IE 6 is actually pretty feature complete even today — especially when augmented with Javascript.

It's that time of the year again

NDP 2009 fireworks rehearsal

Fireworks, fireworks, fireworks! Novel the first time, now it is just burning cash. Still a cheap way to entertain the masses, I suppose.

GW-BASIC Trucker game

I hunted down the only GW-BASIC game that I could remember:

GW-BASIC Trucker game

The actual title screen is in B&W, but the one I played was in magenta, so I "restored" it.

I also took some time to go through the source code. It was beyond my level last time. Now, it looks so simple — it is just 394 lines (about 6 pages). It is reasonably well-written, with well-defined variables and subroutines. Spaghetti code, this is not.

Bugs

Quite surprisingly, the game has a number of bugs.

Overflow

1440 IF AF>RH*25000*RND THEN GOSUB 2600

should be

1440 IF AF>RH*25000!*RND THEN GOSUB 2600

This is a run-time error detected by QBasic. The reason for the overflow error is because the game declares variables C to S to be integers, and the range of integers is +/-32k.

'!' makes the number into a float, so the expression will be promoted automatically. Other parts of the code do this, so obviously the original author must have missed this.

Continuous truck stops

Your next truck stop will be four stops away. If you choose to skip it, you will be prompted at every stop due to a bug:

1720 IF IKEY$="n" OR IKEY$="N" THEN S=1:HL=HL+1:RETURN

You should be prompted every three stops:

1720 IF IKEY$="n" OR IKEY$="N" THEN NS=1:HL=HL+1:RETURN

Incomplete?

At a truck stop, if you don't have a spare tire, the game will ask you if you want to buy one. If you answered yes, the game will execute this:

1850 STOP

This is rare because you start the game with one spare tire. I changed it to,

1850 PRINT:INPUT "     Which type do you want";Z$:Z$=LEFT$(Z$,1)
1860 IF Z$="r" OR Z$="R" THEN XC=XC+T1:TS=1:GOTO 1900
1870 IF Z$="n" OR Z$="N" THEN XC=XC+T:TS=2:GOTO 1900
1880 PRINT"I did not understand your answers.":PRINT"Let's try again!":GOTO 1850

Not a bug: day-time check

You are penalized for resting in the day. The code checks for before 1200 and after 2100:

1970 IF DH>21 OR DH<12 THEN T=INT(T/2+.6):PRINT"Thanks to the daytime noise, you got only"T"hours real sleep."

The wall clock is 8 hours ahead, so the code is checking for before 2000 or after 2900 (0500). So it is correct.

Note that the check is done after you stopped for an hour, so the day-time window is effectively 0400 to 1900.

Free topup!

If you run out of fuel, you are charged $200 extra for the top-up... except that you are not:

2550 WF=55:T1=INT(RND*5):HR=HR+T1:ZC=ZC+200:HL=HL+T1

It should read,

2550 WF=55:T1=INT(RND*5):HR=HR+T1:XC=XC+200:HL=HL+T1

You still waste up to 4 hours, though.

Blown tire

Every time you blew a tire, you get exhausted immediately.

2660 PRINT "     It took"; T; "hours to change the "; T$; " tire.": HR=HR+T:HL=HR+T+1

HL is not updated correctly.

2660 PRINT "     It took"; T; "hours to change the "; T$; " tire.": HR=HR+T:HL=HL+T

Syntax error

This line has two errors: using TIMEPUT instead of TIMEOUT, and lack of whitespace.

2740 TIMEPUT=2:GOSUB59950:RETURN

should be

2740 TIMEOUT=2:GOSUB 59950:RETURN

This is a problem with interpreters. Javascript has the same problem.

Driver condition

The code uses COS() to determine the driver's condition. COS() never returns a value greater than one, so it is very easy to be fully rested.

3020 IF HL<4 AND COS(HR/HS)<2.3 THEN CD=1:CD$="rested & rearing to go.":RETURN
3030 IF HL<8 AND COS(HR/HS)<2.5 THEN CD=2:CD$="fine":RETURN

(HL = tiredness factor in hours, HR = total hours, HS = hours rested)

It should not be so easy. After correction:

3020 IF HL<4 AND HR/HS<2.3 THEN CD=1:CD$="rested & rearing to go.":RETURN
3030 IF HL<8 AND HR/HS<2.5 THEN CD=2:CD$="fine":RETURN

Now you must maintain a good total-to-rest-hours ratio too.

Attempt to run another program

When the program ends, it tries to run another program:

4160 IF IKEY$="n" OR IKEY$="N" THEN RUN "b:???0??"

This will fail, of course. No one has a B: for years. I changed all the RUN to STOP:

4160 IF IKEY$="n" OR IKEY$="N" THEN STOP

Incorrect warehouse time check

If you reach New York at night, you have to wait until next morning. However, the condition is never met because the code checks for the number of days:

5110 T=HR-INT(HR/24):IF T<10 OR T>21 THEN 5140

The formula should be T=HR-INT(HR/24)*24. We can use MOD to extract the hours instead:

5110 T=HR MOD 24:IF T<10 OR T>21 THEN 5140

Note that the code takes before 1000 or after 2100 to be day-time. The "real" time (after conversion) is before 1800 and after 0500. It is correct.

Late delivery

You are supposed to be penalized when you deliver the freight forwarding cargo late.

5340 CX=2:PRINT"     You're late!!  Subtract ten percent penalty.":GOTO 5400

CX is assigned a value, but the code doesn't actually subtract it from XT (profit). Hence, there is no penalty at all! We fix this by adding this line:

5335 XT=XT*.9

No END

Under some circumstances, the game will execute the subroutines when it should have stopped instead. I added this line to end the program:

5540 END

Incorrect delay code

The game will hang from time to time. I believe it is due to the timer delay code. It multiplies the hour by just 120. It should be 3600!.

59950 TIMEOUT$=TIME$:TIME2=VAL(LEFT$(TIMEOUT$,2))*120+VAL(MID$(TIMEOUT$,4,2))*60+VAL(RIGHT$(TIMEOUT$,2))
59960 TIMEOUT$=TIME$:TIME3=VAL(LEFT$(TIMEOUT$,2))*120+VAL(MID$(TIMEOUT$,4,2))*60+VAL(RIGHT$(TIMEOUT$,2))
59970 IF TIMEOUT > TIME3 - TIME2 THEN 59960 ELSE RETURN

We can use TIMER instead:

59950 TIME2=TIMER
59960 TIME3=TIMER:IF TIME3 < TIME2 THEN TIME3=TIME3+86400!
59970 IF TIMEOUT > TIME3 - TIME2 THEN 59960 ELSE RETURN

We gain sub-second delay as well.

The gameplay

I will cover this in another post.

My "I-know-programming" moment

programming

In my search for JS/CSS minification techniques, I stumbled on the blog Moserware. His first entry has some background information on himself. We have a pretty similar start: GW-BASIC, QB4.5 and Delphi!

He also thought that he had learnt everything there is to programming after programming for a short time.

I think that's a pretty common experience. I also had my "I-know-programming" moment, way back when I was using GW-BASIC. I read the Reference Manual cover-to-cover, absorbing every single function. GW-BASIC has lots of functions. They cover everything I could think of, so naturally I thought I could program anything (except TSR), just that it would be slower and has 64 kB memory limitation. Ha!

A GW-BASIC story

Non-programmers count from 1. Thus, it is usual to see for loops that count from 1 to N, rather than 0 to N - 1. I was taught this way for GW-BASIC too.

Declaring an array:

10 DIM MYLIST(10)

Writing a for loop:

100 FOR I = 1 TO 10
110 MYLIST(I) = 0
120 NEXT

This is wrong because, surprisingly for a beginner's language, array indices start from zero!

Those of us who knew wrote it this way:

5   OPTION BASE 1
10  DIM MYLIST(10)
100 FOR I = 1 TO 10
110 MYLIST(I) = 0
120 NEXT

Or this way (to be more like C!):

10  DIM MYLIST(9)
100 FOR I = 0 TO 9
110 MYLIST(I) = 0
120 NEXT

We were penalized by the teacher because we were not taught it!

The correct code

5   OPTION BASE 1
10  DIM MYLIST(10)
100 FOR I% = 1 TO 10
110 MYLIST(I%) = 0
120 NEXT I%

First, GW-BASIC automatically assumes arrays have 10 elements, but it is still good to give them a size.

Second, we are using floating point numbers! Use '%' to force an integer. (In retrospect, using float as the default numeric type is correct — it is less likely to trip beginners.)

Third, it is recommended to put the loop counter in the NEXT statement even though it is optional.

GW-BASIC today

I wanted to test the above code to make sure I got everything right. GW-BASIC doesn't run in the Vista shell, but no worries, DOSBox comes to the rescue:

GW-BASIC 3.23

It took me a while to remember how to quit: by using the SYSTEM keyword.

Minifying

programming

It is possible to achieve dynamic minification with some server-side processing. This gives the best of both worlds: we can keep the logical source code structure and still deploy optimally.

Minification is usually done together with combining the files. I need that more than minification: I have 98 kB of JS code in 7 files (33 kB zipped) and 19 kB of CSS in 6 files (5.5 kB zipped). I should just need one JS file and one CSS file.

A quick search shows up a one-stop solution: Minify!.

Minification is not as straightforward as I thought. Even the simple process of removing whitespaces and comments can break things.

JS files can be minified and combined without special concerns. Sure, the lack of ';' can trip you up, but that's due to bad code.

Surprisingly, CSS files are not safe, due to IE hacks that require whitespace and comments to be present! Minify! will preserve the known hacks. Personally, I will not use such hacks because we can do conditional include for IE — which is guaranteed to work.

Also, some CSS files may not be combined properly, if they use the import keyword. The reason is that import must come before other rules! That's a strange restriction.

I am even more surprised that HTML cannot be minified directly. I forgot that <pre> and <textarea> preserve whitespaces. And IE conditional comments must not be stripped out!

As I don't need the full-fledged solution, I'll probably just build a simple wrapper around jsmin to minify and combine the JS files.

For me, it is more essential to strip out comments from JS files and combine JS/CSS files to a bare minimum. I don't really need to minify CSS and HTML files as I don't write much comments in them. Whitespaces are already compressed very well with gzip.

Update: my JS and CSS files are such a tangled mess that I can't combine them easily. I'll postpone it until I'm able to do a proper design.

A surprise call

finance

I got a call from my mobile phone service provider that they would rebate $4 from my bill if I were to recontract with them for 2 years. $4 is very significant considering that my bill is just around $17.

Is it realistic?

So I'm watching a Korean drama. The guy treated the girl to a high-class restaurant for their first dinner date. The bill ended up at 426,000 Won, including 126,000 Won for a bottle of red wine. 426k Won is S$489, according to Yahoo Finance. Is this realistic for a dinner?

You know a place is expensive when there are no prices in the menu...

The lack of scripting

computer

Vista finally shows a little more info when copying files. This gives the same info as XP, just looks nicer:

Vista Copy Progress Bar

Well, Vista gives one more info: the speed.

Vista Copy Progress Bar (expanded)

What you know what I want? The ability to pause and resume a lengthy file operation; the ability to see the files being copied and change them on the fly; the ability to set the transfer rate — sometimes you want to leave some bandwidth for other apps.

I don't expect to have all these functionality out-of-box, but I should be able to add what I want — through the ability to script.

California issues IOU

finance

Of course, they call it registered warrants. Now, it can be very dangerous to accept one in lieu of real cash. You see, if you cash them in a bank and California reneges on them in October (the IOU's due date), you are still liable for it — as if it bounced; it is a recourse IOU.

As a sign of confidence, some banks have already indicated they will not accept these IOUs. Also, payday loan companies are willing to accept them at a discount of 40% from the face value.

People have already found a good use for the IOU: pay your taxes. California has a budget deficit due to tax collapse, and now you pay your taxes in these funny dollars? Hmm... talk about unintended consequences.

Saved by undo!

computer

I wanted to use a file as a template, so I opened it and started editing. I then saved it. That was when I thought, have I made a copy of the file?

I haven't! And no backup or other copies!

When all appeared lost, I decided to give undo a try. It worked! I was able to undo everything!

Undo: never leave home without it.

A good start

computer

I have very high expectations after I read that the Google Chrome OS "will allow users to access the Web in a few seconds". That sounds like ultra-fast startup time. That's a good start!

An OS should allow almost instant on and off — without losing data. In other words, we will always resume where we left off. (Shutting down is such a quaint concept; I haven't shutdown under normal circumstances in years.)

I wrote almost because it is currently attainable. Instant is not — yet. Almost-instant is 2 seconds at most.

When you think from the end-user's POV, the technical specs are not really important. CPU, RAM, HD size? All irrelevant, as long as the computer can run the apps snappily.

Google OS

computer

News: Google takes aim at Microsoft with new PC platform

Date: 8 July 2009. Source: Reuters.

Google Inc is planning a direct attack on Microsoft Corp's core business by taking on the software giant's globally dominant Windows operating system for personal computers.

Google, which already offers a suite of e-mail, Web and other software products that compete with Microsoft, said on Tuesday it would launch a new operating system that will initially be targeted at netbooks.

Called the Google Chrome Operating System, the new software will be in netbooks for consumers in the second half of 2010, Google said in a blog post, adding that it was working with multiple manufacturers.

You can see this coming when Google came out with its browser. First, web apps, then office apps, then a browser and now an OS.

In this aspect, I say Google is smarter than the other search engines. They stuck to the web, and got left behind when better contenders showed up. Google knew they would suffer the same fate in time, that's why they have to control the browser and the OS too. For example, browsers now have an integrated search. No one goes to www.google.com directly anymore. Will the default remains Google in the future?

The browser is the interface to the web. He who controls the browser controls the web. But browsers don't exist alone magically. They exist in an OS. Just look at Microsoft. Why is IE the dominant browser, despite being at least 3 years out-of-date?

On Windows

Windows has grown too big and bulky over the years. Vista requires 15 GB to install. This is a result of Windows organic growth. New stuff is put in, but old stuff is almost never removed — the new stuff is built on top of the old stuff.

Yet, Windows has a very small core. People have produced "lite" versions of Windows since Windows 3.1. A super-lite version could fit into 2 to 3 1.44 MB floppy disks, iirc.

If you know how to use the Win32 GDI API, you can write snappy self-contained applications ranging from 20 kB to 100 kB — sizes not seen since DOS days. The problem is that no one knows how to (or bothers to) do it anymore, except for virus writers. We are too dependent on frameworks. The .NET 3.5 Run-Time is almost 200 MB, not counting the Service Packs. (But I read this is just the full installer. Installed size is around 50 MB.)

Sunset

Windows is getting more and more irrelevant these days. I tinkered with it a lot in the past, but now I spend most of my time in just four areas: browsing, office apps (Outlook, Excel and PowerPoint), Explorer and watching videos. (Yes, Explorer made it into the list.)

I don't use Word, I prefer to use HTML. PowerPoint is also in danger of being replaced by pure HTML, at least for on-screen presentations.

As long as a computer can do these four things, I'm ready to switch.

How does your browser sort?

programming

I was intrigued when I read about sort()'s inefficiency in IE at Web Reflection. I decided to investigate it myself.

Find out here.

Update (Sep 2009): updated link.

Fast compile times

programming

Compilation is slow. Yet, I think it is made much slower by our flawed compilation process.

Think about it, why should the compiler go through the whole file? Why not just the changed function? Why do we need to link? Why isn't it linked dynamically?

I've used a few fast compilers in the past: Turbo Pascal, QuickBASIC and Delphi.

To say Turbo Pascal is fast is an understatement. It is instantaneous. For a long time, I was biased against it because Pascal is, well, a "student" programming language. Real programmers use C and x86 asm. The Run-Time is also small and fast — Pascal code runs circle around my "faster" C code.

16-bit C compilers are a joke, due to x86's 16-bit data path, lack of general-purpose registers and "complex" memory addressing that the compilers don't know how to make good use of. In those days, it was effortless to get 50% speed boast by simply writing it in assembly. On Pentium-4? Even Michael Abrash, a good — if not the best — x86 assembly programmer doesn't try it.

Delphi is the successor to Pascal, so there is no need to doubt its speed. By this time, I've come around to like Pascal (it is a very elegant language) and scoff at C. I wrote my final year project in Delphi, but I also recognized it would probably be the last time I use it. The real world is C-based: C, C++, Java, Javascript and Perl.

QuickBASIC is not a real compiler, but it converts the whole program to pseudo-code before running it, so you can say it compiles the program. Unlike Turbo Pascal, it is merely a fast compiler. However, most of the time, it is able to run the program immediately after you press F5 (run) — zero compilation time.

How does it do it? It parses the source code as you load it and shows you only one function at a time. Thus, it just has to compile one function at most. But it does it one better: it compiles a line right after you leave it.

QuickBASIC is actually a pretty decent language. It still lives on as Visual Basic and VBA.

BTW, I ran Turbo Pascal and QuickBASIC on a 8 MHz 8088 with 640 kB — and they felt fast.

The takeaway

First, define a language that can be parsed easily, so that it is almost as fast as reading the file. IIRC, Pascal has a LL(1) grammar. Such languages can be parsed very quickly with very simple parsers.

Turbo Pascal goes through each file only once, even if it may be included by many files. (In Turbo Pascal, a file contains both its interface and implementation.) Visual C++ does it with precompiled headers, but it is still slow.

Second, break up the file into finer granularity. A function is a natural unit. It should be possible to compile only functions that change. This may require integration with the editor — the editor knows which lines have changed; by inference, which functions. It can compile as soon as you leave the function.

How not to optimize

programming

My blog does some JS processing when it loads. I decided to tweak it a little to be more efficient:

Browser Before After
Chrome 2.0 41 ms 55 ms
FireFox 3.5 86 ms 112 ms
IE 7 559 ms 578 ms
Opera 9.6 133 ms 180 ms

(Average of 10 runs.)

It's... slower? What have I done?

Change 1: reusing the same list

Previously, I get the list of articles 3 times. I now get it just once, so it should be faster, right? I would think so too.

Change 2: using each() instead of for loop

The code used to look like this:

function process() {
  var list = $(ARTICLES);
  for(var i = 0; i < list.length; ++i) {
    ...
    }
  }

It now looks like this:

function process($list) {
  $list.each(function() { ... });
  }

for loop is faster.

What now?

I will keep the new changes, despite being slower. Using each() is more jQuery-y. And ultimately, speed doesn't really matter in this case.

On optimization

There are two well-known rules of optimization:

  • Don't do it.
  • (For experts only!): Don't do it yet.

(Quoted from Michael A. Jackson.)

Okay, that's a joke (if only). The real rules:

  • Don't optimize as you go
  • Remember the 80/20 rule
  • Always run "before" and "after" benchmarks
  • Use the right algorithms and data structures

Handling etags better

programming

I made a simple change to the Auto Thumbnail Generator: I will only compute the etag if the last-modified (date/time) does not match.

This will shave off perhaps 10 ms per image — more than what you would expect — because we can skip reading the file entirely if last-modified matches.

(Still, I didn't make this change to save time. I did it to reduce coupling and to be able to turn off one or the other in the future.)

Previously:

  1. Read file contents
  2. Check last-modified and etag together; return 304 if either matched
  3. Send file contents

Now:

  1. Check last modified; return 304 if matched
  2. Read file contents
  3. Check etag; return 304 if matched
  4. Send file contents

Note that this still isn't optimal. We shouldn't need to compute etag at all, if we have a persistent hashtable to store it.

If I wrote the code from scratch, I might have skipped etag altogether. Last-modified and etag both do the same thing. It's just that last-modified has a specific format and has only seconds granularity. Etag is usually a hash value, but it can be anything. In fact, some people put the Unix time there, because it can be used directly (in a program).

Etag gotcha

This is a good time mention an etag gotcha. Etags can be modified by the webserver or its extensions, usually by appending some text to it. Thus, you still need to parse it.

Since that's the case, you might as well use last-modified. No one ever touches it. Don't let the fact that it looks like a date deter you. All you need is a string comparison — there is no need to convert it to a numeric time first. It is either equal or not equal to the file's current timestamp. And I've mentioned why it is safe to do so: no one ever touches it.

Simplify it!

programming

I used to use the WiFi device's own utility to connect to my wireless network. You can control all sorts of options there, including the connection speed and even the device's power usage. But, the utility refused to run in non-admin mode. No choice but to use Windows XP's built-in wireless manager.

I was shown only two screens. First, I was asked to choose from a list of networks, including ad-hoc ones. Then, I was prompted for the security key. That's it? Will it work?

To my surprise, it worked flawlessly. I was converted. Since then, I have always used Windows's built-in wireless manager. As a user, I shouldn't need to know the details: type (a/b/g/n), security (none, WEP, WPA, WPA2), encryption (none, TKIP, AES) and the security key format (text/hex, 64/128/256 bits). The utility should auto-detect, or try starting from the strongest to the weakest.

So, if someone asks me if it is difficult to set up a wireless connection, I look at him strangely. It's just selecting the right network and entering the password. What's so difficult about that?

Optimizing my homepage

programming

I want to cut down on my homepage's initial download size, but the solution must fulfill these conditions:

  • I want everything to be accessible even if Javascript is not present
  • I want to load only the active tab if JS is enabled
  • The solution must be based on the same HTML source (no duplication)

I was stuck because I couldn't find a pure JS solution that works when JS is absent. Such a solution means,

  1. the HTML file will contain everything (so that everything is shown when JS is absent)
  2. JS will have to stop loading the HTML somehow and later fetch on-demand

Fetching on-demand is not a problem with AJAX, but I couldn't find a reliable way to stop loading the HTML file.

The obvious thing to do is to split the tabs into physically separate pages, but this will duplicate the headers and footers. In the end, I decided to use PHP to dynamically insert the contents. It turned out that this simplified a lot of things. I was able to remove the Javscript tabs and special browser history handling!

The results:

Size Files
Initial 725 kB 44
Gzip JS/CSS 644 kB 44
One tab only 222 kB 26
Delay slideshow 89 kB 17

At the same time, I also delay loading the slideshow images. Previously I just put all the images in the HTML (they are stacked on top of one another). Now I define only the first as an image, the rest as strings. The slideshow will convert them into images as needed.

The eventual total size is 223 kB, 1 kB more than if I load everything in one go. It is the tradeoff for being much snappier.

The new user experience is not 100% identical to the old one, but it is close enough.

Javascript dependency; it's wrong, all wrong!

programming

Currently, there is no way to indicate dependencies for Javascript files. The client must include all the necessary files in the correct order. Even if the loading is done by a loader, it must hardcode the dependencies within it.

No, the dependencies should be specified in the Javascript file itself, just like #include in C and import in Java.

The problem is, the script executes as soon as it is loaded. Bad things happen if its dependent files have not been loaded.

What should we do? Simple. Load it as a text file, parse it to find its dependencies and load them (recursively), then we process the file. Initially, I thought it was necessary to fetch the file again as a script, but it turned out I could just use the all-powerful eval().

The waiting game

transport

As I drove past several bus stops at 6:25 am one morning, it struck me that many people were already on their way to work.

Due to the heavy reliance of public transport, it is usual to spend a lot of time waiting for the buses or MRT to arrive.

No wonder the stress level is so high and that many people aspire to own a car, despite the cost. A car do cut down on the travelling time, provided you avoid the peak hours and downtown area.

Official notice of paycut

finance

News: Paycut if business worsens

Date: 30 June 2009. Source: ST.

TWO in five private companies said that they would need to cut wages this year if business conditions worsened, when the Singapore economy contracted sharply by 4.2 per cent in the December quarter.

These firms employ about 44 per cent of the private workforce, according to the Report on Wages in Singapore 2008 and Singapore Yearbook of Manpower Statistics, 2009 released by the Ministry of Manpower on Tuesday.

Consider this the official notice. Don't be surprised six months later...

Why recession leads to depression

News: Marital woes up

Date: 30 June 2009. Source: ST.

THE recession is taking its toll on marriages as worries about finances and job security spiral into disagreements and fights.

Counsellors my paper spoke to say that they have seen an increase of up to three times the typical number of couples with such marital woes.

It is not surprising at all. It takes a while to accept the situation and even longer to adapt to a new lifestyle.

$4 for 17 mins!

transport

I reached office at around 6:45 am. I left at 5:12 pm (time at the exit gantry). I was charged $15.10! ($2.04 before 7 am, $1.02/hr before 5 pm, $2.04 after 5 pm.) Yes, the figures don't tally. I'm going to find out why tomorrow.

I should have waited until 7 am before I enter the carpark, and leave before 5:10 pm. That 17 minutes cost me $4!

(Why 5:10 pm and not 5 pm? I observed that the carpark allows a grace period of 10 minutes after 5 pm before it charges you $2 flat rate. It's as if you exit and enter the carpark again.)

Parking on pavement

transport

News: Motorcycles on pavement are a menace

Date: 1 July 2009. Source: Today.

Letter from Raymund Koh

I see motorcyclists, mostly despatch riders, riding their two-wheelers on pavements in the Beach Road and Raffles Place areas. A double charge of riding and parking on pavements would surely send a strong signal that the authorities do not condone this. Can the Traffic Police put a stop to this menace?

Why is it a menace? Parking should be allowed on pavements. The solution is wide pavement, double the current width.

Expenses for June 2009

finance
Category Jan Feb Mar Apr May Jun
Basic 1,061.55 985.02 2,574.12 685.04 987.35 869.82
Cash 213.60 241.50 172.10 151.00 160.00 144.90
Vehicle 258.35 307.97 577.60 1,886.78 146.74 836.51
Others 116.30 875.00 308.15 391.94 66.00 28.99
Total 1,649.80 2,409.49 3,631.97 3,114.76 1,360.09 1,880.22

Vehicle expenses are high due to road tax ($557).