Saturday, December 27, 2008

Bloody weekend

血腥牛扒…






血腥瑪麗…

小小蟲

曲 : 方大同 詞 : 農夫 編曲:方大同

我從小不會說話 總會說些不該說的話 也沒辦法
我知道我是傻瓜 我不是說情話的專家 別怪我
我搞不懂 要不是這條小小的蟲 嚇得你跳進了我的懷中
可不可這樣跟你相擁 我真的很沒用
要感謝這小小蟲給我機會 這是我初次嘗到戀愛滋味
感覺好奇怪 好像 傷害了誰 或許我太壞 把小蟲摔下來 去換你的愛

小蟲也有牠用途 牠用自己摔下的痛苦 換我幸福
也許相愛是這樣 有人快樂就有人受傷 別緊張
像我這樣 那麼平凡一張大眾臉
從來沒人願意多看我一眼 可是在人來人往之間 卻有小蟲還我心願
要感謝這小小蟲給我機會 這是我初次嘗到戀愛滋味
感覺好奇怪 好像 傷害了誰 或許我太壞 把小蟲摔下來 去換你的愛

謝謝你心意 我會好好待你 保護著你 帶你看天地
下次如果看見女孩子 別再猶豫 你趕快跑到她外衣 讓我表現自己 LA LA LA LA
要感謝這小小蟲給我機會 這是我初次嘗到戀愛滋味
感覺好奇怪 好像 傷害了誰 或許我太壞 把小蟲摔下來 去換你的愛

對不起 對不起 對不起 對不起


Sunday, December 7, 2008

My first Facebook application...

... is a PHP program linked back to this blog... =.=...

Sunday, November 9, 2008

Christmas Canon




"Christmas Canon" is a Christmas song by the Trans-Siberian Orchestra from their 1998 album The Christmas Attic. It has debuted on the iTunes Top 100 download chart at #48 during Christmas time.

The song is set to the tune of Johann Pachelbel's Canon in D Major with new lyrics added. The group's rock version is entitled "Christmas Canon Rock", which debuted on their 2004 album The Lost Christmas Eve.

Source: wikipedia

Sunday, November 2, 2008

早餐

這…可以算是早餐嗎…?

Thursday, October 23, 2008

Love Is Dead

Brett Anderson - Love Is Dead



Nothing ever goes right
Nothing really flows in my life
No one really cares if no one ever shares my care
People push by with fear in their eyes in my life

Love is dead, love is dead

The telephone rings, but no one ever thinks to speak to me
The traffic speeds by, but no one's ever stopped too late
Intelligent friends don't care in the end, believe me

Love is dead, love is dead

And plastic people with imaginary smiles
Exchanging secrets at the back of their minds
Plastic people
Plastic people

Nothing ever goes right
Nothing really flows in my life
No one really cares if this horror's inside my head

People push by with fear in their eyes in my life

Love is dead, love is dead
Love is dead, love is dead
Love is dead, love is dead

And all the lies that you've given us
And all the things things that you said

And all the lies that you've given us...
Blow like wind in my head

Saturday, October 4, 2008

是日金句

「失敗原來並非想象中般難過。可能,因為至少有參與過」

Friday, October 3, 2008

Shoot The Moon

Norah Jones - Shoot The Moon



The summer days are gone too soon
You shoot the moon
And miss completely
And now you're left to face the gloom
The empty room that once smelled sweetly
Of all the flowers you plucked if only
You knew the reason
Why you had to each be lonely
Was it just the season?

Now the fall is here again
You can't begin to give in
It's all over

When the snows come rolling through
You're rolling too with some new lover
Will you think of times you've told me
That you knew the reason
Why we had to each be lonely
It was just the season

Wednesday, October 1, 2008

U turn

係真唔係…? o_0

是日晚餐

金菇肥牛 (又係塔斯曼尼亞牛肉 + 日本金菇… d 金菇o個頂真係金色喔~) + 甜燈籠椒 + 大量橄欖油炒烏冬… 又一肥。到。冇。朋。友。之作

faceyourmanga.com

faceyourmanga.com

Monday, September 29, 2008

《畫皮》主題歌

畫心 - 張靚穎




作曲:藤原育郎
作詞:陳少琪

看不穿 是你失落的魂魄
猜不透 是你瞳孔的顏色
一陣風 一場夢 愛如(是)生命般(的)莫測
你的心 到底被甚麼蠱惑

你的輪廓在黑夜之中淹沒
看桃花 開出怎樣的結果
看著你抱著我 目光似(比)月色寂寞
就讓你 在別人懷裡快樂

愛著你 像心跳難觸摸
畫著你 畫不出你的骨骼
記著你的臉色 是我等你的執著

你是我 一首唱不完的歌 (我的心 只願為你而割捨)


『這首名為《畫心》的電影主題曲,由日本音樂人藤原育郎作曲、香港著名詞人陳少琪填詞、華誼音樂旗下藝人張靚穎演唱、莫斯科交響樂團演奏,整首歌曲長度達到七分鐘。這首“超長”主題曲是由電影配樂中的一段主要旋律衍生而來,錶達了電影中主人公的感情世界,旋律幽怨、凄美動人。主題曲不僅是電影情節的延展,而且與這部電影中充滿著神秘東方氣息的畫面也高度統一在一起,達到了音畫合一的效果。漸近式的編曲風格將影片所勾勒出的驚艷凄冷的魔幻風格逐步錶現出來,在配器上使用大量弦樂,配以張靚穎純熟的演唱,讓整首歌聽起來磅礴大氣』

是日今句

「人家都出到聲唔認你係 friend,你都係番入山窿罷喇~」

Friday, September 26, 2008

約定




百聽不厭的一首歌~

Sunday, September 21, 2008

是日金句

「好想好想溫柔相待,只可惜我們就是差那一點點…」

Patching Qmail for remote smtp authentication

My ISP (Netvigator) goes to great lengths to block spam mails sending out from its users. All its users cannot connect to SMTP severs outside. This creates a problem for me as I host my own mail server at home.

Normally, when a mail server tries to deliver an email, it will connect to the destination mail server directly. e.g.

Draft an email to abc@yahoo.com on my PC --->
  my mail server --->
  yahoo's mail server


But as mentioned above, my ISP blocks all the access (port 25) to the outside world. So there is no way for my mail server to connect to other mail servers and deliver the emails.

Luckily, by the SMTP standard, I could force the mails route through my ISP's mail server:

Draft an email to abc@yahoo.com on my PC --->
  my mail server --->
  Netvigator's mail server --->
  yahoo's mail server


So far so good. The remaining problem is: the Netvigator's SMTP server requires authentication which my mail server (Qmail) doesn't support! Thanks to open source, it is easy to patch the code though.


  • First, go get the patch
  • Apply the patch to qmail-remote.c
    patch < qmail-remote-auth.patch

    In my case (on FreeBSD), one of the chunks can't be patched. But don't worry, just open the source to patch it manually!!
  • The patched source needs a base64 function (the authentication for SMTP is in base64 format). Go get it here. Unpack the file and copy base64.c and base64.h to the qmail source folder. Compile it.
    gcc -c base64.c
  • Build the new binary
    make qmail-remote

    Replace the existing qmail-remote (usually under /var/qmail/bin) with the patched version
  • Now, the Qmail SMTP routing file (/var/qmail/control/smtproutes) supports username and password. e.g. To route everything from my mail server to Netvigator's SMTP server:
    :smtp.netvigator.com username password

Saturday, September 6, 2008

挽手袋

「男士應否替女士挽手袋」。一個應該係辯論一世都冇結果o既題目。

早兩日出席左朋友o既婚宴,而當中做伴郎o既係另一位相識o既朋友。只見席間,做伴郎o既朋友一直為伴娘拎住個晚裝手袋。注意: (1) 呢位伴郎朋友乃出名(亦自認)色中餓鬼; (2) 伴郎同伴娘係幫手準備呢場婚宴前,係並不認識的說。 -_-

入番正題。「男士應否替女士挽手袋」。正方代表(通常係女性)會話,咁係男士風度o既表現。就好似男士應該要為女士落車時開車門,落雨時擔遮一樣,好正常喔。 而反方(大男人?)會話男人老狗,拎住個女人手袋通街走係有損尊嚴兼市容喎。

其實,當你的朋友或女伴有需要時,唔理係唔係拎手袋,出手幫忙又有咩唔妥?尤其係當你的女伴要上洗手間、入試身室、或者係季尾減價掃貨時,幫她拎一拎個手袋又何嘗不可?(不過,話說回頭。當你見到女廁門口有一大班男士拎住手袋係到等o既場面,你都咪話唔好笑…)

男士,其實怕o既係一開始幫女士拎手袋,就顯得雄風減弱,驚從此就咁俾個女仔「食住」。

女士呢,其實好多時都唔係要男仔真係幫佢拎手袋。佢地只係想知道個男仔「肯唔肯」拎。到個男仔真係想幫手拎時,反而會話唔洗… -_-

最理想o既情況係:一位男士好禮貌咁問位女士需唔需要幫手挽手袋。女士嫣然一笑咁答:「唔洗喇~」… 咁男o既就顯出左風度,而女o既就可以繼續拎住個成萬銀o既手袋襯番件衫… :P

保險

時: 某星期六,晨咁早
地:馬大夫醫務所
人: 奇雲斯,馬大夫, 及妙齡女護士一名

【幕一開,見阿奇雲斯一條友傻傻咁坐係馬大夫醫務所o既候診室入面。而妙齡女護士則坐係 counter 內, 一面冷漠】

妙齡女護士(忽然大叫): 奇雲斯!入去見醫生!

【阿奇雲斯慌忙跳起,雞手鴨腳走入診証室坐低】

奇雲斯(一面惶恐): 呀馬大夫呀馬大夫… 上個禮拜我去見另一個 eason 時呢,咁呢, 個 eason 建議我呢, 話最好做個小 operation 喎。 咁呢, 所以呢, 走黎俾你睇下,拎個 second opinion 咁囉。

馬大夫(非常淡定): 係呀?等我睇下… (一路檢查,一路自然自語) 唔… 唔… 幾好… 唔… 情況唔係太差呀… 唔… 唔… (失驚無神)呀~ 講開又講,我地o的 eason 最鍾意你呢種病人!你睇你,呢到又大又凸,做起手術上黎話咁易!

奇雲斯: …

馬大夫(越講越興奮): 係o架!拿,係人老左都有機會有毛病o架嘛,你呢到咁大,真係做起手術黎都方便o的o架!

奇雲斯: …

馬大夫(繼續好興奮咁): 拿拿拿!你話喇,個 size 咁大,真係開刀又好,要鑽又好,方便好多o架!再唔係呢…

奇雲斯(開始有o的驚,急忙打斷馬大夫): 咁阿馬大夫,咁我即係洗唔洗做 operation 呀?

馬大夫(忽然回復冷靜淡定): 咁我 check 過,又唔洗即刻做喎。

奇雲斯: …

馬大夫: 拿,你半年之後再番黎 check 下喇!到時再睇睇情況先!

奇雲斯(傻傻地): o下? 哦…咁好喇…

【跟著,阿奇雲斯出番去候診室】

妙齡女護士(面容冷漠): 半年後打電話番黎約時間覆診。 多謝四舊水呀!

奇雲斯: …

【鏡頭忽然一轉…唔知轉左去邊】

朋友甲: 咁咪好囉,唔洗做 operation 喇~

奇雲斯: 馬大夫唔係 panel doc 黎o架!無得 claim 保險o架… 半年後又要再睇呀… 大佬!陰公!

朋友甲: …

Tuesday, September 2, 2008

Gravity



Gravity
John Mayer

Gravity
Is working against me
And gravity
Wants to bring me down

Oh I'll never know
What makes this man
With all the love
That his heart can stand
Dream of ways
To throw it all away

Oh Gravity
Is working against me
And gravity
Wants to bring me down

Oh twice as much
Ain’t twice as good
And can't sustain
Like one half could
It's wanting more
gonna send me to my knees

instrumental

Oh twice as much
Ain’t twice as good
And can't sustain
Like one half could
It's wanting more
gonna send me to my knees

Oh gravity
Stay the hell away from me
Oh gravity
Has taken better men than me
Now how can that be?

Just keep me where the light is
Just keep me where the light is
Just keep me where the light is
C’mon keep me where the light is
C’mon keep me where the light is
C’mon keep me where keep me where the light is

是日金句

「順便話你知…如果你又係準備同我講o的無聊o野,免喇!保重!」

-_-

Monday, September 1, 2008

是日金句

「用眼前o既事抹掉過去o既陰霾」

Saturday, August 23, 2008

death of mini

A few days ago, my ancient mac mini (PowerPC G4) hung during OS upgrade. Can't boot up anymore. Using the original OS X installation disc can't recover the system. The recovery also hang in the middle of installation.

Last resort: crack open the machine and tried to replace the RAM. Actually it is sort of upgrade (from 512M to 1G). Mystically, the re-installation finally worked!

The moral here is... "if anything can go wrong, it will"... -_-... who would suspect the cause was a faulty RAM module... after the machine has been running for many years...

是日晚餐









蒜蓉牛油磨菇 + 塔斯曼尼亞牛仔骨鐵板燒牛肉 + 茄醬通粉


食評:唔咁喉… -_-… orz

Monday, August 18, 2008

褔娃儍了

d褔娃唔知做咩… =_="

Sunday, August 10, 2008

一酒兩食

一個人開支紅酒,飲左兩餐先飲晒。開始發覺紅酒飲到有d悶,可能飲得太密喇…






後記:反而做甜品o既雪糕就一次過 ko 左成盒… :o

屋企有棵小植物。今日發現泥土中突然伸出一株菇形物體… 物似主人形,悶出菇來了… orz

Saturday, August 9, 2008

是日金句

"stop giving in. if you have something to say, just say it. please."

-_-...

Sunday, August 3, 2008

Sun jar 2

A follow-up with the Sun Jar. After I got the jar, i noticed that there are 2 potential improvements: (1) I like it to be brighter; and (2) it doesn't last long enough (only around 5 hours even when fully charged. I prefer it to last the whole night).

Finally have time to disassemble it today and found that the rechargeable battery is only of 700mAh... -.-... no wonder... Replaced it with a 2700mAh battery... will see how it go. But of course, the question is whether the charging circuit efficient enough to fully charge the 2700mAh during the daytime.

(The circuit on the right is my experimental trial on a different approach to test if it can get a better result... both the light intensity and number of hours it can last)

Friday, August 1, 2008

是日金句

「好戲連場!」

Monday, July 28, 2008

joker

屋企附近終於開左間粥鋪。好處係,以後胃痛時可以做「粥er」… o_0…

Sunday, July 27, 2008

活在當下

仲記唔記得,幾時因為有得吃糖果而開心?幾時因為考試成績好而高興?幾時因為看了一部好的電影而體會到百看不厭?幾時因為一頓滋味的晚餐而吃得津津有味?

又記唔記得,幾時為摔了一跤而哭?幾時因為老師的責罰而覺得羞愧?幾時因為朋友的離去而覺得失落?幾時因為工作上的失誤而氣餒?

總有不少回憶吧?不過,好肯定的說,已經淡忘的一定比記得的多。

是的。人無時無刻都有不同的感覺。但多年後,記得的還有幾多?時間始終向前。對過去的愐懷,或對將來的願景,都不及現在的感覺。現在的甜、酸、苦、辣,就是要讓當下的你盡情去感受。

但是,傷心時不要太絕望,高興也不必太自滿。

對現在的感覺和體會,就是成長的一部份吧…

葡萄成熟時

作曲:Vincent chow /anfernee cheung
填詞:黃偉文
編曲:Adrian chan

差不多冬至 一早一晚還是有雨
當初的堅持 現已令你很懷疑 很懷疑
你最尾等到 只有這枯枝

苦戀幾多次 悉心栽種全力灌注
所得竟不如 別個後輩收成時
這一次你真的很介意

* 但見旁人談情何引誘 問到何時葡萄先熟透
你要靜候 再靜候 就算失收 始終要守

# 日後 儘量別教今天的淚白流
留低 擊傷你的石頭 從錯誤裡吸收
也許 豐收 月份尚未到你也得接受
或者要到你將愛釀成醇酒 時機先至熟透

應該怎麼愛 可惜書裡從沒記載 終於摸出來
但歲月卻不回來 不回來 錯過了春天 可會再花開

一千種戀愛 一些需要情淚灌溉
枯毀的溫柔 在最後會長回來
錯的愛 乃必經的配菜

repeat * #

想想天的一邊 亦有個某某 在等候
一心只等葡萄熟透 嚐杯酒

別讓 寂寞害你傷得一夜白頭
仍得不需要的自由 和最耀眼傷口
我知 日後 路上或沒有更美的邂逅
但當你智慧都蘊釀成紅酒
仍可一醉自救 誰都心酸過 那個沒有

死機

本來諗住 reboot 的,可惜來不及了…它已經宣佈「死機」…


不過,可能都係好事黎o既… RIP

做個決定

人越大,反而對好多嘢猶疑左。

上網 search "decision making",有成千上萬個 methodology。有叫你畫圖分析去算盡天機的,有鼓吹憑本能「眨下眼」就做決定嘅,又有啲叫你要深思熟濾嘅… 點都好,做左「十幾廿年」人,理論上應該有番咁上下閱歷,做決定時應該越嚟越成熟兼快手先啱。

但係點解好似係現實世界係剛剛相反?身邊有朋友「一腳踏兩船」,唔知要同「二奶」遠走高飛好,定番轉頭去修保已經千瘡百孔嘅婚姻好。結果拖下拖下,瞞下瞞下,一年又一年咁過。又有朋友成日諗,究竟應該留係間穩定但係悶到發瘟公司等退休好,定係出去轉工搏一搏搵真銀好…話唔定做十年八年之後就可以唔洗撈喎。諗下諗下,不如等出左 bonus 先算喇~ 結果等下等下又過左幾年,年年都係「等出左 bonus 先」。

係人大左,遇到啲問題越嚟越複雜吖,定係經驗越多,越要多啲時間反複去諗?其實,可能只係大家都怕要承擔後果吧。

係人都有試過做錯決定…有時睇番轉頭,會諗 "what if…", 又會諗「如果當初…」等等。後悔得多,就會發現面對新問題時,做決定會越嚟越猶疑。因為,試果做錯的後果,怕怕了。

記得有次同朋友吹水,佢失驚無神爆左句:「x,有咩好後悔…你自己揀嘅!」

就咁聽落去好似冇咩「玄機」,兼有啲冷血。但諗深一層…又係喎。有時啲嘢,真係要「打落門牙和血吞」的。有時左諗又諗,問下朋友意見。啱聽嘅,只係叫就有啲心理安慰,有啲支持咁。唔啱聽嘅,反而攪到自己仲唔知點好。

其實到頭來如果發現做錯左,要承擔後果嘅,又咪係得自己。

人思考,好多時只係因為自己「想」去咁諗。因為想快樂,所以諗好唔好去搵「二奶」。因為想發達,所以諗轉工。咁諗法,齋諗一世都唔會有結果 — 直到你真係去試下先知…

不過…現實真係咁?一個人冇幾多個「十幾廿年」…錯左唔係下下都可以番轉頭…死,又有啲猶疑…

Saturday, July 26, 2008

十字架

十字架

歌手:謝安琪
作曲:周博賢
填詞:周博賢

很久的當年 媽媽天天忠告
好心交給人 總可得到好報
過去按這教導埋頭做
可惜隨年長一步 傷口隨年多一度

伸出手攙扶 遭鬆開手警告
交出心戀愛 反得傷心的控訴
厭棄我過份熱情流露
或是仁慈得恐怖 燙手我已怕碰到

彷彿背上十字架捨我救贖未算好
愈奉獻得到結局愈殘酷
教我為免傷勢再會變更槽 圍牆變更高
圍住要自己的去路 防護罩終變成墳墓
將根本的我葬下去獨自老

伸出手攙扶 遭鬆開手警告
交出心戀愛 反得傷心的控訴
厭棄我過份熱情流露
或是仁慈得恐怖 燙手我已怕碰到

彷彿背上十字架捨我救贖未算好
愈奉獻得到結局愈殘酷
教我為免傷勢再會變更槽 圍牆變更高
圍住要自己的去路 防護罩終變成墳墓
將真的我埋葬下去 哀悼裡獨個漸漸老

多想光陰退後到舊時 童年重度
多斬釘截鐵共處 態度
我對你好所以你會對我好
心裡沒墳墓 無奈這幸福的國度
已飽經災劫無寸草
今天只得我野地裡在獨舞
要怎麼的上路 祈望一天我能知道 

Monday, July 21, 2008

Bypass web proxy with SSH tunneling

For the company that I work in, the network is protected by a firewall and web proxy. We could access the web via the proxy, except those sites that are considered to be "unproductive". Most discussion forums, which are valuable when searching for technical issues, are unfortunately blocked.

Here is how setup a SSH tunnel to bypass the firewall. Basically, it requires to create a SSH connection to a server (e.g. your home pc) outside the company firewall via the company proxy. At the same time, configure the SSH connection to do a port forwarding from your workstation at work to a free proxy server on the Internet (better yet, point it to a proxy server at home).

First, make sure that the SSH daemon on the server has enabled the TCP forwarding option. For OpenSSH, it is the AllowTcpForwarding option.

On the workstation at work, use Putty to connect to the SSH server.




At the Proxy option, set it to use the company proxy to pass through the firewall.



Then the tricky part. Setup a port forwarding rule to forward a port on the workstation to another machine and port on the Internet. Here, this example is forwarding the port 8080 on the workstation to the port 8888 on host proxy.pcathome.com



Click on the Add button to add the rule.



Click Open to connect to the server. Login as usual.

Then change the proxy setting of the browser on the workstation to point it to localhost and port 8080. From now on, web browsing will go through the proxy (for the above example, the proxy.mypcathome.com) outside the company and all restrictions are gone!

Notes:

  1. Since the connection is via a HTTP proxy, connection will timeout when there is not data going through. Either execute a run-forever command on the terminal (e.g. top) or let Putty sends null packets periodically (under the Connection option)

辦公室躲懶術(一)

係辦公室工作咁多年,對於「躲懶」呢門學問點都有d心得。特意係到交流一下,歡迎指點~

不過,話說在前:「躲懶」並不等於唔做野!一日十個八個鐘頭係 office,總唔可能 100% 工作吧?有時點都要上網 check 下 email,去 forum 搵下料,msn 同朋友吹下水…簡單d講:處理下私人事務o者,唔係咁都唔得下話!

言歸正傳,先講下電腦桌面o既「佈陣」。常見初心者會咁樣做:




即係:將一個工作用o既 application 放到最大,然後再開一個小小 window 係到上網或者做自己野。原意係,當發現有咩風吹草動時,可以好快咁用 alt-tab 轉番去後面個 application 到… -_-

但係…大佬~!係「老年」咁遠睇見呢個陣,就算睇唔到你做緊咩,都知你鬼鬼祟祟有d野喇~!試問,當你真係做緊野o既時候,會唔會將背後個 window 放到鬼咁大,反而前面做緊野個 window 縮到睇得個兩行字先?!

計我話,應該要以打亂敵人視線為重點…例如:



用於「躲懶」o既 window 照樣縮小,但係同一時間就開多四五個「疑似」工作 window 亂放係到。咁就算俾人遠遠「目及」到自己個 mon,都冇咁礙眼先啦!只要記得放其中一個「疑似」工作 window 同「躲懶」 window 重疊,咁咪一樣可以用 alt-tab 去救命。 道行再高 d o既,可以 set 到條 active 同 non-active o既 title bar 一樣顏色…咁人地「沖沖一瞥」時都唔知你用緊邊個 window 喇(不過可能到真係做野時攪到自己都唔知用緊邊個… @_@)

Wednesday, July 2, 2008

Writing assembly in Java

Not really assembly language... but byte code instead.

I read an article on dynamic code generation with C# a few days ago and was wondering if similar thing can be done with Java. After some Google searches, the (expected) answer is yes!

The benefit of dynamic code generation is performance, esp. when the logic involved has to be executed many times. This article has a good example on using dynamic programming on a stack-based calculator.

For example, to a stack-based calculator, the expression "2 $0 * $1 /" means:

  • push the value 2 to the stack
  • push parameter 0 to the stack
  • execute the multiple calculation by popping two values from the stack and store the result back to the stack
  • push parameter 1 to the stack
  • execute the divide calculation by popping two values from the stack and store the result back to the stack

That is: (2 * $0) / $1.

Using the plain old Java, it is easy to implement the logic. All we need to do is tokenize the expression and push/pop values from the Stack class for calculation.

......
                char opchar = tok.charAt(0);
                int  op = "+-*/".indexOf(opchar);

                if (op == -1)
                {
                    // not an operator
                    stk.push(Double.valueOf(tok));
                }
                else
                {
                    double arg2 = (stk.pop()).doubleValue();
                    double arg1 = (stk.pop()).doubleValue();

                    switch(op)
                    {
                        case 0:
                            stk.push(arg1 + arg2);
                            break;

                        case 1:
                            stk.push(arg1 - arg2);
                            break;

                        case 2:
                            stk.push(arg1 * arg2);
                            break;

                        case 3:
                            stk.push(arg1 / arg2);
                            break;

                        default:
                            throw new RuntimeException(
                                "unknown parameter(" + i + "): " + tok);
                    }

                }
......


When tested for performance, this implementation can compute the expression "2 $0 * $1 / 1 + $2 -" a million times in less than 3 seconds. Not bad. But it could be improved.

Using dynamic code generation, we can actually create a method (or a Java class) on-the-fly for each expression. This eliminates all the unnecessary testing and branching logic in the loop. For comparison, the above mentioned test can finish in less than 20ms when using dynamic code generation. (yes... finishing the one million calculation in less than twenty millisecond!!).

Here are the details on the dynamic code generation. First, go get the Byte Code Engineering Library (BCEL). It simplifies the way to write byte code logic.

Create a new ClassGen class as the template for the new Calculator
        ClassGen gen = new ClassGen(
            dynClassName,  // fully qualified class name
            "java.lang.Object",  // fully qualified superclass name
            "",  // source file name
            Constants.ACC_PUBLIC | Constants.ACC_SUPER,  // access qualifiers
            new String[]
                {"net.clarenceho.calc.Calculator"}  // implemented interfaces
            );


Then, define methods for the class. We need to define a constant pool and an instruction list of each method. The logic is similar to writing assembly language. We will be pushing/popping values from the operand stack and call arithmetic/logic operations to do the work. e.g.

......
                int op = "+-*/".indexOf(tok.charAt(0));

                switch(op)
                {
                    case -1:
                        // not an operator
                        double val = Double.parseDouble(tok);
                        il.append(new PUSH(cp, val));
                        break;

                    case 0:
                        il.append(InstructionConstants.DADD);
                        break;

                    case 1:
                        il.append(InstructionConstants.DSUB);
                        break;

                    case 2:
                        il.append(InstructionConstants.DMUL);
                        break;

                    case 3:
                        il.append(InstructionConstants.DDIV);
                        break;

                    default:
                        throw new RuntimeException(
                            "unknown parameter: " + tok);
                }
......

il is the instruction list for the method. the DADD, DSUB etc are the arithmetic logic for double values. Refer to the BCEL apidoc for details.

Following the above mentioned article, I also implemented the logic for fun. The only different is that my implementation takes doubles instead of integers. Full source can be found below.


Interface for the Calculator:
package net.clarenceho.calc;

public interface Calculator
{
    double calc(double[] param);
}


Implementation using traditional Java logic:
package net.clarenceho.calc;

import java.util.StringTokenizer;
import java.util.Stack;
import java.util.ArrayList;

public class SimpleCalculator implements Calculator
{
    private ArrayList<String> lst = new ArrayList<String>();

    public SimpleCalculator(String exp)
    {
        StringTokenizer st;
        st = new StringTokenizer(exp, " ");
        while (st.hasMoreTokens())
        {
            lst.add(st.nextToken());
        }
    }

    public double calc(double param[])
    {
        Stack<Double> stk = new Stack<Double>();

        for (int i=0; i < lst.size(); i++)
        {
            String tok = lst.get(i);
            if (tok.startsWith("$"))
            {
                // a parameter
                int pos = Integer.parseInt(tok.substring(1));
                stk.push(param[pos]);
            }
            else
            {
                char opchar = tok.charAt(0);
                int  op = "+-*/".indexOf(opchar);

                if (op == -1)
                {
                    // not an operator
                    stk.push(Double.valueOf(tok));
                }
                else
                {
                    double arg2 = (stk.pop()).doubleValue();
                    double arg1 = (stk.pop()).doubleValue();

                    switch(op)
                    {
                        case 0:
                            stk.push(arg1 + arg2);
                            break;

                        case 1:
                            stk.push(arg1 - arg2);
                            break;

                        case 2:
                            stk.push(arg1 * arg2);
                            break;

                        case 3:
                            stk.push(arg1 / arg2);
                            break;

                        default:
                            throw new RuntimeException(
                                "unknown parameter(" + i + "): " + tok);
                    }

                }
            }
        }

        return (stk.pop()).doubleValue();
    }
}


Implementation using dynamic code generation:
package net.clarenceho.calc;

import java.lang.Thread;
import java.lang.Class;
import java.lang.ClassLoader;
import java.util.StringTokenizer;
import java.util.Stack;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.Type;

public class DynamicCalculator extends ClassLoader implements Calculator
{
    Calculator dynCalc = null;

    public DynamicCalculator(String exp)
    {
        String dynClassName = "net.clarenceho.calc.DynamicCalculator_" +
            Thread.currentThread().getId() + "_" +
            System.currentTimeMillis();

        ClassGen gen = new ClassGen(
            dynClassName,  // fully qualified class name
            "java.lang.Object",  // fully qualified superclass name
            "",  // source file name
            Constants.ACC_PUBLIC | Constants.ACC_SUPER,  // access qualifiers
            new String[]
                {"net.clarenceho.calc.Calculator"}  // implemented interfaces
            );

        // need to manually add an empty constructor
        gen.addEmptyConstructor(Constants.ACC_PUBLIC);
        // add member function to the class;
        this.addMethod(gen, exp);

        // create a new instance of the dynamic class
        byte[] data = gen.getJavaClass().getBytes();
        Class theClass = this.defineClass(dynClassName, data, 0, data.length);
        try
        {
            this.dynCalc = (Calculator)theClass.newInstance();
        }
        catch (Exception e)
        {
            throw new RuntimeException(e.getMessage());
        }
    }


    private void addMethod(ClassGen cgen, String exp)
    {
        // each method has a constant pool and an instruction list
        ConstantPoolGen cp = cgen.getConstantPool();
        InstructionList il = new InstructionList();

        StringTokenizer st = new StringTokenizer(exp, " ");

        while (st.hasMoreTokens())
        {
            String tok = st.nextToken();

            if (tok.startsWith("$"))
            {
                // a parameter
                int pos = Integer.parseInt(tok.substring(1));

                // load and store the array reference to the operand stack
                il.append(InstructionConstants.ALOAD_1);
                // store the array index to the operand stack
                il.append(new PUSH(cp, pos));
                // load and store array item
                il.append(InstructionConstants.DALOAD);
            }
            else
            {
                int op = "+-*/".indexOf(tok.charAt(0));

                switch(op)
                {
                    case -1:
                        // not an operator
                        double val = Double.parseDouble(tok);
                        il.append(new PUSH(cp, val));
                        break;

                    case 0:
                        il.append(InstructionConstants.DADD);
                        break;

                    case 1:
                        il.append(InstructionConstants.DSUB);
                        break;

                    case 2:
                        il.append(InstructionConstants.DMUL);
                        break;

                    case 3:
                        il.append(InstructionConstants.DDIV);
                        break;

                    default:
                        throw new RuntimeException(
                            "unknown parameter: " + tok);
                }
            }
        }

        // return a double
        il.append(InstructionConstants.DRETURN);

        MethodGen mgen = new MethodGen(
            Constants.ACC_PUBLIC,  // access qualifiers
            Type.DOUBLE,  // return type
            new Type[] {Type.getType("[D")},  // argument types
            new String[] {"param"},  // argument names
            "calc",  // name of method
            cgen.getClassName(),  // class name containing this method
            il,  // instruction list associated with this method
            cp  // constant pool
            );

        // compute the max stack size
        mgen.setMaxStack();
        // compute the max number of local variables
        mgen.setMaxLocals();

        cgen.addMethod(mgen.getMethod());
    }

    public double calc(double[] param)
    {
        if (this.dynCalc != null)
        {
            return this.dynCalc.calc(param);
        }
        else
        {
            throw new RuntimeException(
                "problem creating dynamic calculator");
        }
    }
}

Sunday, June 29, 2008

No One - Alicia Keys



I just want you close
Where you can stay forever
You can be sure
That it will only get better

You and me together
Through the days and nights
I don't worry 'cause
Everything's going to be alright
People keep talking they can say what they like
But all i know is everything's going to be alright

No one, no one, no one
Can get in the way of what I'm feeling
No one, no one, no one
Can get in the way of what I feel for you, you, you
Can get in the way of what I feel for you

When the rain is pouring down
And my heart is hurting
You will always be around
This I know for certain

You and me together
Through the days and nights
I don't worry 'cause
Everything's going to be alright
People keep talking they can say what they like
But all i know is everything's going to be alright

No one, no one, no one
Can get in the way of what I'm feeling
No one, no one, no one
Can get in the way of what I feel for you, you, you
Can get in the way of what I feel

I know some people search the world
To find something like what we have
I know people will try try to divide something so real
So till the end of time I'm telling you there ain't no one

No one, no one, no one
Can get in the way of what I'm feeling
No one, no one, no one
Can get in the way of what I feel for you, you, you
Can get in the way of what I feel for you

oh oh oh oh oh oh oh oh oh oh oh oh oh oh oh oh

Sunday, June 22, 2008

反樸歸真?




本來冇諗住又去呢個位到影相,事關呢個位已經影過無數咁多次,有d悶。不過今日實在太好天,兼且又順路,所以帶部傻瓜機去影下。

八、十年前冇咩人會特登去呢個位影,最多只係遊客經過用傻瓜機影下留念。今日上到去,見到幾十人係到等日落影相,仲要九成人都係用DSLR…真係勁!反觀我自己用傻瓜機但係配鬼死咁大枝o既 Gitzo 腳,有d攪笑~




太陽能電筒?



係太陽能燈先真。日間吸收太陽光為電池差電,夜間就著燈發光。

太陽能燈本身冇咩特別,但係佢又好有創意咁張盞燈放係一個半透明 jar 入面…出黎效果就好似捉左d瑩火蟲入去咁…好正!

之前係某網上商店見過有得賣,不過好貴(幾十蚊成本,但係賣成幾百蚊!)想 DIY,不過又懶。早兩日經某網友提起,今日經過某店時終於忍唔住要敗家… orz

Monday, June 2, 2008

Don't shoot! Let them burn!!

為早幾日的「是日金句」解下畫。

事源忽然又諗起 Saving Private Ryan 中,一句我覺得幾震撼的對白…

套戲開場後第一場戰爭場面,就係盟軍登陸諾曼第一役。一幕幕子彈及血肉橫飛的場面。開始時由於德軍有地利,所以盟軍方面傷亡慘重。

Tom Hanks 一隊人幾經辛苦,終於打到高地,攻到德軍的 bunker。為左避免強攻入 bunker 而令到自己一方有傷亡,佢地用當時常用的一招:炸開 bunker 的入口後,係外面用噴火槍向內噴火,將入面o既德軍連人同 bunker 一齊燒左佢。

咁當入面d德軍個個著曬火,係另一面跳出 bunker 碌落山坡時,外面有個盟軍回頭大聲向同袍叫:

   Don't shoot! Let them burn!!

意思即係,唔好開槍殺左呢d德軍,如果唔係就便宜左佢地。由得佢地活生生燒死…

睇完套戲之後,句 "let them burn"久唔久就會係我腦袋中浮出來。

原來憎一樣野時,你係唔會由佢走得咁爽的說。 @_@

Sunday, June 1, 2008

平到冇朋友

之前試用 QuickTime 將高清片轉俾 PS3 用,發覺部舊 notebook 得o個 1G ram 真唔多夠。於是趁今日出城,順手買兩條 1G ram 換左用開o個兩條 512M。 唔洗四舊水攪掂,依家d ram 真係平到冇朋友。不過問題係:就算 upgrade 左 ram,呢部舊 notebook 仲可以玩幾耐?

PS. 現有 2x 512M DDR2-667 SODIMM 多左,有冇邊個親朋戚友有用?

Monday, May 26, 2008

是日金句

"... let them burn"

Sunday, May 25, 2008

Converting Quicktime HD videos for playback on PS3

Apple Movie Trailers

To convert the Quicktime movies:

  1. Get Quicktime Pro. Only it has the function to save the movies on local drives. If you have the registration code, you can turn your Quicktime into Quicktime Pro (Edit > Preferences > Register)
  2. Once you have Quicktime Pro downloaded the movies, choose the File > Export... function
  3. Choose "Movie to MPEG-4" in the Export dropdown box. Then click Options
  4. For File Format, choose "MP4"
  5. For the Video settings, choose "Pass through" if no need to resize the video. Otherwise, choose "H.264". Change the Data Rate (esp if encoding for high resolution). Pick the desired resolution. Suggested to check the "Preserve aspect ratio" and select "Letterbox". Set Frame Rate as "Current". In Video Options, choose "Best Quality (Multi-pass)" to increase the quality (but double the encoding time).
  6. For the Audio settings, choose AAC-LC. Sampling Rate as "Recommended". Encoding Quality as "Best"
  7. Click OK to start the Export. When done, copy the file to the VIDEO folder (under the root directory) on a storage media and plug it into PS3 for playback / copy.

Saturday, May 24, 2008

黑仔

黑仔。即係係大熱天時,為左部老爺吸塵機,四圍咁搵邊到有吸塵袋賣。而終於係某門市部,有位好心售貨員姐姐係倉底搵到 last 一包,買完跟住番到屋企換上新袋後準備吸塵時,部機開唔夠十秒個摩打就打柴~!

Wednesday, May 21, 2008

是日金句

"If doing evil creates good, so be it"

Sunday, May 18, 2008

Firefox 3

Firefox 3 Release Candidate 1 is out now

Notes to self for compiling the source:

  • On my XP machine with VC++ 2005 Express, need to disable the Vista SDK support
    ac_add_options --disable-vista-sdk-requirements

  • Need to remove the /EHsc compiler optimization (it was used to tell compiler to assume extern C functions never throw exceptions)

Wednesday, May 14, 2008

是日金句

「人的前半生不能猶豫,人的後半生不能後悔」

Saturday, May 10, 2008

是日金句

Persephone is Queen of the Dead. Betrayed by Zeus and tricked by her husband Hades.

"I have watched over you pathetic mortals for a thousand years, and it is always the same. Serving yourself before the needs of others has always been your flaw... It is time for all that came before to end."

save game -> 打大佬 -> 死左 -> load game -> 打大佬 -> 死左 -> load game -> 打大佬 -> 死左 -> load game -> 打大佬...... 終於打爆隻 God of War: Chains of Olympus... orz

Wednesday, April 30, 2008

PSP video encoding with FFMPEG

For 4:3

ffmpeg -i in.mp4 -acodec aac -ab 128kb -vcodec mpeg4  -b 1200kb -ar 24000 -mbd 2 -4mv -trell -aic -cmp 2 -subcmp 2 -s 320x240 -r 30000/1001 -title X -f psp J:MP_ROOT100MNV01M4V10001.MP4

Saturday, April 26, 2008

鋸扒

早兩日同朋友去食飯,點了客澳洲和牛「扒」。講真,點時冇期望,食落亦冇驚喜(自我安慰:正常呀~ 幾舊水你想點?)…

不過,其實有隻澳洲牛真係唔錯,要推介下的。呢隻就係「塔斯曼尼亞牛肉」。佢食落去真係有種唔錯o既質感同牛肉味,仲要係夠腍 - 鐘意食十成熟o既都冇問題。最最最重要係唔貴:大概廿零蚊一百克左右。 「拮豬賭」有售~








Saturday, April 5, 2008

typo bug fixing

Found 2 bugs in the blogging software typo that I am using. Both are related to the Atom feed for comments.

First, the theme incorrectly used the name instead of the id to retrieve the article. In views/articles/_article.html.erb, change :

<a href="<%= url_for :controller => 'xml',
 :action=>'feed', :type=>'article',
 :format => 'atom', :id => @article %>">Atom feed</a>.

to:
<a href="<%= url_for :controller => 'xml',
 :action=>'feed', :type=>'article',
 :format => 'atom', :id => @article.id %>">Atom feed</a>.


And in the app itself, the code forgot to check if there is any comment at all before trying to read from it. In app/views/articles/_atom_feed.atom.builder, change:

feed.updated atom_feed.first.updated_at

to:
unless atom_feed.first.blank?
    feed.updated atom_feed.first.updated_at
end

Friday, April 4, 2008

typo correction

Finally a holiday that no need to go to work. Time to fix those broken things.

Recently, this blog started to get attention from spam robots. Time to enable the spam filter. Luckily, the blogging software that I use supports Akismet. Also took this opportunity to upgrade Ruby, Gems, Ruby on Rails, and the blogging software typo... what a better way to spend the whole afternoon... orz...

Still have some minor issues... e.g. the Atom feed is not working etc... will fix them later.

Tuesday, March 18, 2008

是日金句

"If you don't want to tell me everything, that’s fine. Just don't lie to me."

Monday, March 10, 2008

Be Strong

Be Strong - Delta Goodrem



Are you swimming upstream in oceans of blue?
Do you feel like you're sinking?
Are you sick of the rain after all you've been through?
Well I know what you're thinking
When you can't take it
You can make it
Sometime soon I know you'll see

'cause when your in you're darkest hour
And all of the light just fades away
When you're like a single flower whose colours have
  turned to shades of gray
Well hang on and be strong

We're taking each step one day at a time
You can't loose your spirit
Let live and let live forget and forgive
It's all how you see it
And just remember keep it together
Don't you know you're never alone

'cause when you're in your darkest hour
And all of the light just fades away
When you're like a single flower whose colours have
  turned to shades of gray
Well hang on, and be strong

No you're not defeated
And soon you'll be smiling once again
Then you won't have to feel it
Let it go with the wind
Time passes us by
And know that you're allowed to cry

'cause when you're in your darkest hour
And all of the light just fades away
When you're like a single flower whose colours have
  turned to shades of gray
Well hang on and be strong

何處惹塵埃

煩~

上星期尾發現咸水喉入屋附近有水滲出黎。Weekend 叫左屋主上黎睇,拍板要整。隨即 call 師傅上黎。睇完一輪,又鑽左一輪。



最後發現水喉裂o既位置太深,係牆中間,所以要搭棚出去鋸左條喉,成條換。

星期一朝早九點幾兩個搭棚師傅就到左。唔洗一個鐘頭就搭好個棚。勁!








跟手兩個水喉師傅接力。一個係出面,一個係入面。又鋸又鑽,幾個鐘頭之後攪掂。之後等多幾個鐘等o的膠水乾。之後再開番大廈水制試過ok,師傅係六點幾o既時候終於可以收工。





最後成屋都係灰塵,又要抹抹抹抹抹… orz…

Sunday, March 9, 2008

iPod mini mod

The microdrive in my first generation iPod mini finally died a few days ago.

The 1st gen iPod mini utilizes the same Wolfson Microelectronics WM8731L audio chipset as the 3rd gen iPod (which is believed to be the iPod with the best sound quality). The technical details can be found here.

Anyway, many people has tried to mod the 1st gen iPod mini to use flash based card. Now I have a good excuse to try it :P

First thing to do is to find the right compactflash card. The key word to look for here is "ATA compatibility". The speed of the card doesn't matter. The one that I used is from the A-Data Speedy series. Picked up the 16GB model for HKD520.




The rest is easy. Just follow the online tutorial to disassemble the iPod mini and replace the microdrive.











Finally, connect the iPod mini to a computer and use iTunes to restore the firmware. Restart. And iPod mini recognizes the card without any problem.



To do: replace the battery with a higher capacity one and reinstall the Rockbox firmware later to play FLAC.

Monday, March 3, 2008

PIMETA headamp

After reading my DAC post, some friends asked me what exactly is my "PIMETA headamp".

The PIMETA headamp is the box on the left.




Well, first of all, headamp is an amplifier for headphones. It takes the audio signal from the source, amplify it (can be both in terms of voltage and current), and drive the headphone.

This particular headamp that appeared with the DAC is a PIMETA that I built a few years ago. It is simple to built and tune.

Here is a peek inside.



The big blue box is an Alps volume control. The four red cylinders are Elna Cerafine capacitors. The little board on the side with yellow sockets is my DIY voltage regulator to provide clean power to the headamp.

A closer look.



I fitted this headamp with 6 x BUF634. BUF634s are buffers that provide enough current to drive the headphone. They also help to drive the headamp "deeper" into Class-A.

3 x AD843 op-amps provide the voltage amplification. Different op-amps have different sound signature. Op-amp swapping is the easiest way to tune and modify this headamp.

Close up on the op-amps. These 2 are for the left and right channels. The other OPA843 is on another socket for the ground channel.