Friday, December 31, 2010

Five Books That I Enjoyed Reading in 2010

The followings were the five books that I enjoyed reading most in 2010:

  1. Dreams From My Father by Barack Obama

  2. The Black Swan: The Impact of the Highly Improbable by Nassim Nicholas Taleb

  3. The Girl with the Dragon Tattoo by Stieg Larsson

  4. 3:59.04:  The Global Quest to Break the 4 Minute Mile by John Bryant.   

  5. The Best Australian Essays 2010 edited by Robert Drewe


Special mention goes to Pride and Prejudice by Jane Austen. It happened to be the first book that I read in EPUB format on a mobile device.

Thursday, December 30, 2010

The Banyan Tree of Limbus

On Christmas Day, a couple of Limbu friends favored us with a visit to our place at Kogarah, and, over orange juice and canapes, we discussed the consanguinity taboos that govern Limbu marriages.  My friend's spouse pointed out that if the consanguinity taboos were observed strictly, a Limbu lad or lash would find it difficult to find a suitable match.

The Limbu marriage code forbids two types of marriages on consanguinity grounds. First, marriages between parties having same surnames are proscribed in most cases. I said "most cases". For example, my mother's Limbu surname is "Menyangbo", and, as far as I know, a Menyangbo cannot wed another Menyangbo.

My own Limbu surname is "Thebe". Thebes are further sub-divided into "Thupukko", "Sing" and "Maabo". A union between a Thupukko Thebe such as myself and a Sing Thebe is legitimate but not, as far as I know, between other Thebe sub-types. This inconsistency betrays the somewhat arbitrary, malleable nature of this particular taboo, and may suggest that Sing Thebes were once a distinct clan who were assimilated by the numerically superior Thebes at some point in history.

Local legend has this to say about the origins of Sing Thebes: The first Sing Thebe was a Thupukko Thebe who went foraging for firewood in the forest and got lost. So, he became Sing or "Firewood" Thebe. This legend is a warning to any future Sing Thebe genealogist tempted to find false glory by tracing their roots to the "Singhs" of North India. Bull dust such as  "Sing Thebes descended from Rajput kings of North India who fled the bountiful plains of Rajasthan after losing a bloody palace power struggle and hid in the forested eastern foothills of what is now Nepal. The defeated royal warriors took Thebe girls for wives and concubines and, in time, their descendants combined the hallowed surnames of their royal ancestors and tribal mothers to emerge as Sing Thebes" should be left to the fawning astrologers of the now defunct courts of pompous Shahs and Ranas. But I digress.

The second type of marriage forbidden by Limbu tradition is between parties separated by up to five (some say nine) degrees of consanguinity. The definition and degree of consanguinity as understood and practiced by the Limbus is different to those of other groups within Nepal such as Magars, Gurungs and Ranas and those of Europeans.

Let us imagine a Thebe lad named Kaanden who is scanning a mental list of prospective brides. Since he is a Thebe, all Thebe girls anywhere except Sing Thebes are classed as Chelis (sisters) and expected to be treated as such. Even flirting and minor indiscretions (surely an oxymoron) with these Thebe girls would be strictly forbidden and attract severe consequences. In olden times, this basically meant the collective loss of face of the offender's extended family, blood feuds with the girl's own extended family, and the banishment of the offender into "Munglan", the land of the Mughals, i.e. India and Sikkim.

Let us imagine that our would-be bridegroom's  mother is a Menyango. According to Limbu practice, this places the set of all Menyango girls at a distance of one degree of consanguinity. They are off limits to Kaanden and as sacred as his own mother.

Kaanden's paternal grandmother was a Maaden and maternal grandmother a Jabegu. So, any union with Maaden and Jabegu girls would be illicit as they are just two degrees removed, and, yes, Kanden needs to pay the same degree of respects to these girls as due to his own grandmothers. In fact, the prescribed way for Kanden to address these girls is as "grannies".

Kaanden's paternal grandfather's mother was a Lingden and maternal grandfather's mother a Chongbang. This means all Lingden and Chongbang girls are only three degrees removed and out of Kaanden's matrimonial calculus.

This hypothetical case illustrates the complexity of the web of clans that a Limbu has to negotiate to properly observe marriage taboos. Without getting lost in the forest of Kaanden's ancestral extended family, the number of clans, here loosely equated with major surname groups, excluded from Kaanden's marriage suit equals 32. Thirty-two! If nine degrees of consanguinity were to be observed, as is sometimes claimed, this number shoots up to 512. Are there even this many Limbu clans?

In practice, though, I suspect that even five degrees of consanguinity was always maintained on the female, the so-called 'leaf', sides of the family, branching off from mother, grandmother, great grandmother, etc. Though the Limbus have their own script, mass literacy is still a work in progress, and, in the absence of written records, folk memory can retain only so much information.

Late in the afternoon on the Christmas Day, we, along with our visiting friends, drove to a small park in Marrickville, an inner west suburb of Sydney, to mark the rite of passage of a Limbu friend's 10-month-old son. As many as 70-80 Limbus from various parts of the Limbuwan in eastern Nepal had flocked there. I suspect that if we all had taken the trouble to introduce ourselves and map our family trees, most of us would have found some common branches in our family trees, making most of us related by blood as per Limbu tradition.

Tuesday, December 21, 2010

The Mythical Man-Month: Project Management Insights

Frederick Brooks, Jr’s software project management classic, The Mythical Man-Month (The MM-M), continues to be read, debated and entrenched in undergraduate reading lists thirty-five years after its publication. This is surprising in a field where 20 percent of current knowledge is estimated to become obsolete every 12 to 18 months.

To put things in perspective, when the book was first published, computer memory rented at USD12.00 per kilobyte per month. At that rate, the 32GB SD card in my Samsung Galaxy S would have cost over 400 million dollars per month.

One of the reasons behind the enduring popularity of The MM-M is the author’s premise that “managing a software project is more like other management than most ... initially believe”. Brooks, who earned his stripes as the Project Manager for the IBM System/360 computer family and its software, advances “propositions” that would be applicable in almost any non-trivial project, repeatedly hammering the point that project management is about people and teams, not technology.

He buttresses his propositions with war stories drawn from his IBM experience and other contemporary projects. Little wonder, then, that The MM-M continues to attract a motley readership comprising doctors, psychologists, sociologists, lawyers as well as IT professionals.

What are Brooks’ project management insights that have withstood the test of time and continue to be relevant in the new century? Here are some randomly selected teasers from the book, in quotes when copied verbatim, with my own comments in square brackets:

  • The Biblical Tower of Babel project failed due to communication issues

  • “... the essential problem [in project management] is communication”

  • “The crucial task is to get the product defined. Many, many failures concern exactly those aspects that were never quite specified.”

  •  “... adding manpower to a late software project makes the project later”

  •  “... conceptual integrity is the most important consideration in system design” [or project]

  •  “Organizations which design systems are constrained to produce systems which are copies of the communication structures of these organizations”

  • “The [project] manager will be continually amazed that policies he [sic] took for common knowledge are totally unknown by some member of his team”

  • “The total cost of maintaining a widely used program is typically 40 percent or more of the cost of developing it. Surprisingly, this cost is strongly affected by the number of users. More users find more bugs.”

  • “The fundamental problem with program maintenance is that fixing a defect has a substantial (20-50 percent) chance of introducing another”

  • “Many poor systems come from an attempt to salvage a bad basic design and patch it with all kinds of cosmetic relief.”

  • “How does a project get to be a year late? ... One day at a time” 

  • “When one hears of disastrous schedule slippage in a project, he images that a series of major calamities must have befallen it. Usually, however, the disaster is due to termites, not tornadoes ... ” [As the saying goes, for want of a nail, the kingdom was lost]

  • “I believe the hard part of building software to be the specification, design and testing of this conceptual construct, not the labor of representing it and testing the fidelity of the representation.” [author’s emphasis. He means coding by ‘representing’. The author contends that fidelity to specification is pointless if the specification itself is flawed]

  • “The hardest single part of building a software system is deciding precisely what to build ... For the truth is, the clients do not know what they want ... So ... it is necessary to allow for extensive iteration between the client and the designer as part of the system definition.”

  • “In my experience most of the complexities which are encountered in systems work are symptoms of organizational malfunction. Trying to model this reality with equally complex programs is actually to conserve the mess instead of solving problems”

  • “Chemical engineers have learnt not to take a process from the lab bench to the factory in one step, but to build a pilot plant to give experience in scaling quantities up and operating in non-protective environments.” [author’s emphasis]

  • “The second [project] is the most dangerous system a person ever designs; the general tendency is to overdesign it ... using all the ideas and frills that were cautiously sidetracked on the first one.”

  • “My rule of thumb is 1/3 of the schedule for design, 1/6 for coding, 1/4 for component testing, and 1/4 for system testing” [This means testing should be assigned half the schedule time. Coding gets the least amount of time]

  •  “Schedule disaster, functional misfit, and system bugs all arise because the left hand doesn’t know what the right hand is doing. Teams drift apart in assumptions.”

Saturday, December 18, 2010

Deconstructing an SQL snippet

Recently, I came across the following snippet in a Teradata SQL script that came my way:

select calendar_date
from sys_calendar.calendar
where Calendar_Date Between
(((ADD_MONTHS(DATE,-1)) /100) *100 +1) (DATE)
AND (((((ADD_MONTHS(DATE,-0)) /100 ) *100 +1) (DATE)) -1) (DATE);

I knew that Teradata, just like Excel, internally saves dates as integers but did not remember the conversion scheme. A quick search later, I had the formula:

Integer Date = (YEAR – 1900) * 10000 + (MONTH * 100) + DAY

So, Teradata internally saves today’s date, i.e. 2010-12-18, as 1101218. Here, year = 110 (110 years since 1900), month = 12 and day = 18.

Now, it was just a matter of taking the expressions apart piece by piece, starting from the innermost expressions.

In the line “(((ADD_MONTHS(DATE,-1)) /100 ) *100 +1) (DATE)”, the function ADD_MONTHS(DATE, -1) is merely adding -1 month to today’s date. Since today is 2010-12-18, the function returns 2010-11-18. It’s internally saved as 1101118.

The result of ADD_MONTHS(), 1101118, is divided by 100. This is an ‘integer’ division and results in 11011. The division removes the day part. If one tries to cast this number to date, Teradata complains, predictably.

In the next step, 11011 is multiplied by 100 to restore the day part, resulting in 1101100. However, there is something seriously wrong with 1101100 as it has ‘00’ for day part, which is illegal. This is why 1 is added to the result of (ADD_MONTHS(DATE,-1)) /100 ) *100 to give the first day of the previous month. The final '(DATE)' merely casts the integer to date.

So, all that the expression “(((ADD_MONTHS(DATE,-1)) /100 ) *100 +1) (DATE)” is doing is calculating the first day of the previous month. Similarly, the expression “( ( (((ADD_MONTHS(DATE,-0)) /100 ) *100 +1) (DATE) ) -1) (DATE)” calculates the last day of the previous month. It first calculates the first day of the current month and subtracts 1 day from it, which results in the last day of the previous month. (The ADD_MONTHS() function is redundant in the second expression as it is basically doing nothing except confound users).

Coincidentally, I had previously written a more intuitive way of achieving the same result. It has the virtue of not using magic numbers:


SELECT CALENDAR_DATE
FROM SYS_CALENDAR.CALENDAR
WHERE CALENDAR_DATE BETWEEN
ADD_MONTHS(CURRENT_DATE, -1) - EXTRACT(DAY FROM CURRENT_DATE) + 1
AND CURRENT_DATE - EXTRACT(DAY FROM CURRENT_DATE);

Saturday, October 23, 2010

Running with a Metaphor

This year, I took part in three fun runs and may yet participate in one more before the year wraps up. My most recent dash was at the Brighton Beach Fun Run on October 17, in which I managed to reel in and out-kick the fastest pace-marking green dragon in the last 2/3 hundred meters. My official time for the 10k event was 40 minutes and 4 seconds.

In the Blackmore Half Marathon on Sept 19, I clocked 90 minutes and 3 seconds and, earlier on Aug 8, I completed the 14.1 k City2Surf in 62 minutes and 44 seconds. Not bad for a recreational runner who pounds the dirt only 3/4 times a week. Having said this, I would dearly love to improve my times by a couple of minutes next year.

Why run? More specifically, why run in fun runs? Personally, I find running in a vacuum mentally and physically tough and unsustainable in the long term. I need some sort of goal in order to motivate myself to lace up my running shoes and hit the road regularly.

In the lead-up to the Blackmore Half Marathon, I ran a half marathon every week for 5/6 weeks, one of them at work before a late morning team meeting in which I turned up feeling slightly dizzy and nauseous. Right after the Blackmore Half Marathon, I felt drained of all motivation and will power to run half marathons, which I have done only once since. This is where fun runs come in. They provide an excuse and catalyst to go out there and run week in and week out, helping to raise funds for charities at the same time.

I specially love the final week before a race day and the race day itself. Runners typically taper down in the final week or two before the big day, which basically means scaling back training and allowing the body to rest. On the day before the race, there is the delightful ritual of assembling the running gear, securing the bib and timing chip, arranging transport, and resting and drinking a lot of water.

On the race day itself, you see a lot of runners on the streets and public transport, some of them garbed in expensive lycra running rags and heart-rate monitors, and most of them bristling with bibs, timing chips and that quintessential trapping of all image-conscious modern runners - iPod.  Even though most fun runs are just that - fun runs - you sense tension in the air as most runners would be aiming for some kind of 'PB' (personal best), which may be nothing more ambitious than finishing the race.

Walking along the streets with other fellow runners, sharing public transport in expectant silence or milling about in the crowd waiting for the starter gun to fire, one feels a keen sense of kinship with other runners, all of whom would have busted their lungs and legs for weeks and even months to be where they are. In the early morning air, with limbs taut with coiled energy, one feels this adrenaline-laced cocktail of anticipation, tension and excitement that alone makes all the solitary runs worth the while.

Sunday, July 18, 2010

A View Of Taishan

By Du Fu

What shall I say of the Great Peak? --

The ancient dukedoms are everywhere green,

Inspired and stirred by the breath of creation,

With the Twin Forces balancing day and night.

...I bare my breast toward opening clouds,

I strain my sight after birds flying home.

When shall I reach the top and hold

All mountains in a single glance?

Sunday, July 11, 2010

My Country

By Dorothea Mackellar (1885 - 1968)


The love of field and coppice,

Of green and shaded lanes.

Of ordered woods and gardens

Is running in your veins,

Strong love of grey-blue distance

Brown streams and soft dim skies

I know but cannot share it,

My love is otherwise.

I love a sunburnt country,

A land of sweeping plains,

Of ragged mountain ranges,

Of droughts and flooding rains.

I love her far horizons,

I love her jewel-sea,

Her beauty and her terror -

The wide brown land for me!

A stark white ring-barked forest

All tragic to the moon,

The sapphire-misted mountains,

The hot gold hush of noon.

Green tangle of the brushes,

Where lithe lianas coil,

And orchids deck the tree-tops

And ferns the warm dark soil.

Core of my heart, my country!

Her pitiless blue sky,

When sick at heart, around us,

We see the cattle die-

But then the grey clouds gather,

And we can bless again

The drumming of an army,

The steady, soaking rain.

Core of my heart, my country!

Land of the Rainbow Gold,

For flood and fire and famine,

She pays us back threefold-

Over the thirsty paddocks,

Watch, after many days,

The filmy veil of greenness

That thickens as we gaze.

An opal-hearted country,

A wilful, lavish land-

All you who have not loved her,

You will not understand-

Though earth holds many splendours,

Wherever I may die,

I know to what brown country

My homing thoughts will fly.

Sunday, July 4, 2010

Correlated Subquery

The NULL issues discussed in the previous entry on subquery can be side-stepped with correlated subqueries. Correlated subqueries are different to subqueries in that for each outer row, the query tries to find a matching inner row. If such an inner row exists, then the outer row is returned. For example,
select *
from t1
where exists
(select *
from t2
where t1.a_col = t2.a_col);

The above query can be optimized as follows:
select *
from t1
where exists
(select '1'
from t2
where t1.a_col = t2.a_col);

Notice that the inner query projects the value '1' if there is a match instead of the whole row, which could have dozens of columns.

What happens if the WHERE clause has NOT EXISTS instead of EXISTS, and the inner query returns NULL? In that case, the outer query returns nothing, which is the expected behavior. Presto, the perils of NULLs are nullified!

Sunday, June 27, 2010

Subquery And Null

Last week, I completed a four-day Teradata Advanced Sql course offered by Teradata Australia Pty Ltd and sponsored by my employer. In this series, I will document some of the insights that I gained and the tricks of the trade that I learnt. Today, I will write about some gotchas associated with subqueries.

The following is a garden variety subquery that I write all the time:
SELECT *
FROM t1
WHERE some_col NOT IN (SELECT a_col FROM t2);

The above works as long as the subquery does not return a NULL in what is effectively a comma separated list of values within the pair of parenthesis. However, things go pear-shaped if the subquery returns one or more NULLs in the list. When that happens, the outer query returns nothing.

The outer query compares some_col with each value returned by the subquery.  Say, the subquery returns val1, val2 and NULL. For the outer query to return a row, the condition that needs to be true is this: 
some_col not equal to val1
AND some_col not equal to val2
AND some_col not equal to NULL;

Comparison with a NULL results in NULL, and so does ANDing a value with a NULL. Therefore, the preceding condition never becomes true, and the outer query does not return anything.

NULL is not an issue in the following query (it has 'IN' instead of 'NOT IN' in the WHERE clause):

SELECT *
FROM t1
WHERE some_col IN (SELECT a_col FROM t2);

In the above query, some_col needs to be equal to only one of the values returned by the subquery for the outer query to return a row. In other words, some_col gets ORed with the values returned by the subquery. Therefore, NULL is not an issue.

There are ways to circumvent the problems caused by NULLs in a subquery, and they will be discussed in the next installment of this series.

Sunday, June 20, 2010

Returning At Night To Lumen Mountain

By Meng Haoran

A bell in the mountain-temple sounds the coming of night.
I hear people at the fishing-town stumble aboard the ferry,
While others follow the sand-bank to their homes along the river.
...I also take a boat and am bound for Lumen Mountain --
And soon the Lumen moonlight is piercing misty trees.
I have come, before I know it, upon an ancient hermitage,
The thatch door, the piney path, the solitude, the quiet,
Where a hermit lives and moves, never needing a companion.

Monday, June 14, 2010

Can Israel Folau Beat the 10k Rule?

Malcolm Gladwell, author of the international bestseller The Tipping Point (2000), pored through reams of research data compiled by economists, psychologists and social scientists to construct a simple formula for becoming a genius in his 2008 book Outliers: The Story of Success. Gladwell's own genius lies in teasing out previously unsuspected patterns and relationships from jumbles of arcane academic data, and weaving them into simple narratives accessible to the lay audience. His insights into the makings of a genius can be summarised as follows:

  • True mastery in any complicated field requires 10,000 hours of conscious practice

  • Geniuses such as Bill Gates, Bill Joy and the Beatles received their apprenticeships through a combination of good fortune and conscious effort

  • The popular myth of a genius as a solitary figure fighting alone against the prevailing currents of society is just that, a myth. Most geniuses receive family support and community patronage during their formative years


A couple of weeks back, at the height of the media frenzy sparked by National Rugby League (NRL) superstar Israel Folau's six-million dollars defect to the rival Australian Football League (AFL), Channel 9's sports program Wide World of Sports re-played Folau's interview from a year back.

Many NRL fans regard Folau, a towering, powerfully-built young centre-winger whose gravity-defying jumps and electrifying bursts near the try line captured the imagination of NRL fans and excited the predatory instincts of AFL poachers, as a natural talent and born athlete. In the interview, Folau bristled at this notion, reminiscing about getting up at 5am in the mornings as a kid and being driven to trainings by his dad when most kids his age would have been sleeping like angels.

If Folau is not a natural talent, then I suppose no one is.

Another famous 'natural' talent, at least in the minds of sports fans, was Michael Jordan, who could perhaps teach even Folau a thing or two about gravity-defying jumps. However, many sports fans are unaware that Jordan could not even get picked for his high school basketball team. What Jordan did after being snubbed thus was practise, practise and practise until he had presumably clocked over 10,000 hours of conscious practice.

The operative word here is 'conscious'. Practice must be informed by a conscious, deliberate effort to improve, which more often than not entails seeking critical appraisals from mentors, coaches and peers, creating a positive feedback loop. This perhaps explains why many clubhouse chess players get stuck in a rut despite many years of weekend skirmishes, or most aspiring footy players fall by the wayside,  never graduating from park footy.

According to research, this 10k hours rule is quite general and applies across diverse non-trivial human pursuits such as music, mathematics, computer programming and writing.

Of British-Jamaican heritage, Gladwell, who currently writes for The New Yorker, is on the record saying that he completed his 10k writing apprenticeship working as a journalist, in which role he would have received feedback from experienced copy editors on his craft. He closes Outliers with his own chequered family history to buttress his thesis that family and the wider community play a vital role in the development and emergence of a genius.

Given that any non-trivial skills set requires 10k hours of practice to master, University of Sydney's David Anderson, a skills acquisition specialist who was quoted in a Sydney Morning Herald article on June 5-6, cast serious doubts on the ability of Folau to transfer his NRL skills to AFL, which is governed by a completely different set of rules and patterns of play.

Athletes with brute physical strength and ability to run short distances at explosive pace tend to thrive in NRL. AFL is more of an endurance sports requiring kicking skills absent in NRL and the temperament of a long distance runner. Then, there is that undefinable knack for reading the game, which separates elite players from merely good ones.

"They (NRL players) can run fast and they're big and strong, they can pass and catch and kick, but what they can't do is read the patterns of play and that puts them at a massive disadvantage," Anderson was quoted as saying. The article also pointed out the obvious: Folau and his fellow AFL convert Carmichael Hunt will simply not have enough time to log 10k hours of practice, which roughly takes around 10 years in real life, before the show begins.

Will Folau defy the 10k rule and prove his doubters wrong to have a productive AFL career, or will he, too, join the rank of other code-switching NRL stars such as Wendell Sailor, Lote Tuqiri and Timana Tahu, all of whom came back to NRL after disappointing stints at another rival footy code?  Only time will tell.

Sunday, June 13, 2010

City2Surf 2010: Running for Fun & Charity

City2Surf

I am taking part in this year's City2Surf fun run to be held on Sunday, August 8. The run starts at Sydney's Hyde Park at the heart of the city and ends 14.1km later at Bondi Beach in the eastern suburbs. This will be my third participation in this event, which is regarded as the world's largest fun run. More than 75,000 people took part in it last year.

This year, I am raising funds for RSPCA, which is Australia's only truly national animal welfare organization. If you can, please help me raise funds for RSPCA via my HeroPage here. Your tax-deductible contributions will help RSPCA accept and take care of unwanted animals and investigate cases of animal cruelty.

rspca

Friday, June 11, 2010

Three Chinese Poets

After one of my friends made a favourable comment on the Tang poem posted in the previous entry, I picked up from my modestly-stocked bookshelf Vikram Seth's Three Chinese Poets, a translation of poems by the three major Tang Dynasty (618-907) poets, Wang Wei, Li Bai and Du Fu. I have this habit of signing and dating books after I buy them, and this is how I know that I purchased this hardcover edition on March 1, 1993, probably from one of my three favourite bookshops in Kathmandu.

When I visited the small Himalayan Book Centre opposite the old bus park at Bagh Bazaar, Kathmandu, in 2007 after an 8-year-long self-imposed antipodean exile, I was pleasantly surprised and moved when the elderly bookstore owner with thick glasses and slick oiled hair recognized me and enquired where I had been for so long!

Seth, who spent eleven years at Stanford University from 1975 to 1986 researching an economic doctoral thesis that he never completed, went to China to collect field data. While there, he honed his Mandarin. This must have inspired and emboldened him to translate the three premier classical Chinese poets later on.

Seth introduces the three near contemporary poets thus:  "The standard trichotomy of Wang Wei as Buddhist recluse, Li Bai as Taoist immortal and Du Fu as Confucian sage has been rejected by some critics as unsubtle and artificial, but it can act as a clarifying approximation for those approaching Chinese poetry of this period for the first time.

"Wang Wei's typical mood is that of aloneness, quiet, a retreat into nature and Buddhism. What one associates with him are running water, evening and dawn, bamboo, the absence of men's voices. The word 'empty' is almost his signature."

Enough talk. Here is a famous poem by Wang Wei as translated by Seth that distills the essence of the contemplative state of mind of a Buddhist hermit:

Birdsong Brook
Idly I watch cassia flowers fall.
Still is the night, empty the hill in Spring.
Up comes the moon, startling the mountain birds.
Once in a while in the Spring brook they sing.

The theme of aloneness and emptiness that Seth associates with Wang Wei is expressed masterfully in the following verse. In the introduction to the translations, Seth draws readers' attention to the contrast of meaning between lines three and four, and five and six, which was required by the strictures of the regulated form of the octet.

Living in the Hills: Impromptu Verses
I close my brushwood door in solitude
And face the vast sky as late sunlight falls.
The pine trees: cranes are nesting all around.
My wicker gate: a visitor seldom calls.
The tender bamboo's dusted with fresh powder.
Red lotuses strip off their former bloom.
Lamps shine out at the ford, and everywhere
The water-chestnut pickers wander home.

In terms of poetic sensibilities, Li Bai was the opposite of Wang Wei. In Seth's words, "Li Bai's poetry sparkles with zest, impulsiveness, exuberance, even at the risk of bombast and imbalance. Sword, horse, wine, gold, the moon, the Milky Way and impossibly large numbers are recurring features of his work. He attempts alchemically to transmute life through the intoxication of poetry or music or wine into delight and forgetfulness." This is what Seth means:

The Waterfall at Lu Shan
In sunshine, Censer Peak breathes purple mist.
A jutting stream, the cataract hangs in spray
Far off, then plunges down three thousand feet --
As if the sky had dropped the Milky Way.

'Bromance' was a staple of classical Chinese poetry long before Matt Damon and Ben Affleck pioneered it in Hollywood. Just like everywhere at the time, in classical China, travels were ponderous, long and fraught with many dangers, and, when friends parted, they could not be sure if they would ever see each other again. After one such parting, Li Bai wrote the following:

Parting at a Wineshop in Nanjing
Breeze bearing willow-cotton fills the shop with scent.
A Wu girl, pouring wine, exhorts us to drink up.
We Nanjing friends are here to see each other off.
Those who must go, and those who don't, each drains his cup.
Go ask the Yangtze, which of these two sooner ends:
Its waters flowing east - the love of parting friends.

Du Fu was born into an impoverished, cadet branch of the royal family, and, despite his prodigious poetic talents, did not pass the imperial civil service exams after repeated attempts (Empress Wu, a concubine of the founding Tang Dynasty Emperor Tai Zong, introduced verse composition into civil service exams during her 15 year reign, prompting a critic to observe that the Chinese thought poets made the best administrators). This effectively hobbled his Confucian aspirations to high office. He spent the rest of his life in penury and desperation, holding minor official posts and suffering horribly during a rebellion from which the Tang Dynasty never recovered.

Typically, the rebellion was started by a 'barbarian' general named An Lushan, who had managed to gain the trust of Emperor Ming Huang's favorite concubine Yang Guifei. When An Lushan marched on the imperial capital Changan, the court fled the capital. The emperor was powerless to save Yang Guifei from the murderous rage of court officials, who strangled her to death with a silk scarf. Whenever I mentioned this episode to my Chinese friends at university, all of them would automatically assume that I had learnt about it by watching Chinese TV soaps on the topic.

Seth writes: "Du Fu's poetry is informed by deeply suggestive and often sad reflections on society, history, the state and his own disturbed times, all central concerns of Confucianism. But what especially endears him to the Chinese is his wry self-deprecation combined with an intense compassion for the oppressed or dispossessed people of every kind in a time of poverty, famine and war."

The following poem is interpreted either as the universe's indifference to human suffering or life's capacity for renewal and growth in the face of wars and tragedies.

Spring Scene in Time of War
The state lies ruined; hills and streams survive.
Spring in the city; grass and leaves now thrive.
Moved by the times the flowers shed their dew.
The birds seem startled; they hate parting too.
The steady beacon fires are three months old.
A word from home is worth a ton of gold.
I scratch my white hair, which has grown so thin
It soon won't let me stick my hatpin in.

Du Fu was too poor to keep his family in the capital Changan. In the following poem, he pines for his family in a moonlit night in Changan:

Moonlit Night
In Fuzhou, far away, my wife is watching
The moon alone tonight, and my thoughts fill
With sadness for my children, who can't think
Of me here in Changan; they're too young still.
Her cloud-soft hair is moist with fragrant mist.
In the clear light her white arms sense the chill.
When will we feel the moonlight dry our tears,
Leaning together on our window-sill?

Sunday, June 6, 2010

On Climbing Orchid Mountain In The Autumn To Zhang

By Meng Haoran
On a northern peak among white clouds
You have found your hermitage of peace;
And now, as I climb this mountain to see you,
High with the wildgeese flies my heart.
The quiet dusk might seem a little sad
If this autumn weather were not so brisk and clear;
I look down at the river bank, with homeward-bound villagers
Resting on the sand till the ferry returns;
There are trees at the horizon like a row of grasses
And against the river's rim an island like the moon
I hope that you will come and meet me, bringing a basket of wine --
And we'll celebrate together the Mountain Holiday.

Tuesday, May 25, 2010

Memoir of a Long Distance Runner

In his former life, Haruki Murakami used to be a  jazz club owner in Tokyo who smoked 60 cigarettes a day. In his current avatar, he iswhat i talk about when i talk about running a passionate long distance runner who quit smoking when he started running. By his own confession, he runs 10k everyday, and a half marathon and a marathon every year. From New York City to Boston Marathon, he has done it all. Many times. And he once ran a soul-sapping 100k ultramarathon.

In 1982, he ran his first unofficial marathon, in the original marathon course in Greece in sweltering summer heat, from Athens to the eponymous village of Marathon. It was near this small village that a small army of Athenians defeated the invading Persian force of Darius in 490 BC (British author Tom Holland's 2005 book Persian Fire re-tells the story of this battle in a colorful style although his barely disguised contempt for orientals might put off some readers). On the busy straight highway that links Athens to Marathon, Murakami kept the count of roadkill: three dogs and eleven cats flattened against the bitumen.

Murakami also happens to be a world famous writer. Everyday, before hitting the road for his daily 10k run, he sits at his desk for four hours,  applying the same intensity, focus and perseverance to writing that he cultivates during long solitary runs. In fact, Murakami claims that he learned everything about writing from long distance running. These two key preoccupations of his life, writing and running, furnish the subject matter of his slim memoir What I Talk About When I Talk About Running published by Vintage in 2008.

I bought Murakami's memoir to seek inspiration and concrete advice on training, strategy, race-day tactics, etc, in order to rekindle my own flagging running motivation. On these counts, the book was a bit disappointing. Sure, Murakami takes readers through his elaborate preparations for 2005 New York City Marathon and triathlons held in Japan and Hawaii, and how he fared in them. However, they come between extended meditations on aging and its corrosive effects on creativity and physical abilities. After a certain age, just as the wellspring of creativity starts to dry up, so does distance running increasingly become an exercise in diminishing returns.

This is not to say that the book was not a pleasure to read. It is just that it dwells less on the mundane details of distance running and more on its metaphysics. If you are after a distance running training manual, you have to look somewhere else.

Wednesday, May 19, 2010

Watson Bay to Bondi Beach

On the day that 16-year-old Jessica Watson sailed back into the Sydney Harbour after her solo world round trip in her pink yacht, we walked from the Gap in Watson Bay to the famous Bondi Beach along the track that hugs the cliffs in Sydney's beautiful eastern suburbs. On the left, we could observe a flotilla of boats sailing out on the turquoise blue waters of the Tasman Sea to escort back Australia's latest teen hero. On the right, the Sydney Harbour was teeming with more watercraft.

Along the track from the Gap to the Lighthouse Reserve and further down in Dover Heights, walkers and tourists, some of them with binoculars, were peering out to the sea at a distant cluster of  boats,  which had circled a seemingly stationary pink speck that we surmised was Jessica's yacht. During the whole time that the cluster was in our field of vision, it did not seem to move an inch.

The Watson Bay to Bondi Beach walk, along with Manly to the Spit and Bondi to Clovelly walks, must rank among Sydney's three most beautiful walks. The trail itself is easy with no sharp elevations, and is less than nine kilometers long. At the Bondi Beach, instead of hopping onto a Sydney bus, we decided to keep pushing on foot along Bondi Road to the nearest train station at Bondi Junction, notching up another couple of kilometers.

To get to Watson Bay, buy a return train ticket from your nearest train station to Bondi Beach, get off at Edge Cliff station and catch 324 or 325 bus from Stand C to Watson Bay. The bus ride takes around half an hour.  You can use the train ticket to catch a bus from Bondi Beach to Bondi Junction.

I took a few pictures with my old, automatic Sony DSC-W70 along the way, which you can see below.

[gallery link="file" columns="6" orderby="ID"]

Sunday, May 16, 2010

Three Cheers To the Old Champion

With the successful defense of his second world title against the formidable Bulgarian GM Veselin Topalov earlier this month, Indian GM Vishwanathan Anand has again proved himself to be one of the greatest chess players of his era. The match went down to the wire, with both GMs tied at 5.5 with one game to play. In the final game, Anand, disadvantaged with Black pieces, still prevailed over his illustrious opponent.

[caption id="attachment_351" align="alignright" width="297" caption="Anand contemplates the position on the board."]Vishy Ananda[/caption]

Anand was a childhood hero of mine. After he became the FIDE world junior champion in 1987, I think it was the Indian sports magazine, Sports Stars, that shipped with a centerfold of a youthful, bespectacled Anand staring intensely at a chess board. That image adorned the wall of my bedroom and fueled my own youthful, quixotic dreams of becoming a GM.

In a sense, Anand carried the hopes of all the youth from the developing world but specifically from South Asia. The fact that India is today poised not only as a rising economic but also a chess powerhouse owes a lot to Anand's spectacular exploits at the highest levels of chess. He has inspired an entire generation of chess players in India and South Asia. The only other Indian chess player to make his mark at international level before was Mir Sultan Khan (1905 - 1966), who won the prestigious British Championship three times between 1929 and 1933 before his promising career was cut short to attend to his duties in the service of an Indian prince.

When Anand first won the FIDE world chess championship in 2000, he became only the second person to wear the crown who was not of Russian or former Soviet background since 1937. The other person was the mercurial American GM Bobby Fischer, who, in a highly charged Cold War superpower theatrics of 1972, defeated the then world champion Boris Spassky of the USSR in Reykjavik, Iceland.

What was astonishing about Anand's meteoric rise was the suddenness with which he appeared out of seemingly nowhere to take the chess world by storm. Although the birthplace of chess, India did not have the state-backed chess patronage system of the USSR, which marketed the post-WW II global dominance of its chess players as the validation of its ideology, or a vibrant community of grandmasters, theorists, writers, supporters and clubs to match those in the US and Britain. When he attained the rank of grandmaster in 1988 at the age of 18, it was the first for his country.

One of the crowd-pleasing features of Anand's style is the speed with which he makes his moves. This has served him well in shorter formats of the game as well as in the classical format, where he almost never runs into time troubles. Imagine Sachin Tendulkar and Cameron White, unrivaled masters of Test and Twenty20 cricket, respectively, rolled into one. This capacity for lightning fast thinking earned Anand the nickname of 'speed demon' early in his career.

A new brat pack of chess superstars led by the 19-year-old Norwegian GM Magnus Carlsen, who became a GM at the age of 13, is snapping at the heels of Anand and his generation of grandmasters. In the next world championship cycle in 2012, Anand will face players who grew up on a diet of computer chess. It will be interesting to see if this new breed of players will be able to topple the last of the pre-Internet generation of grandmasters who still rule chess.

Sunday, May 9, 2010

Reading Best Australian Essays: An Annual Rite

Best Australian Essays 2009Reading Black Inc. anthology of best Australian essays of the year has become something of an annual rite for my readerly self. Edited by a well-known published writer, the anthology showcases essays that meet three criteria.

Firstly, the essays have to satisfy the standards set by the editor, which could include parameters such as currency and relevance to contemporary debates and issues, literary and aesthetic merits, and whatever else the particular  idiosyncrasies of the editor dictates. Secondly, the writers have to be Australians and/or the essays have to engage Australian topics. Lastly, the essays have to have been published in the previous calendar year.

Having found last year's offering, The Best Australian Essays 2008, which was edited by David Marr, a bit disappointing, I bought this year's collection with a degree of reservations. I need not have worried. So far, the essays, which range far and wide just as Black Inc. promises, have succeeded to 'entertain, inspire and provoke'.

I have been reading the essays in random order (surely, an oxymoron), and two essays that have so far stood out are Richard Castles' Death Duties and Robert Dessaix's The Grand Illusion. In the former, Castles recounts the year when he worked in the grandiosely named Transfer Response Unit but which the author liked to think of as a 'taxi service for the already departed'. In unadorned terms, the job involved collecting dead bodies or body parts from homes, hospitals, nursing homes and crash sites whenever the coroner wanted to examine the bodies.

Castles observes that 'death could be beautiful', especially when old folks died in nursing homes. However, "Like all things in life, death can be done in a variety of bizarre and often messy ways". And, yes, people die during sex, he informs helpfully from his macabre experience.

The writer describes his first body pick-up memorably. A gung-ho homicide detective greets him and has a dig at him for his inexperience. He writes: "I simply told him that he was right, that this was my first time. As I walked through the door, I caught first glimpse of the body in situ. I never got used to the jolt of those moments. A dead body is always out of place: an interruption in the picture; a spectre; a mistake."

In a culture where death is sanitized, where most people have never seen a dead body, Castles' essay confronts readers with the pervasiveness of death, its finality and  implacability, the stark reality that it is always lurking nearby in the shadows, just below our consciousness.

[caption id="attachment_344" align="alignright" width="105" caption="Robyn Davidson, editor of The Best Australian Essays 2009. Picture Courtesy of Black Inc."]Robyn Davidson[/caption]

Dessaix's essay The Grand Illusion sifts through the travel industry hype and hyperbole to re-define the notion of a perfect getaway, in itself a quaint Victorian notion that became a 20th century mass consumer craze just like MacDonald's thanks to the swelling of the middle class rank in the developed countries.

For Dessaix, who, among others, authored Twilight of Love: Travels with Turgenev (2004), the perfect holiday destination is not tourist-infested Bali but the "grubby little Tozeur on the edge of the Sahara in Tunisia's south, however, where there's nothing to see and nothing ever happens" but where "I feel dense with wakefulness".

"In Tozeur, in the labyrinth of the old town, lost amongst the endless palms, or just loitering with intent outside a carpet shop at night, when the town springs to life, I find myself very interesting indeed."

The central contention of this essay is that modern travel is a form of escape from our own bored, satiated selves to places where we find ourselves interesting again. This may not necessarily happen in luxury hotels and resorts where we end up drinking the same cocktails and watching the same silly sitcoms as at home.

"The antidote to boredom lies not in excitement, amazement, unparalleled vistas or repose in paradise, but in being woken up to our own inner complexity, our density, and befriending it," Dessaix writes. He cheekily suggests that such inner awakenings could take place even in, wait for it, Dubai, the new Mecca of conspicuous consumerism, for "Good travelling depends not on the travel, but on the traveller."

Wednesday, April 28, 2010

Binary Search Algorithm

Apparently, only 10 percent of programmers can correctly code the binary search algorithm. What follows is my attempt to prove once and for all that I belong to that select group of programmers! I implemented the algorithm in Excel VBA.
Option Explicit
'++++++++++++++++++++++++++++++++++++++++++++++++
' Author: Ram Limbu
' Date: 2010/04/28
' Description: Binary Search Algorithm in VBA
'++++++++++++++++++++++++++++++++++++++++++++++++

Sub BinaySearch()
Dim UpperLimit As Long
Dim LowerLimit As Long
Dim Mid As Long
Dim Val As Long
Dim NotFound As Boolean
Dim WS As Worksheet

On Error GoTo ErrRtn

'prompt user to enter the number to search
Val = InputBox("Please, enter the number to search")

'set WS to the first Worksheet
Set WS = ThisWorkbook.Worksheets(1)

NotFound = True
LowerLimit = 1

UpperLimit = WS.Cells(65536, 1).End(xlUp).Row

If UpperLimit = 1 And IsEmpty(WS.Cells(1, 1).Value) Then
MsgBox "The Column A on Worksheet1 is empty. Exiting ..."
Exit Sub
End If

While NotFound And LowerLimit <= UpperLimit
Mid = (LowerLimit + UpperLimit) \ 2 ' perform integer division
If Val = WS.Cells(Mid, 1) Then
NotFound = False
ElseIf Val < WS.Cells(Mid, 1) Then
UpperLimit = Mid - 1
Else
LowerLimit = Mid + 1
End If
Wend

If NotFound Then
MsgBox Val & " wasn't found"
Else
WS.Cells(Mid, 1).Select
MsgBox Val & " was found at row " & Mid
End If
Exit Sub
ErrRtn:
MsgBox Err.Description
End Sub

The assumptions are as follows: (1) The data set is pre-ordered and (2) located on Column A of the first Worksheet. To keep the code simple, no data validation is performed.

Monday, April 26, 2010

The Makings of Obama

The life story of President Barack Obama is too well-known to repeat here.  However, having just read his acclaimed memoir Dreams From My Father (1995), I just had to add my two cents' worth to the growing discipline of Obamalogy.

[caption id="attachment_303" align="alignright" width="220" caption="Young Obama with his (from left) grand father, mother and half-sister Maya in Hawaii early 1970s. Picture sourced from wikipedia"][/caption]

Written after becoming the first African American president of Harvard Law Review and years before he ran for his first political office, the book strikes readers with the honesty and poignancy of the voice of a young African American man searching for his place in the world.

Born in Hawaii to a white American mother and a black Kenyan father, the young Obama feels trapped between two worlds, and groans under the unbearable burden of the 'unique and universal' history of his people in the US.  He recounts the angst and sense of helplessness felt by himself and his black college friends in a world in which they feel the rules of the game have been written by somebody else and even their perceived identity foisted on them by others.

The search for his identity takes Obama to the ancestral land of his forefathers in a remote village in Kenya.  There, he learns the full story of his own absent father, who, despite being a brilliant Harvard scholar, died a broken man thanks to a combination of his naive idealism, incurable character flaws and ancient tribal grudges.

Obama largely won his presidency in 2008 on the back of his sublime oratory reminiscent of Abe Lincoln. His simple, direct prose is equally a joy to read.

Towards the end of the book, a female African history professor that Obama meets in Kenya tells him, "In the end, I'm less interested in a daughter who's authentically African than one who is authentically herself." I think there is a priceless lesson for all of us in this beautiful thought.

Friday, April 23, 2010

Remembering the Forgotten Soldiers of a Vanished Empire

It is that time of year again when Australians and New Zealanders remember their fallen war heroes and celebrate the veterans as part of the ANZAC Day festivities. It is an occasion when the self-sacrifice of the few are mourned and mythologized, and the values they fought for are cheered and cherished by the multitude. It is a day when the national fabric is examined, repaired and renewed in solemn ceremonies held across Australia, New Zealand and Gallipoli, Turkey.

On this occasion, I shall remember another group of heroes, second to none in valour, sacrifice, comaraderie and courage under fire, whose stories remain largely untold, who have been shoved into the shadows of history, who have been disowned by their own compatriots. I am talking about Gurkhas, the forgotten soldiers of a vanished empire.

A fortuitous by-product of the 19th century British imperial adventures in India, Gurkhas shared many commonalities with their ANZAC counterparts. Both Gurkhas and ANZACs, along with colonials such as Sikhs in India, fought in wars in which they had no discernible stakes. Both served masters whose chief interest in them lay in using them as shock troops to further their imperial gains. Both were heaped with soaring rhetorical bouquets but cast away thoughtlessly as soon as they outlived their usefulness. Both were used as political pawns by their respective leaders eager to please an empire where the sun never set.

The crucial difference between the Gurkhas and ANZACs lies in the way they were perceived and treated by their own countrymen. ANZACs were retrospectively elevated to the sacred rank of national heroes and living treasures. They formed the bedrock of their nation's founding myth. In the Australian national narrative, the new European settlers' claim to the Great Southern Land, whose original inhabitants were dispossessed, marginalized or killed after the European arrival in 1788, did not become legitimate until young Australians were mowed down by Ottoman Turkish machine guns on the shores of Gallipoli Peninsula in 1915. Such is the price exacted by the birth of a nation state.

Gurkhas occupied the opposite end of the national mythology spectrum. They were an unpleasant reminder of a humiliating defeat inflicted on Nepal by the British East India Company in the Anglo-Nepalese War (1814 to 1816). In a treaty that followed the war, Nepal was forced to cede one-third of its territory, put up with a British Resident in Kathmandu and permit the British East India Company to recruit Nepalese youth into its private army, effectively becoming a British client state. Such is the price exacted upon a nation that comes second best in a collision with a super power.

Furthermore, the British chose to recruit only from those Nepalese ethnic groups that it classified as the "martial races", which also happened to be the newly conquered peoples of Nepal by the Gorkha Kingdom. Hence, the Gorkha ruling elite had at least two reasons to banish Gurkhas from national consciousness and write them out of history books.

On this ANZAC Day this Sunday, when fresh wreaths are laid down and the Last Post sounded for the fallen ANZAC diggers, some of us whose stories are interleaved with those of the Gurkhas will mourn and pay homage to the memory of these forgotten soldiers of a vanished empire, whose exploits in Gallipoli matched and even outshone those of the ANZAC heroes.

Sunday, April 18, 2010

Miserere mei, Deus, or How Two Wunderkinds Foiled a Papal Conspiracy

The story has all the elements of Da Vinci Code minus the horrible murders: A powerful organization that wants to keep the lid on a sacred work it deems too painful for ordinary mortals to bear. An accomplished music composer and priest who sings in the Papal Chapel in Rome from the age of 9 until his death. A Pontiff who makes smoking tobacco an excommunicable offence, orders the most notorious persecution of a scientist in history and pillages the Partheon. An 18th century child prodigy who creates the first known bootleg copy of a piece of music. A 20th century boy with the voice of an angel who sings it into a global sensation. And the celebrated Old Testament King who starts it all.



Welcome to the enchanting world of Miserere mei, Deus, a late Renaissance work for choirs composed by the Italian priest Gregorio Allegri (1582 - 1652), who was an early candidate for the intriguing phenomenon known as the one hit wonder. Although Allegri composed many other works, he, like Pachelbel of Canon and Gigue in D fame, is today solely remembered for a single work, Miserere me, Deus, which is shrouded in secrecy, legend and mystery thanks in no small part to the Vatican.

Allegri was appointed to the Papal Choir in Rome during the reign of Pope Urban VIII (1568 - 1644), who was a scion of the important Florentine family of the Berberini. A great patron of the arts, the Pope earned future notoriety by coercing Galileo to recant his heliocentric theory of the solar system and looting the metal girders from the Partheon to manufacture canons, an act of unimaginable vandalism that prompted the jibe "What the barbarians did not do, the Berberini did".

In the subsequent years, the only place where Miserere could be heard was in the Sistine Chapel in Rome, where it was played during the Holy Week. Like modern-day pilgrims' visit to Google's headquarters in Mountain View, California, it became fashionable for 18th century artists, socialites and aristocrats on European Grand Tour to attend the performance of Miserere in the Papal Chapel and twitter about it in their diaries and letters back home.

Then, suddenly, in 1770,  Miserere became public property thanks to a precocious 14-year old boy named Mozart.  The visiting child prodigy from the Archbishopric of Salzburg listened to the performance of Miserere once, transcribed everything from memory, went back next day to listen to one more performance in order to make some corrections in his copy, and shared his booty with a visiting English traveler, composer and musicologist, Dr. Charles Burney. Dr. Burney published the score in 1771. The Vatican had no choice but to remove the ban and applaud the genius of history's first and most famous music bootlegger.

Some time in the late 19th century, a copyist's error crept into the score of Miserere, the so-called top C, which became part of the canonized version of the work as performed today. Many connoisseurs believe that this chance error actually served to enhance the beauty of the original score, in itself a contentious notion that has added to the work's enduring mystique.

Before the teenage Mozart blew the Vatican ban on Miserere, only three authorized copies of the score were reported to have been made and distributed outside the Vatican. One of the recipients of this papal favor, the Holy Roman Emperor Leopold I, is reported to have remonstrated with the Pontiff that the copy that he received did not match the score as performed in the Papal Chapel as the score was missing ornamentation. Ever since then, rumors and myths have circulated about secret ornamentation that were passed from performer to performer in the Papal Chapel but missing from many unadorned scores that floated around Europe.

The modern version of Miserere itself is an end product of many revisions and editing as the work crossed both temporal and spatial boundaries after its release from the gilded vaults of the Vatican.

It was another wunderkind, a 12 year old British boy named Roy Goodman who propelled Miserere into popular consciousness with his celebrated performance with the Choir of King's College in the March 1963 recording for Decca label. In a pleasing symmetry spanning a couple of centuries, the Renaissance polyphonic masterpiece set to Psalm 51, which was written by King David in a fit of repentance after his adulterous affair with Bathsheba, was finally introduced into the bazaar of popular culture by another daring youngster.

Roy Goodman, currently one of the world's most successful freelance conductors, was recently interviewed by Margaret Throsby on ABC Classic FM radio. You can listen to the interview here.

Saturday, April 10, 2010

Remembering Girija

Reading through some obits of late Girija Prasad Koirala (1925 - 2010) in the Nepalese media, it seems that in the eye of some commentators, Koirala managed to redeem himself somewhat in the final decade of his life. To begin with, he is praised for standing up to the then Nepalese King Gyanendra, who assumed absolute power in a short-sighted royal coup in 2002 that spelled the end of monarchy in Nepal. Koirala is also credited with persuading parliamentary parties to sign a 12-point agreement with Maoists in Delhi in 2005 that paved the way for the peace process that halted a decade-long armed Maoist insurgency.

The vast majority of the Nepalese people remained hostile to Koirala until the very end, however. Many were unmoved by the news of his death, openly expressing glee and hope that his death would herald a new dawn for Nepal. Others pointed out that even his practical rapproachment with the Maoists was motivated by what many consider to be the most serious blemish on his legacy: nepotism and self-aggrandizement. According to this interpretation, Koirala wanted to resurrect his political fortunes and dynastic ambitions by aligning himself with his erstwhile sworn foes, the Maoists.

Whatever one's opinions about Koirala, he was indisputably one of the most divisive, controversial and dominant political players in the post-1990 Nepal. His early political apprenticeship itself reads like that of a Mafioso enforcer. Living in the shadows of his illustrious older brother BP, who surely was the toughest act to follow in the Nepalese politics, Koirala did the dirty work for his party, the Nepalese Congress. Organizing Biratnanar Jute Mill workers to agitate against the autocratic Rana regime, smuggling weapons across the Nepal-India border to arm anti-Rana forces, hijacking a currency-laden Royal Nepal Airlines plane, printing counterfeit Indian Rupees and eliminating party rivals were all in the day's work for a young Koirala.

In his autobiography, BP, whose literary legacy might yet outshine and outlive his considerable political legacy, famously dismissed his younger brother GP as a party hawaldar or foot soldier, anointing his niece Sailaja as his political heir.

In the wake of the 1990 People's Movement that restored multi-party democracy in Nepal, Koirala emerged as an influential member of the powerful Nepalese Congress triumvirate along with Ganeshman Singh and Krishna Prasad Bhattarai. In the political chaos that followed, Koirala managed to sideline his two key party rivals amidst bitter public feuds, and did his best to undermine a short-lived government of Communist Party of Nepal (United Marxist Leninist). In the end, he surpassed his older brother BP's wildest dreams by becoming Prime Minister for five times on the back of his undoubted skills at mobilizing the party faithful.

As a reporter for The Kathmandu Post, I had occasion to cover many tedious ceremonies and functions in which Koirala cut ribbons and/or starred as the chief guest. One such occasion left an indelible mark on my mind.

The function was held at the Blue Star Hotel. Koirla was not holding any official position at the time - of course, nothing less than the Prime Minister's Office would have suited his ego - even though his party had formed the government. One of the senior ministers in the government, Bal Bahadur Rai, was also invited to the function.

Rai committed the unpardonable sin of arriving at the function after his party overlord Koirala, whose career was far from over despite appearances. As if to prove this point, a sanctimonious Koirala went on to give a very public dressing down to the embarrassed senior minister, scolding him like a mischievous school boy for arriving late at the "people's function". All that Minister Rai, who looked more like a dreamy, self-satisfied Abbot of a Buddhist monastery than a senior Nepali Congress leader and cabinet minister that he was, could do to counter the hypocritical and self-righteous tirade from his boss was to grin sheepishly and fold his hands together in a gesture of feigned humility and remorse.

Tuesday, January 5, 2010

Parsing INI Files With VBA

INI files are still widely used to enable users to configure applications even though their use is deprecated in the Windows world. The recommended method is to write user configuration data to the Windows registry.

From VBA Word, the INI files (and Windows registry) can be accessed and modified through the VBA Object library function System.PrivateProfileString(). In VBA Excel, this library call is not available. To read INI files, one option is to employ the Windows API function GetPrivateProfileString Lib "kernel32". The other option is to write your own INI parser, which is what I did the other day.

Apart from the normal comments (comments start with # in this version of INI), the script also tolerates end-of-line comments.

The script also enforces unique section names by generating an error if a duplicate section name is detected. This is quite important as the script stores the INI data as key-value pairs, and the way it ensures unique key names is by prefixing section names to key names, with a dot separating the two.

The script also gives users the option of loading the generated data file to Teradata using the FastLoad utility.

Sample ini file:

# Author: Ram Limbu
# Date: 2009/11/28
#
#
#

[FILE_SETTINGS] # start of file settings

DestDir=C:\Documents and Settings\Ram_2\My Documents\Excel Projects\Config # don't end this line with slash
Delimiter=,
DestFileName=ErrorFiles.csv

[DATABASE_SETTINGS] # start of database settings

InsertData=Yes # Acceptable values: Yes or No
DbName=IPSHARE
TblName=RL_PP_Hist
DbUsername=cncra/RLIMBU
DbPw=secret



Option Explicit


Const MsgTitle As String = "Wooden Horse Pty Ltd"

Sub Batch_Err_Parser()

'//define constants
Const OldDelim As String = "|"
Const NewDefDelim As String = ","
Const Colon As String = ":"
Const BackSlash As String = "\"

'//define vars
Dim LastRow As Long
Dim Rec As String
Dim TimeStamp As String
Dim Msn As String
Dim RejCode As String
Dim RejMsg As String
Dim FileName As String
Dim DestDir As String
Dim DestFile As String
Dim NewDelim As String
Dim Identifier As String
Dim OrderNotes As String
Dim Status As String
Dim t1 As Double
Dim t2 As Double
Dim Rng As Range
Dim C As Range
Dim Arr As Variant
Dim ColonPos1 As Integer
Dim ColonPos2 As Integer
Dim TotPasses As Integer
Dim TotFails As Integer
Dim RecCtr As Long
Dim DatExists As Boolean
Dim fs As Object
Dim ts As Object
Dim ws As Worksheet
Dim INI_COL As New Collection


On Error GoTo Err_Rtn:

t1 = Timer

'// call initialization rtn
InitRtn

'//ensure that it is a batch error rpt
If Mid(Cells(1, 1), 3, 8) <> "RPT_OPOM" Then
If Not MsgBox("Is this Marketing Batch Error report?", vbYesNo + vbQuestion, MsgTitle) Then
Exit Sub
End If
End If

Set ws = ActiveSheet

'// find the last row
LastRow = ws.Cells(65536, 1).End(xlUp).Row

'// if there is no data, exit
If LastRow = 1 Then
MsgBox "Goodbye!", vbExclamation, MsgTitle
Exit Sub
End If

'// get ini data
If Rtn_Config_Col(INI_COL) <> 0 Then '// 0 = successful func call
Exit Sub
End If

'// read new delim from ini settings
NewDelim = INI_COL.Item("[FILE_SETTINGS].Delimiter")
If Trim(NewDelim) = "" Then
NewDelim = NewDefDelim
End If

'// read identifier
Identifier = INI_COL.Item("[FILE_SETTINGS].Identifier")

'// if headers are turned on, write them to data file
If UCase(INI_COL.Item("[FILE_SETTINGS].Headers")) = "ON" Then
Rec = "service_no" & NewDelim & "status" & NewDelim & "time_stamp" & NewDelim & _
"rej_code" & NewDelim & "rej_descript" & NewDelim & "identifier" & vbCrLf
End If

'// create the data range
Set Rng = ws.Cells(1, 1).Resize(LastRow)

'// loop the cells and parse the records
Application.DisplayStatusBar = True

For Each C In Rng
Status = Trim(Left(C, 4))
If Status = "PASS" Or Status = "FAIL" Then
RecCtr = RecCtr + 1
Application.StatusBar = "Processing record: " & RecCtr
If Status = "PASS" Then
TotPasses = TotPasses + 1
Else
TotFails = TotFails + 1
End If
If Status = "FAIL" Or (Status = "PASS" And UCase(INI_COL.Item("[FILE_SETTINGS].FailOnly")) <> "YES") Then
Arr = Split(C, OldDelim)
Msn = Right(Arr(4), 10)
ColonPos1 = InStr(1, Arr(6), Colon, vbTextCompare) ' find 1st position of colon
ColonPos2 = InStr(ColonPos1 + 1, Arr(6), Colon, vbTextCompare) ' find 2nd position of colon
RejCode = Trim(Mid(Arr(6), ColonPos1 + 1, ColonPos2 - ColonPos1 - 1)) ' extract rej code
RejMsg = Trim(Mid(Arr(6), ColonPos2 + 1, Len(Arr(6)))) ' extract rejection description
OrderNotes = Trim(Arr(7)) 'extract order notes
OrderNotes = Right(OrderNotes, Len(OrderNotes) - InStr(OrderNotes, Colon)) 'extract only aft colon
RejMsg = RejMsg & " " & OrderNotes
RejMsg = Left(RejMsg, 298) ' ensure that rejmsg is not more than 298 characters as db vartext field is 300 only
Rec = Rec & Msn & NewDelim & Arr(0) & NewDelim & TimeStamp & NewDelim & _
RejCode & NewDelim & RejMsg & NewDelim & Identifier & vbCrLf
DatExists = True
End If
ElseIf Status = "DATE" Then
TimeStamp = Right(C, 19)
ElseIf UCase(Left(C, 15)) = "INPUT FILE NAME" Then
FileName = Trim(Right(C, Len(C) - 16))
End If
DoEvents
Next C '// end of main loop

'// if error records exist, write them to a file
If DatExists Then
Application.StatusBar = "Writing to text file ..."
'// read dest folder and file details from ini setting collection
DestDir = INI_COL.Item("[FILE_SETTINGS].DestDir")

'// if there is no slash at the end of destination folder, put one there
If Right$(DestDir, 1) <> BackSlash Then
DestDir = DestDir & BackSlash
End If

DestFile = INI_COL.Item("[FILE_SETTINGS].DestFile")
If UCase(Trim(DestFile)) = "DEFAULT" Or Trim(DestFile) = "" Then '// if no filename has been supplied, name it after error file
If FileName <> "" Then '// ensure error report filename exists
DestFile = FileName
Else
MsgBox "Error: No valid destination file could be found. Exiting ...", vbOKOnly + vbInformation, MsgTitle
Exit Sub
End If
End If

Set fs = CreateObject("Scripting.FileSystemObject")
If fs.FolderExists(DestDir) Then
DestFile = DestDir & DestFile 'prepend dest dir
Set ts = fs.CreateTextFile(DestFile)
ts.Write Rec
ts.Close
Else
MsgBox DestDir & " doesn't exist or not accessible!", vbOKOnly + vbCritical, MsgTitle
Exit Sub
End If

'do clean up
Set fs = Nothing
Set ts = Nothing
Set ws = Nothing

'if the user has elected to write records to db, fastload them
If UCase(INI_COL.Item("[DATABASE_SETTINGS].InsertData")) = "YES" Then
'//ensure all other database setting values are provided
With INI_COL
If .Item("[DATABASE_SETTINGS].DbName") <> "" And .Item("[DATABASE_SETTINGS].TblName") <> "" & _
.Item("[DATABASE_SETTINGS].DbUsername") <> "" And .Item("[DATABASE_SETTINGS].DbPw") <> "" Then
DbRtn Db:=.Item("[DATABASE_SETTINGS].DbName"), Tbl:=.Item("[DATABASE_SETTINGS].TblName"), _
UsrNm:=.Item("[DATABASE_SETTINGS].DbUsername"), Pw:=.Item("[DATABASE_SETTINGS].DbPw"), _
InputFile:=DestFile, TargetDir:=DestDir, Start:=IIf(UCase(.Item("[FILE_SETTINGS].Headers")) = "ON", 2, 1), _
Delim:=NewDelim
Else
MsgBox "Since you didn't provide all required database settings, records were not " & vbCrLf _
& "written to database!", vbInformation + vbOKOnly, MsgTitle
End If
End With

End If

t2 = Timer
Application.StatusBar = "Done!"
MsgBox "Finito! Time taken = " & Format(t2 - t1, "##0.000") & " seconds" & vbCrLf _
& "Total Passes = " & TotPasses & vbCrLf _
& "Total Fails = " & TotFails & vbCrLf _
& "Total Records = " & TotPasses + TotFails, vbInformation + vbOKOnly, MsgTitle

End If
Application.StatusBar = ""
Exit Sub
Err_Rtn:
Application.StatusBar = ""
MsgBox Err.Description, vbInformation + vbOKOnly, MsgTitle
End Sub

Private Function Rtn_Config_Col(ByRef INI_COL As Collection) As Integer
Dim INI_File As String
Dim Rec As String
Dim SecName As String
Dim Kee As String
Dim val As String
Dim SubStrPos As Integer
Dim fs As Object
Dim f As Object
Dim flds As Object
Dim SecCol As New Collection


'// define some constants to make script more userfrieldly
Const READ_ONLY As Integer = 1
Const CommStr As String = "#"
Const Eq As String = "="
Const SecStart As String = "["
Const SecEnd As String = "]"
Const MinValLen As String = 3
Const Blank As String = ""

Set fs = CreateObject("WScript.Shell")
Set flds = fs.SpecialFolders
INI_File = flds("mydocuments") & "\Batch_Errors\Batch_Errors.ini"
Set fs = CreateObject("Scripting.FileSystemObject")

'//ensure ini file exists
If Not fs.FileExists(INI_File) Then
MsgBox "My Documements\Batch_Errors folder " & vbCrLf & _
"or INI file does not exist! Exiting ...", vbCritical + vbOKOnly, MsgTitle
Rtn_Config_Col = -1
End If

Set f = fs.OpenTextFile(INI_File, READ_ONLY)

'//read the ini file

Do Until f.AtEndOfStream

Rec = Trim(f.ReadLine)

'// ignore comments and blank lines
If Left(Rec, 1) <> CommStr And Rec <> Blank Then
'// check to see if there are comments at end of key-value pair
'// if so, take them out of current record
SubStrPos = InStr(2, Rec, CommStr)

If SubStrPos > 0 Then '// end of line comments exists
Rec = Trim(Left(Rec, SubStrPos - 1)) '// eliminate comments
End If

'//check for section name
If Left(Rec, 1) = SecStart Then
If Right(Rec, 1) = SecEnd And Len(Rec) >= MinValLen Then '// ensure it is valid section name
'ensure that there are no duplicate section names
SecName = Rec
On Error Resume Next
SecCol.Add SecName, SecName
If Err <> 0 Then
MsgBox "Error: The INI file has duplicate section names! Exiting ...", vbCritical + vbOKOnly, MsgTitle
Rtn_Config_Col = -1
End If
End If
Else
'// check if it is key-value pair
SubStrPos = InStr(2, Rec, Eq)
If SubStrPos > 0 And Len(Rec) >= MinValLen Then
'// insert key-value pair into collection
'// prepend section name to key name to make it unique
Kee = Left(Rec, SubStrPos - 1)
val = Trim(CleanData(Right(Rec, Len(Rec) - SubStrPos)))
Kee = Trim(CleanData(SecName & "." & Kee))
INI_COL.Add val, CStr(Kee)
End If
End If
End If
Loop
f.Close
Set fs = Nothing
Set flds = Nothing
Rtn_Config_Col = 0
End Function

Private Function CleanData(Data As String) As String
Dim i As Integer
For i = 0 To 31
While InStr(1, Data, Chr(i)) > 0
Data = Replace(Data, Chr(i), " ")
Wend
Next i

For i = 127 To 255
While InStr(1, Data, Chr(i)) > 0
Data = Replace(Data, Chr(i), " ")
Wend
Next i
CleanData = Data
End Function

Private Sub InitRtn()
'// in this routine, ensure that user has
'// Batch_Errors.ini file within Batch_Errors in My Documents folder. If not, create one
'// and inform user
Dim fso As Object
Dim fws As Object
Dim sfld As Object
Dim Rec As String
Dim txt_file_o As Object

Set fso = CreateObject("Scripting.FileSystemObject")
Set fws = CreateObject("WScript.Shell")
Set sfld = fws.SpecialFolders
If Not fso.FolderExists(sfld("mydocuments") & "\Batch_Errors") Then
'// create Batch_Errors folder
Application.StatusBar = "Creating " & sfld("mydocuments") & "\Batch_Errors"
fso.CreateFolder (sfld("mydocuments") & "\Batch_Errors")
Application.StatusBar = "Creating .ini file ..."

'// create ini file
Rec = "# This .ini file was created by batch error parser on " & Format(Now, "YYYY-MM-DD HH:MM:SS") & vbCrLf & "#" & vbCrLf
Rec = Rec & "# Comments start with the hash symbol (#), just like this line" & vbCrLf & "#" & vbCrLf & "#" & vbCrLf
Rec = Rec & "# start of file settings" & vbCrLf & vbCrLf
Rec = Rec & "[FILE_SETTINGS]" & vbCrLf & vbCrLf
Rec = Rec & "DestDir=" & sfld("mydocuments") & "\Batch_Errors" & vbCrLf
Rec = Rec & "Delimiter=, # leaving it blank will choose comma as default delimiter" & vbCrLf
Rec = Rec & "DestFile=default # the destination file name defaults to error report name" & vbCrLf
Rec = Rec & "Headers=On # On or Off" & vbCrLf
Rec = Rec & "Identifier= # write something descriptive to identify service numbers or leave blank" & vbCrLf
Rec = Rec & "FailOnly=No # Yes or No. If Yes, only services that returned rejections will be extracted" & vbCrLf & vbCrLf
Rec = Rec & "# start of database settings" & vbCrLf & vbCrLf
Rec = Rec & "# If you want the error report data to insert into a table, then provide appropriate values " & vbCrLf
Rec = Rec & "# for the following database settings, starting with 'InsertData'." & vbCrLf
Rec = Rec & "# *** Please, note: you need to create the required table as follows" & vbCrLf
Rec = Rec & "# before attempting to load data into it via this utility!" & vbCrLf
Rec = Rec & "#" & String(50, "-") & vbCrLf
Rec = Rec & "# CREATE SET TABLE DB_FOO.TBL_BAR (" & vbCrLf
Rec = Rec & "# service_no VARCHAR(20)" & vbCrLf
Rec = Rec & "# ,status VARCHAR(10)" & vbCrLf
Rec = Rec & "# ,tm_stamp VARCHAR(30)" & vbCrLf
Rec = Rec & "# ,rej_code VARCHAR(10)" & vbCrLf
Rec = Rec & "# ,rej_descript VARCHAR(300)" & vbCrLf
Rec = Rec & "# ,identifier VARCHAR(50)" & vbCrLf
Rec = Rec & "# ,open_dt DATE" & vbCrLf
Rec = Rec & "# ,close_dt VARCHAR(30)" & vbCrLf
Rec = Rec & "# ) PRIMARY INDEX (service_no);" & vbCrLf
Rec = Rec & "#" & String(50, "-") & vbCrLf & vbCrLf
Rec = Rec & "# Since the data get FastLoaded, you need to have FastLoad utility installed on your machine." & vbCrLf & vbCrLf
Rec = Rec & "[DATABASE_SETTINGS]" & vbCrLf & vbCrLf
Rec = Rec & "InsertData=No # Yes or No. To insert data into database, write 'Yes'" & vbCrLf
Rec = Rec & "DbName=xx_my_db # replace 'xx_my_db' with your database name, i.e. IPSHARE" & vbCrLf
Rec = Rec & "TblName=xx_my_tbl # replace 'xx_my_tbl' with your table name" & vbCrLf
Rec = Rec & "DbUsername=cncra/jdoe # replace 'cncra/jdoe' with your data source name/username" & vbCrLf
Rec = Rec & "DbPw=secret # replace 'secret' with your password"

Set txt_file_o = fso.CreateTextFile(sfld("mydocuments") & "\Batch_Errors\Batch_Errors.ini")
txt_file_o.Write Rec
txt_file_o.Close
Set txt_file_o = Nothing
Application.StatusBar = ""
End If

End Sub

Sub DbRtn(Db As String, Tbl As String, UsrNm As String, Pw As String, InputFile As String, TargetDir As String, Start As Integer, Delim As String)
'// this routine fastloads error records into named tables

Dim flScript As String
Dim fso As Object
Dim fo As Object
Dim ScriptInput As String
Dim ScriptOutput As String
Dim ShStatus As Double
Dim BatOut As String


'// start creating fastload script

flScript = "/* " & String(84, "+") & " */" & vbCrLf
flScript = flScript & "/* FASTLOAD SCRIPT CREATED BY BATCH ERROR PARSER */" & vbCrLf
flScript = flScript & "/* ON " & Format(Now, "YYYY-MM-DD hh:mm:ss") & " TO FASTLOAD BATCH ERROR DATA */" & vbCrLf
flScript = flScript & "/* " & String(84, "+") & " */" & vbCrLf
flScript = flScript & vbCrLf & "/* Setup FastLoad parameters */" & vbCrLf & vbCrLf
flScript = flScript & "SESSIONS 50;" & vbCrLf
flScript = flScript & "ERRLIMIT 50;" & vbCrLf
flScript = flScript & "LOGON " & UsrNm & ", " & Pw & ";" & vbCrLf
flScript = flScript & "SHOW VERSIONS;" & vbCrLf
flScript = flScript & "RECORD " & Start & ";" & vbCrLf
flScript = flScript & "SET RECORD VARTEXT """ & Delim & """ DISPLAY_ERRORS NOSTOP;" & vbCrLf & vbCrLf
flScript = flScript & "/* Define text file layout and input file */" & vbCrLf & vbCrLf
flScript = flScript & "DEFINE service_number (VARCHAR(20))" & vbCrLf
flScript = flScript & " ,status (VARCHAR(10))" & vbCrLf
flScript = flScript & " ,tm_stamp (VARCHAR(30))" & vbCrLf
flScript = flScript & " ,rej_code (VARCHAR(10))" & vbCrLf
flScript = flScript & " ,rej_descript (VARCHAR(300))" & vbCrLf
flScript = flScript & " ,identifier (VARCHAR(50)) " & vbCrLf
flScript = flScript & "FILE=" & InputFile & ";" & vbCrLf & vbCrLf
flScript = flScript & "SHOW;" & vbCrLf & vbCrLf
flScript = flScript & "BEGIN LOADING " & Db & "." & Tbl & vbCrLf
flScript = flScript & " ERRORFILES " & Db & ".Batch_Err1, " & Db & ".Batch_Err2" & vbCrLf
flScript = flScript & " CHECKPOINT 500;" & vbCrLf & vbCrLf
flScript = flScript & "INSERT INTO " & Db & "." & Tbl & " VALUES" & vbCrLf
flScript = flScript & " ( :service_number " & vbCrLf
flScript = flScript & " ,:status " & vbCrLf
flScript = flScript & " ,:tm_stamp " & vbCrLf
flScript = flScript & " ,:rej_code " & vbCrLf
flScript = flScript & " ,:rej_descript " & vbCrLf
flScript = flScript & " ,:identifier " & vbCrLf
flScript = flScript & " ,CURRENT_DATE " & vbCrLf
flScript = flScript & " ,'2899-12-31'); " & vbCrLf & vbCrLf
flScript = flScript & "END LOADING;" & vbCrLf
flScript = flScript & "LOGOFF;"

Set fso = CreateObject("Scripting.FileSystemObject")
ScriptInput = TargetDir & "FastLoadThis" & ".src"
ScriptOutput = TargetDir & "FastLoad_" & Format(Date, "YYYY_MM_DD") & ".log"
BatOut = TargetDir & "Batch_Errors.bat"

On Error Resume Next
Kill BatOut
Kill ScriptInput
On Error GoTo 0

Set fo = fso.CreateTextFile(ScriptInput)
fo.Write flScript
fo.Close

'//create bat file
flScript = "FastLoad < """ & ScriptInput & """ > """ & ScriptOutput & """ 2>&1"
Set fo = fso.CreateTextFile(BatOut)
fo.Write flScript
fo.Close

'do clean-up
Set fso = Nothing
Set fo = Nothing

'//start fastload
Call Shell(BatOut)

End Sub