5/12/2008

InformIT, the new addiction

As of current writing, I've currently purchased 6 ebooks from InformIT, Pearson Education's IT book portal. It's seriously addictive! There're two very good reasons why this is.

  1. Their newer ebooks are non-DRM, straight PDFs. You can view them using any PDF reader; on my computer, this happens to be Evince, but anything else should work. This is much akin to MP3: 100% compatible; just as you can play MP3s on all music players, you can view PDFs on all document viewers.
  2. When you first sign up, you get a 35%-off coupon to spend at their store; every purchase you make, they give you another 35%-off coupon for your next purchase. This incredibly clever marketing technique means that you'll keep coming back to their store when you want more books (at least ones published by Pearson's companies); in many cases, they even undercut Amazon.

So next time you're hunting for a computer book, you know where to go! :-)

30/11/2008

On idealists and idealism

Let me say up front: I'm an idealist, and I'm proud of it. It's a central part of my identity, and I'd be totally lost without it. Idealism, to me, is believing in doing something because you should, because it's right, not because it's expedient or pragmatic. Sure, practical concerns do affect our daily lives—for example, we can't spend more money than we have—but where we have a choice in what we do, idealism means that we do what's right for everyone around us, for the world around us, for the bigger picture.

It's nice to serve ourselves, and we certainly shouldn't help others at the total expense of our wellbeing, but the opposite is (in my idealist eyes) far worse. In that picture, everyone is just out to serve themselves, with no regard for others; they'd slay their own parents if it meant they'd get their inheritance a few years sooner, for example, or their spouse so they can fraudulently cash in on their life insurance.

The latter is not a world I want to live in. I've already seen too much evil in my years on this earth, and it's burdened me with a fair sense of cynicism. However, I refuse to let that cause me to allow the world to get any worse. No matter how much of a dead end we feel we live in, we must strive to make things better, in whatever way we can. No amount is too little, as long as we can honestly say we did our best.

It is for that reason that I have voted (since I was 18), and continue to vote, for the Green Party, that I support Richard Stallman and Dennis Kucinich, and why I'm married to The Ferret, who, though not as high-profile as the people I just named, is a shining example of how idealists should live their lives.

It is also the reason why I speak up for causes I care about. One of the things I care about is that companies should get their priorities straight, which is to serve customers first, and not just as a means to profit. In fact, I personally believe that running a business just to profit (with other reasons being subservient to that) is dubious; it says you've sold your soul to money. Profit should be a means to other (hopefully more ethical) ends, and not the end to which other considerations are merely means. The profit-as-end companies are those that will gladly fire their most talented staff if it were expedient for them to do so, such as if said people started whistle-blowing. Clearly, that sort of company would be a poor place to work for if you value social justice.


Recently I had a run-in with some misguided people who have their priorities the wrong way around. One, I believe, even accused me of “shit-stirring”. (Thankfully, since they're posting anonymously, they can't go and redact their comment, and I certainly have no inclination to remove evidence of people's, umm, ‘logical slip-ups’.) I believe, in that poster's eyes, any kind of ‘heretical’ ideas (such as the Earth being round, or that evolution created the earth's many varied lifeforms, to name two historical examples) are shit-stirring. Well, in that case, I'm glad to be among that crowd.

Shit-stirring is how the world progresses! How on earth do you expect our society to give women the right to vote, to abolish segregation, to legalise abortion and (in New Zealand) prostitution—and hopefully soon, gay marriage and cannabis—if there weren't people who spoke up about it?! If people don't put their foot down on things they care about, how does this world improve? By sitting on our arses and excusing companies' and governments' misbehaviour? Surely you jest!

Also, just because a company ‘invented’ a technology does not give them the (ethical) right to piss on others, whether others be their customers, competitors, or anyone else. Even if the law doesn't require it, we all owe it to the society and world around us to be better than self-serving. Much, much better.

But since we're talking about innovation, who do you think invented Firefox, or BitTorrent, or other such great technological wonders of the Internet age? Is it a certain company in Cupertino? Bzzzt! The world does not revolve around them, contrary to the apparent opinion of some of my respondents.

As we all know, they're inventions of some of the best free software hackers in this world. Likewise with many other pieces of free software that's in daily use, all around the world, created by people who believe, one way or another, that giving their users freedom is the right thing to do. Off the top of my head, I can name: Emacs, Apache, TeX (and LaTeX), PHP, Ruby (and Rails), Java (although it's had a funny kind of history, in the free software world), and Mozilla (the progenitor of Firefox). These are all original works, not clones of proprietary software.

Luckily for proprietary software supporters (and social justice in general), free software does not allow discrimination against certain groups of users, and they can all benefit from these works. I wish I could say the same of non-free software.

So, am I shit-stirring enough yet? Glad to be of service!

29/11/2008

A curry a day keeps the doctor away

As I'm writing this post, I'm very sniffly and otherwise not doing very well. And I'm reminded of my usual cure for such ails: spicy foods! I find that spices make me sweat all the nasties away, and I end up feeling much better the next day.

I especially love hot Indian curries, such as vindaloo. Although in New Zealand it's customary to serve vindaloo with beef, I find that I like lamb vindaloo a lot. There's also an Indian place around here that serves an item they call goat curry; it's one of my favourite dishes, especially if I order it ‘Indian hot’ (as opposed to ‘Kiwi hot’, which is more like medium heat).

Right now, though, just past midnight, there are no Indian restaurants open, so I have my next best fix, which is instant Korean noodles. That's one thing I love about Korean food: it's spicier than most other kinds of East Asian cuisine (except perhaps Sichuan, which I also love). You've got to hand it to those northeners; the southern cuisines (e.g., Cantonese) tend not to be nearly as spicy. I know I'm making huge generalisations, but in terms of Asian foods available in an English-speaking country, I dare say I'm not too far off the mark.

I'm just now reading about the phall, which I've not seen in New Zealand, but which I'd love to try! Bring it on! :-)


Well, this is it! I've come to the end of NaBloPoMo, with a post a day! Sure, some posts were more content-free than usual, and some, well, rather controversial, but at long last, it's done! I have one more post in my blog topic queue, which I might post tomorrow if I flesh it out by then, but in any case hopefully sooner rather than later; after that, I don't suppose I'll have anything to blog about for a while. I don't intend to do NaBloPoMo for December.

28/11/2008

The law of gravity

Whether you're tramping up a hill, or kicking a ball, or whatever, one of the first things you learn is that things come down much more easily than they go up. That's gravity for you!

I've come to appreciate exactly the same phenomenon for rechargeable batteries. Today, I've been setting up a brand new UPS to power three laptops and a printer; I've read from HP support that it's best not to leave a laptop battery in the laptop if you're running your laptop for long periods of time, so, in order to do that and still have some protection against power outages, I felt that a UPS is the best option (not to mention that it helps protect against power surges, which have killed more of my computers than I want to think about).

Anyway, as a test of the UPS-monitoring interface (if you're an Ubuntu user, the nut-hal-driver package is all you need, if your UPS has a USB connector like mine does), I decided to unplug the mains for a while, and see how long things would run. The system estimated about 20 minutes of run time. When the UPS got to about 70% charge (after about 5 minutes), I plugged it back in. As of current writing, it's been charging for forty minutes now, and it estimates taking another twenty minutes to finish charging.

Yeah, you definitely don't want to be having power outages too often, UPS or no. :-)

Update: it's now been charging 75 minutes now, and still estimates 20 minutes to finish charging. This is starting to look like software development schedule estimates! :-P

The Simpsons says it best

I was trying to come up with a topic to blog about today, when a friend kindly posted a link on his Facebook profile about the shenanigans Apple has been getting up to lately, what with trying to lock all iPod users to using iTunes, by threating lawsuits against hosting companies whose clients include makers of any software that's iPod-compatible. My vendetta is made rather more personal because one such maker is a friend of mine, and his software, El Tunes, has been targeted by Apple's hitmen too.

I see too many software geeks using MacBooks and other Apple gear, and in so doing elevated them to a geekier status than they deserve. (Apple products, geeky? Surely you jest!) That must stop! It's trendy in geek culture to trash Microsoft and wish for its downfall; I feel that the same trendiness in trashing Apple must come forth in the geek world too. I'm no fan of Microsoft (I'm primarily a Unix coder, after all), but it's quite something for me to hate Apple even more than I hate Microsoft.

To whatever degree I (or my real-life persona :-)) will gain some voice in the geek world in future, I will not hesitate to give Apple some of the same ‘love’ that Microsoft has been getting, nor will I be anything less than encouraging to other geeks who wish to spread said ‘love’. Let me start by recommending, if you're on Facebook, the I hate Apple group.

Yes, this is a declaration of war: they deserve it, every little bit.

Update: Getting swarmed by fanboys! Oh noes! :-P

Update: The Simpsons episode with the Mapple store is totally hilarious, says what I've wanted to say much better than I ever can. :-) And, what's funnier, the way some ‘Mapple’ fans make fools of themselves on this thread are priceless! (And not very much different from the ones on this thread. :-P)

27/11/2008

Serendipity? (Part 4)

Some readers may know of my recent fun getting my laptop, and getting it up and going. I've been using the laptop some more now, and I'm finding more and more things to like about it and its setup. It's a really neat laptop, and I consider it quite a bargain.

I finally got around to setting up Ubuntu 8.10 on it. Despite my struggles with its installer, I now have a fairly ‘standard’ crypto setup, wherein hibernation actually works (after a couple of false starts). This is a biggie to me: I run my last laptop 24/7 because there isn't a good way to hibernate it, and this is not good for the laptop's longevity. Heck, my last laptop is only 11 months old, and it looks twice its age.

What I mean by ‘standard’ crypto setup is one that's quite similar to that created by the Ubuntu installer: namely, a big crypto partition that houses an LVM physical volume, within which you set up all your logical volumes. This is quite an improvement on the old, hacky way I set up crypto on my laptop, which was to set up a big LVM physical volume, with each logical volume encrypted separately. The old setup is bad because that means that the LVM data structures are unencrypted, and also that the logical volumes are not transparent: you'd have to know to decrypt them on each use. Evidently, too, it is also bad enough to prevent hibernation from working.

Both my old and new laptops have nVidia graphics cards in them, though the old one has a (apparently) dinky GeForce Go 6100, whereas the new one has a GeForce 8400M GS. This means that while the old laptop can't really do a very good job of using Compiz (at least without freezing the computer on occasion), the new one just breezes through it.

I've installed VMware Workstation 6.5 on the new laptop (since getting 6.0.5 to work on Ubuntu 8.10 is a huge pain, you'd have to hack vmmon to make it work with 2.6.27 kernels), and it's neat (even though I detest having to use their installer). I can run two virtual machines simultaneously without having one stall the other excessively (as has happened with the old laptop); this is a big deal given that:

  1. They both use a fair amount of memory (2 GB and 1 GB respectively)
  2. I run them with all memory allocated in host memory; this is because the virtual machines are fairly disk-intensive and I don't need VMware's memory swapping to add to that load
  3. Both laptops has just 4 GB of RAM (since it only has two DIMM slots, and 2 GB DIMMs are the biggest affordable type you can get; 4 GB DIMMs are hugely expensive)

Oh, but on using virtual machines, the most useful thing about the new laptop is that the wireless card (Intel PRO/Wireless 4965, as opposed to the old laptop's Broadcom BCM4311) supports promiscuous mode. This means I can have guests use bridge mode networking; previously, I'd have to set them all up to use NAT mode.

So yes, that's my initial impression after a day of using the new setup on the new laptop. So far, I must say I'm very impressed; I look forward to more pleasant surprises! :-)

26/11/2008

Buffets and eating habits

What is it with people who order more food than they can eat? Now, I understand that for à la carte dining, you can simply take your food home and eat it later (although actually remembering to eat the leftovers is another story). But you're not really supposed to take buffet food home (at least without paying more for it). Some buffet places will charge you more if you leave food behind; I think it's a great idea. People should not be encouraged to waste food in general, but when it's at the restaurant's expense, this should be doubly discouraged.

My point about eating what you've ordered is not about stuffing food down your gullet and gaining 50 kg in the process. I don't advocate overeating in general, and definitely not in a buffet context (I say this as someone who had overeaten at buffets too often in the past). The idea of a buffet is that you can choose how much to put on your plate. Most good buffets let you go on multiple trips. Make use of it!

By which, I don't mean stuff your plate on the first go, then the second, then the third, and so on (unless you actually want to eat that much food, and can fit it all in). I mean, put a little on your plate each time, and top up while you're still hungry.


I'm now going to go off on a rant about parents who make kids eat everything on their plate, and give them huge complexes about it. This is, in my rather arrogant opinion, a terrible habit to teach kids. Kids should learn not to waste food, yes, but the lesson they should learn is to not bite off more they can chew (and in fact, when they do order more than they can eat, they should learn to keep the food overnight to eat another day), rather than that they should eat everything, as such. The latter approach will make them overeat, then discover that they're getting much, much fatter than they want to be, and (later on in life) start leaving tons of food on their plates to compensate for the emotional trauma they've gone through.

Remember, less is more. There, I'm done ranting. :-)

25/11/2008

In JIT we trust, part 2

One of the things I love about dynamic compilation is that it frees me from having to think about the minute details of how some piece of code will be compiled. In fact, the effects of hand-written ‘optimisations’ are less predictable than one might expect!

Take this simple example: which one do you prefer between these two?

  1. for (i = 0; i < array.length; ++i)
  2. for (i = 0, len = array.length; i < len; ++i)

I've been guilty, for a long time, of picking the second one, on the premise that if the length operator didn't have to be called all the time, it'd be faster.

That's the biggest pack of lies ever.


The punchline is that both are about the same in terms of performance, counter-intuitive as that may be. I wrote a little test program, which I tested on my system (Java SE 6, update 10, 64-bit server VM). All it does is print all the command-line arguments to System.out, a million times.

My code tests this loop under a number of different execution scenarios: checking the array boundary the ‘naive’ way (i.e., checking the array length at each run); hoisting the array length and checking that; using for-each (aka extended for loops); as well as hoisting System.out for each of those test types. There's also a ‘super hoist’ mode that does all the above hoisting, as well as doing the million-run loop by hand rather than using a range iterator (see below).

To minimise other system effects, I wrote a null output stream (which acts like /dev/null, without actually opening any device), a range iterator (that always returns null, rather than the iteration count, to avoid boxing overhead), and a no-op loop (just to see how much overhead the loop itself is creating). To minimise warm-up effect, the whole suite is run three times.


Here are some test results for a run with 25 empty arguments. I picked empty arguments to minimise any string-copying overhead that may exist; after all, the point here is to test the various array looping techniques, not string copying!

Timing results (all times in seconds)
Loop typeRun 1Run 2Run 3
Naive11.33810.45510.460
Length-hoisted11.78611.51911.299
For-each11.52111.51711.141
Out-hoisted naive11.41911.46511.184
Out-hoisted length-hoisted11.51411.47511.168
Out-hoisted for-each11.73011.51911.512
‘Super hoist’11.61411.53912.045

If anything could be considered ‘faster’, I'd have to say it's the naive loop! However, the differences are too minute to be meaningful.


Code listing:


import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class PrintArgs {
    static final int RUNS = 1000000;

    public static void main(String[] args) {
        System.setOut(new PrintStream(new NullOutputStream()));
        runSuite(args);
        runSuite(args);
        runSuite(args);
    }

    private static void runSuite(String[] args) {
        time(new NoopLoop(args));
        time(new NaiveLoop(args));
        time(new NoopLoop(args));
        time(new LengthHoistedLoop(args));
        time(new NoopLoop(args));
        time(new ForEachLoop(args));
        time(new NoopLoop(args));
        time(new OHNaiveLoop(args));
        time(new NoopLoop(args));
        time(new OHLengthHoistedLoop(args));
        time(new NoopLoop(args));
        time(new OHForEachLoop(args));
        time(new NoopLoop(args));
        time(new SuperHoistLoop(args));
        System.err.println();
    }

    private static void time(Runnable runnable) {
        long t0 = System.currentTimeMillis();
        runnable.run();
        long t1 = System.currentTimeMillis();
        System.err.printf("%s: %d ms%n", runnable.getClass().getName(),
                t1 - t0);
    }

    static class NullOutputStream extends OutputStream {
        public NullOutputStream() {}

        @Override
        public void write(int b) {}

        @Override
        public void write(byte[] b) {}

        @Override
        public void write(byte[] b, int off, int len) {}
    }

    static class Range implements Iterable<Void> {
        private final int start, end;

        public Range(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public Range(int end) {
            this(0, end);
        }

        @Override
        public Iterator<Void> iterator() {
            return new RangeIterator(start, end);
        }
    }

    static class RangeIterator implements Iterator<Void> {
        private int start;
        private final int end;

        public RangeIterator(int start, int end) {
            if (start > end)
                throw new IllegalArgumentException();
            this.start = start;
            this.end = end;
        }

        @Override
        public boolean hasNext() {
            return start != end;
        }

        @Override
        public Void next() {
            if (start == end)
                throw new NoSuchElementException();
            ++start;
            return null;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    static abstract class MainRunnable implements Runnable {
        private final String[] args;

        protected MainRunnable(String[] args) {
            this.args = args;
        }

        @Override
        public void run() {
            for (Object _ : new Range(RUNS))
                main(args);
        }

        protected abstract void main(String[] args);
    }

    static class NoopLoop extends MainRunnable {
        public NoopLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {}
    }

    static class NaiveLoop extends MainRunnable {
        public NaiveLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            for (int i = 0; i < args.length; ++i)
                System.out.println(args[i]);
        }
    }

    static class LengthHoistedLoop extends MainRunnable {
        public LengthHoistedLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            int len = args.length;
            for (int i = 0; i < len; ++i)
                System.out.println(args[i]);
        }
    }

    static class ForEachLoop extends MainRunnable {
        public ForEachLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            for (String arg : args)
                System.out.println(arg);
        }
    }

    static class OHNaiveLoop extends MainRunnable {
        private final PrintStream out;

        public OHNaiveLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            for (int i = 0; i < args.length; ++i)
                out.println(args[i]);
        }
    }

    static class OHLengthHoistedLoop extends MainRunnable {
        private final PrintStream out;

        public OHLengthHoistedLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            int len = args.length;
            for (int i = 0; i < len; ++i)
                out.println(args[i]);
        }
    }

    static class OHForEachLoop extends MainRunnable {
        private final PrintStream out;

        public OHForEachLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            for (String arg : args)
                out.println(arg);
        }
    }

    static class SuperHoistLoop implements Runnable {
        private final String[] args;

        public SuperHoistLoop(String[] args) {
            this.args = args;
        }

        @Override
        public void run() {
            PrintStream out = System.out;
            for (int i = 0; i < RUNS; ++i) {
                for (String arg : args)
                    out.println(arg);
            }
        }
    }
}

24/11/2008

Serendipity? (Part 3)

Remember how I said that the DVD drive on my laptop was busted and that I'd follow up on it sometime? Well, the main reason it bugged me was that I wasn't able to burn a clean copy of my recovery discs, which obviously is a huge concern of mine should I need to bring the system back to a factory-original state.

So, I ordered a set of recovery discs ($30—it would have been free under warranty if I could provably demonstrate that my DVD drive is busted, but as I mentioned in my last post, that's not easy, and I figured $30 is a small price to pay for peace of mind), and received them today. There are two discs: a System Recovery DVD, and an Application and Driver Recovery CD (the latter has 10 MB of content).

As opposed to normal recovery discs, which are simply a snapshot of the system at initial installation, these discs contain just program files. As such, it has to install everything from scratch, and is, in my case, a five-hour-long recovery process. But, at the end of this process, a program called PC Angel creates a snapshot of the entire system image into the recovery partition for quick recovery; once that is all done, I have a laptop setup that is identical to how I expected it! Colour me impressed!

23/11/2008

Short and sweet

I just want to say that WCPE is still my favourite station! They have an amazing selection of music, and is the perfect coding companion (yes, I've been too busy coding to write a real blog post)! If I ever move to the Raleigh, NC area, I look forward to listening to them all day long. :-)

(Note to self: fulfil the pledge I sent in earlier this month!)

22/11/2008

Serendipity? (Part 2)

Remember the new laptop I got, the ex-demo one that sold at $100 discount? It's mostly good, but the DVD drive is busted. I have some theories as to why, but won't speculate.

I called up HP about it, but the service people there aren't particularly happy to look at it if it “works” (which it does, but there are intermittent read failures, and the speed of disc access is atrocious). At the moment, I'm writing a program to demonstrate the bustedness of the DVD drive in a more reproducible way. Hopefully, once that's done, I'll have much more grounds to send the laptop for warranty servicing.

I'll post the code in a future post, if it does do what I intend….

21/11/2008

Syntactic sugar makes the world go 'round

For a long time, I considered Java the C of the Java platform. It's pretty much the lowest-level language you can write code with, unless you're using assembly language (and I remember playing with Jasmin in the early days of Java).

Back in early days, the Java language has very little in the way of syntactic sugar, much like C, except that in C you have a preprocessor, whereas in Java, you had to hack your own (just look at the OpenJDK source code for ample examples of custom preprocessing tools in use). So all the code is ultra-verbose, and (to me) there was a huge mental barrier to writing code of any size, just because I had to spell everything out, every time. (Just don't get me started on the lack of typedef in Java.)

With what syntactic sugar there was, it was too easy to encourage programmers to Do The Wrong Thing™: the availability of operator += for strings meant that a lot of programmers tried doing string-building “the C++ way”, never knowing the difference in semantics between += in C++ versus Java. My feeling on this is that += should not be defined for strings, only on StringBuilders and StringBuffers, with the same effect as calling append() but without the function call syntax.

Of course, once we go down that road, perhaps we would need to provide operator overloading across the board. That, of course, leaves me asking why we don't just use C++ in the first place, perhaps doing a variation of C++/CLI for the Java platform. Oh wait, the Java platform isn't as accommodating as .NET is non-Java concepts; I just can't imagine how, say, the STL would be implemented on the Java platform.


As of Java 5, there were certainly a lot more syntactic sugar for things that were commonly done ‘the hard way’ in the past; type-safe enumerations and generics are two that come to mind, not to mention ‘real’ for loops and variadic functions. And soon (perhaps by Java 7, perhaps not), there'll be closures, which I also welcome.

After all these changes, would Java then be seen as the C++ of the Java platform? (Sorry, this is not meant as a slight to C++; I know that Java does not even come close to approaching the power of C++, but it's the best analogy I can come up with.) Is this actually worth pursuing, or should we all start flocking to more powerful languages, perhaps to Groovy?

I singled out Groovy for one reason: it was a language designed for the Java platform, and has the greatest chance of fitting snugly within the Java platform. Of course, there are things it needs to implement, like annotations, but all in good time. My feeling is that, be it Groovy or some other new language, it has to be a language that allows you to express anything you can in Java; and it has to also allow more powerful ways of expressing them, as well as things that cannot easily be expressed in Java.

I would love nothing more than for programmers to write most of their code in a high-level language, with the bottlenecks separated out into a separate module, which can be written in a lower-level language like Java, for performance. But to get people to switch, that high-level language had better be good to use, and not compromise on features that are available in the Java language.


So, the whole point of that little ramble is that I do think there needs to be a ‘better Java’ for Java programmers. Everything that's easy in Java should stay easy, and anything that's commonly done in Java (and programming in general) but is hard should also be made easy, to further encourage their use. Also, it should link to Java in a seamless way; it should be easy to incrementally convert a project to this new language, file by file.

The language can use either static typing (provided there's good type inference so programmers don't have to spell out types all the time) or dynamic typing; I don't care either way, especially given developments in Da Vinci Machine that will give dynamic languages much better support on the Java platform. I have a feeling, though, that experienced Java programmers will continue to prefer static typing.

The language I envision does not need to be backwards-compatible with Java; for example, the whole insane syntax for anonymous classes can be done away with, especially in one-method cases. It also does not need to function in any platform other than the Java platform. I don't really care about portability to unmanaged platforms or to .NET or to Parrot or to anything else.

Really, like I said, I'm just looking for a ‘better Java’ that's good for most present Java programmers to switch to. And, given the current wide(ish) adoption of Groovy, this ideal language would probably be implemented as a future version of Groovy.

20/11/2008

Foodstalgia

I know that's not a word, but whatever. Right now, The Ferret and I are trying to decide what to eat for dinner, and it just reminds me of all the foods I loved and can now no longer find. What about you? What foods do you have fond memories of, that you can't get anymore? Are you as devastated about it as I am?

My current list is as follows (these are all New Zealand-specific, sorry to foreign readers!):

  1. Georgie Pie (both their pies and their fish'n'chips)
  2. BK Big Fish
  3. Chocolate Frosty
  4. Cherry Coke
  5. Weis ice cream bars
  6. Mooloo ice cream

Sad yet? I sure am….

19/11/2008

Even expert mode ain't expert enough

Some diehard geeks I know consider Ubuntu to be too softcore for their liking. However, it seems a reasonably good compromise to me: I get new versions every six months, instead every 3 or so years (Debian stable), or every week or so (Debian testing). I've used Ubuntu for three years now, and am quite happy with it. (Prior to that, of course, I was a diehard Debian user. I can't bring myself to use any Red Hat-derived system; it's far too different from the Debian way of things.)

Anyway, control-freak Ubuntu users know to use the alternate (as opposed to the live desktop CD) for setting up their systems, as well as the picking the expert mode option; this allows the user to make decisions on a much larger variety of options than the system usually bothers the user with. Still, there are things that I wish the installer allows me the choice of, so I don't have to fight it so hard.

  • When encrypting my hard drive, I use the cipher specification aes-xts-plain or twofish-xts-plain. But the best choice of cipher mode that the Ubuntu installer allows (as of 8.10) is CBC. That is a long, long way off from XTS!

    So instead, I get to rerun cryptsetup luksFormat after the installer creates the CBC-mode partition (if the installer doesn't get to create the partition first, it gets seriously confused), just so I can have XTS mode. Great!

  • The whole installer uses decimal gigabytes (10⁹ bytes) instead of binary gigabytes (2³⁰ bytes), which is quite frustrating to a programmer who's used to the latter. If I want a 10 (binary) gigabyte partition, I should be able to type 10 GiB, and not be told “Invalid size” and made to type 10.73741824 GB instead.

    So instead, I get to create all my partitions by hand, running fdisk and lvcreate manually. Great!

  • The installer does not let you pick your uid/gid, always using 1000. So instead, I get to edit that afterwards:

    
    sed -i s/1000/$UID/g /target/etc/passwd
    sed -i s/1000/$GID/g /target/etc/group
    chown -R $UID:$GID /target/home/$USER
    

    Great!

18/11/2008

Sometimes I wish I were a cop…

…just so I could bust all the bad drivers out there. The amount of bad driving I see, especially in Auckland, is phenomenal! By far, my biggest peeve on the road is with people who are too lazy to signal. It's not that hard to flick your signalling stick—especially when you're leaving a roundabout!

That little ‘courtesy’ (it's a legal requirement, but a vast number of Jafas seem to be wilfully ‘ignorant’ of that) means that people can move through roundabouts much faster—as soon as one signals out of the roundabout, other drivers can enter it straight away, safe in the knowledge that one isn't going to end up in their path (unless one is stupid and changes one's mind about leaving the roundabout—but that's another rant for another day).

16/11/2008

Item 52: Refer to objects by their interfaces

(A side related note first: I still need to get the 2nd edition of Effective Java. The 1st edition copy I have is very well-read, though! Highly recommended for all Java programmers. Edit: I did buy the book in the end; InformIT sells non-DRM PDF copies, so yay!)

Anyway, this is a short rant about one of my personal peeves. To put it succinctly, I'll borrow a line out of Effective Java: Item 52: Refer to objects by their interfaces. I believe this so strongly that I've used that as the post title too. :-)

Let's motivate with an example:


Vector<Number> getPrimes(Number from, Number to);
Collection<Number> getPrimes(Number from, Number to);

Which one of the two would you prefer for your interface? Without reservation, I'd prefer the second one, because it doesn't name a concrete return type, thus giving the implementation more flexibility; for example, instead of having to create a Vector, you could use sets, singletons (from Collections), arrays (with a List view created by Arrays.asList), or even a custom type, as long as it fulfils the Collection interface. Clients that want to use Vector could simply construct one from the returned collection.

The same applies to incoming parameters in an interface too. For example:


Number sum(Vector<Number> numbers);
Number sum(Collection<Number> numbers);

Which one do you prefer here? (It could be argued that List should be preferred to Collection if the order of the numbers is significant; however in this example of summing, addition is commutative so this doesn't matter.) To use the first signature would put a burden on your interface's clients; there is no easy construction syntax for making a Vector, whereas on the other hand there are easy ways to express creation of lists (Arrays.asList is variadic), singletons, etc.

In summary, where there are interface types you can use for incoming parameters and return values, you should definitely prefer them. Concrete types are for instantiation only, or perhaps for use in legacy hierarchies that don't have interface types. (For instance, I consider it totally broken that Reader and Writer are abstract classes instead of interfaces.)

Serendipity? (Part 1)

Last night I was looking at the Dick Smith website, looking for a special on a laptop I could use with hardware virtualisation (this is not just a performance issue—my work is done in a 64-bit virtual machine, and it's impossible to run 64-bit guests without HV—on VMware, at least for Intel processors; and on VirtualBox, across the board).

I found one, the HP Pavilion dv5-1004ax, which, being an AMD system, would fit the bill just fine. Only one problem: they're very short on stock on them, and they're not really generally available anymore. But the Sylvia Park branch apparently had 3 available this morning (which means, in practical terms, at most one new one; the others are demos and returns). The salesperson on the phone said he'd hold it for me, so I thought I'd go there and see.


When I turned up, the salesperson asked me to wait while he fetched the laptop for me. When he came back, about a quarter of an hour later (just how long does it take to retrieve a unit?!), I noticed that the box did not have the HP seal on it, and I queried him about it; what happened to the new one?

Apparently sometime earlier in the morning, someone bought the last new one, and he ‘thought’ that this one was new too, just that there was a customer who demanded to have a look at the unit, and that was why it was opened. I said, okay, let's have a look at the laptop; I could at least see what the state of the laptop bag seal is (if the laptop bag seal was intact, I'd treat it as completely new).

However, not only was there no laptop bag seal, there was no laptop bag at all! I thought that was highly suspicious, and told him so, and asked him what happened to the bag. He said there was none. Yeah, right! I've bought many laptops before (we currently have 7 at home, and most of them are HP), and I know better. Okay, let's have a look inside the laptop.

Two things were instantly obvious. The touchpad was full of fingerprint marks, and the screen was coated with dust. That meant only one thing: it was a demo unit! I was not at all impressed that he was trying to sell me a demo unit as new, without any discount. He said he'd talk to the manager to see how much he could knock off the price.

In the end, the most he could do was $100; he said that the manager normally only discounted $50 for demo units, and that the $100 discount was a special dispensation over the fact that I came here on the understanding that a new laptop would be available. I thought, yeah right, whatever; I was seeking a $200 discount. But I figured I'd talk to The Ferret and see what she'd say about it all.


When I went back to tell the salesperson that no can do, he was tied up with other customers, so I thought I'd look around and see what other laptops there were around. One of the ones I spotted, also selling for cheap, was the HP Pavilion “Thrive” SE dv6723tx; it was much like the “Influx” SE dv6819tx, but with a T7500 processor (which is exactly what I needed). So I asked another salesperson (let's call him salesperson 2) about it.

He told me that that was the last unit left; there are no new ones, anywhere (which I know to be a fact). So I asked him what he can do for me, in terms of discount. Initially he offered me extended warranty, and a ‘starter pack’ thrown in for free, but neither of these things did anything for me, so he offered a $100 discount. This was acceptable to me (hey, the starting price wasn't bad to begin with, and nobody tried to pass it off as a new laptop), but I'd talk to The Ferret about it first, anyway.

Upon my return, salesperson 2 was tied up with another person, and the manager (whom salesperson 1 spoke with) came by, and I explained that I'd like to buy that laptop, with the $100 discount that was agreed to. The manager looked shocked; he couldn't believe that a $100 discount was being offered, and explained that normally with display units, the discount is $50. Well, not my problem. (At $50 discount, I would have been much less willing to part with my money.)

Salesperson 2 then returned, glad that I was back to place the order. However, the manager wasn't nearly as glad that the $100 discount was being offered; it turned out that, because the laptop was already on clearance discount, the $100 discount would put the sale price at parity with the cost price. I felt really bad for salesperson 2, especially because if their staff work on a commission basis, he'd get $0 from that sale. However, far be it from me to complain about the price!

Salesperson 2 was very good-natured about it though, and was very helpful for the whole transaction. So there are definitely good people at the Sylvia Park Dick Smith; don't let my earlier section dissuade you from shopping there!


Anyway, I'm pretty pleased with my new purchase, and with the excellent service I got from salesperson 2; I don't yet have time to play with it, what with all the work I currently have, but The Ferret is drooling all over it (I said to her that if she liked the chassis of the “Thrive” one better than the “Influx” one, I'd be willing to swap with her, after swapping the CPUs). I look forward to giving it a go in a couple of days!

15/11/2008

When you buy Intel, you get what you pay for

After all that drama with trying to get cheap laptops on specials, it's time to return them! At least most of the ones I'm returning are unopened, so they can resell them ‘as new’.

Okay, okay, the one I got from Noel Leeming (HP Pavilion dv6914tx), that was my bad, for not checking out on the Core 2 Duo specifications on the CPU first. That CPU (T5750) was listed in the table, as one that doesn't support hardware virtualisation.

But I also took advantage of a couple of other specials too (HP Pavilion “Influx” SE dv6819tx, and Sony VAIO VGN-NS15G), and their CPUs (T5850 and T5800 respectively) aren't even listed! How could I know whether a CPU supports hardware virtualisation if it isn't listed?!

Now, reading up on the web revealed that I could replace the CPU (the HP manuals go into great lengths to explain how to accomplish this, so it's obviously a supported procedure), but the only comparable CPU currently available, a T7500, will cost me $400 to get, which would completely negate the savings I made from the specials, and put me back to square one.

In comparison, my current laptop (which I got for even cheaper), a Compaq Presario F573AU (with an Athlon X2 TK-53 processor, i.e., not even a Turion), is the dinkiest laptop of all the ones we have at home; it also happens to be the only one that supports hardware virtualisation (being the only laptop here with AMD instead of Intel).

So, that's why AMD rocks, and Intel can eat a donkey or something. :-)

13/11/2008

A short little appeal to programmers

Please, please, please, before you check in your changes, make sure you've updated your working copy and merged in changes made by other developers first! Enough said. :-)

On online auctioning and people's strategies

I've used online auction sites a little, mostly to buy stuff, but soon we'll have stuff to sell, so I've been doing more thinking about it. More specifically, how to set the reserve price, relative to how much we actually want to get out of the item.

See, my philosophy is this. The reserve price should be slightly lower than the price you want, to hook people in (or, for a really popular item, set the reserve to $1, and let the fun begin), then watch the price skyrocket. Well, that's the idea anyway.


One of the things I enjoy doing at eBay is something called bid sniping. Within a few seconds of the sale closing, you place a bid higher than the last bid, and hope to win the bid that way. The thing is, though, for a really popular item, others will also be bid sniping too, so you have to be sure to go above their snipe bid (if you want the item enough). It's this counter-sniping that helps drive the price up, and increases the excitement level for everyone involved. To me, bid sniping is one of the main attractions of online auctions.

TradeMe has this option called auto-extend that sellers can use. The idea behind auto-extend is that if a bid is placed within two minutes of the closing time, then the auction is extended for another two. Of course, this completely kills bid sniping (and the resulting excitement), so I will not be using that for any of my sales.

I should explain why I brought up the excitement factor. To me, the risk is part of the online auctioning experience, and why I even bid at all. I actively avoid bidding in sales that don't allow bid sniping. For that reason, by not using auto-extend in my sales, I am betting that it will result in more bidders, and thus a higher sale price in the end.


I have a few things to say about reserve prices and Buy Now prices too. I have seen some people set their reserve price at the Buy Now price. This is completely stupid: if a buyer actually will meet the reserve price, they may as well Buy Now, and not bid at all.

My preference is to set the Buy Now price at, say, twice the value of the reserve price (well, unless I'm using a $1 reserve price, in which case it'll just be a price well above what I expect the final bid to fetch). It's a way to have ‘sure money’ from someone who wants the item enough to pay the price for it, without wanting to risk the chance of being outbid.

In other words, if I use Buy Now at all, I set it at a price that I don't ever realistically expect someone to pay. Say I'm selling my current (one-year-old) laptop, and I want to sell it for about $750. My Buy Now price would be $1000, and my reserve would be about $500.

Some people seem to have this fear of using a low reserve price, as if that will be the final price they will have to sell their item at. I say, if your item is in high demand, you have nothing to fear. And if it's not in high demand, don't sell it via auction.

There, I'll get off the soapbox now. :-P

12/11/2008

Little Eats

I just realised that I forgot to blog today, and that I have about 10 minutes left to do so (11.48 pm at the time of writing this), so I thought I'd rant about something quick.

Those of you who live in New Zealand, what do you think of Wattie's Big Eat brand? I understand that Big Eat is marketed, and supposed to appeal, to the more blokey types (what with the tagline Cordon Bloke), but I don't know. I don't consider myself any sort of a bloke, but still I seem to find that their meals are not very…big at all. So I'm kinda stumped here, as to what the point of Big Eat is.

In other news, The Ferret and I went to eat rice noodles at a Vietnamese place today. Now that's what I call a big eat! :-D

10/11/2008

Noel Leeming has the most useless call centre ever

Remember the drama I had with Noel Leeming? I thought it couldn't get worse. I was wrong.

So, you remember how 10 days ago, I called the call centre, and they said they found a unit in Christchurch, and that they would courier it to my home? Well, I got a call today, from the Christchurch store in question, saying that the laptop has arrived at their store and is ready for me to pick up.

I appreciated hearing from the lady at the store—it's a nice change from the idiots at the call centre—but I regretfully explained to her that I don't live in Christchurch, and that there's no way I can go there to pick the laptop up. I also explained to her that the call centre has stuffed up yet again. She said she'd follow up with the call centre.

Anyway, 10 minutes later, I got a call from the call centre (for the first time ever!). The guy on the line said he was calling because there was “conflicting notes” on my order and that was why the item got sent to the wrong place, and he wanted to confirm that I was wanting to have it couriered to my home.

Now, I've dealt with him before; he was the one who ‘accidentally’ disengaged my call. So I was disinclined to be kind to him. I asked him just what conflicting notes were on the order, and he mumbled something about how the order said something about shipping it to a store for pickup, but now it's to be couriered. Yes, originally I did request for it to be sent to a store (in North Shore, not Christchurch, mind), but because they've stuffed up and delayed my order so much, the least they could do is send it directly to me instead.

Anyway, he said he could arrange to get it couriered this afternoon, and I asked if, on account of their multiple stuff-ups, they could arrange for it to be urgent-couriered. They couldn't, and said the ETA would be in a couple of days' time.

I'd had enough at that point. I asked, in my usual steely voice :-P, if I needed to speak to his supervisor. He was like, no, escalations have to be done in writing. Oh, I see. Well, then, they'd better expect something ‘nice’ in their letterbox soon.

To end the call, I said to him that I hope that the item will indeed arrive at the correct place, in the stated time; I wouldn't like to have to call again. (There's that glint in my voice that hinted at something very unpleasant should that happen.) By this point, he sounded scared shitless, and just stammered, “Of course not; all right, goodbye,” and couldn't hang up the phone soon enough.

Now, just so you know, I don't especially delight in making customer service people squirm, and had he been someone I hadn't dealt with before, I'd have been far kinder. But, some people just deserve what they get. *shakes head*

On Nikki Kaye's recent victory

I never thought I'd write this post; I mean, between my interests in relationships, programming, and left-wing politics (and that's just the topics I've touched so far in my blog; I'm sure I'll cover my other interests soon enough), surely they'll give me enough to talk about. But no, what with the recent National victory in New Zealand, especially in Auckland Central (which is still getting a lot of media coverage), I figured I'd chip in with a word or two.

Today, while taking The Ferret to the dentist to have her wisdom tooth pulled out and sitting in the waiting room, I saw a big half-page article in the Herald about none other than our favourite Auckland Central MP. I'm very happy for her that she's so much in the limelight; yes, I'm envious. (More on this later.)

You see, I've known Nikki for a couple of years, but we haven't crossed paths for more than 10 years now; and yet after all these years, I still remember what a great friend she was, and I'd find it difficult to imagine anybody who knew her who would say otherwise. I strongly believe that her personable and memorable nature is, and will continue to be, a key ingredient of her success.

10+ years is a long time for anybody to change, and we probably wouldn't recognise each other on the street now, but if she's anything like the person I knew then, then I know Auckland Central is in very good hands.

So, on that note, as someone who knew Nikki and has only good things to say: Nikki, I want to congratulate you on winning Auckland Central; I knew, long before the election, you would succeed where nobody else could. And I mean this as high praise, coming as I do as someone with no love of National's aims or values. But then again, I believe your success was of your own making, and not in any way due to your party allegiance. I have every confidence that you will pave an even greater path of success in the next three years. Keep up the good work!

9/11/2008

In JIT we trust

I remember a debate with a colleague five years ago about the merits of dynamic compilation versus static compilation. He was arguing that .NET was ‘better’ than Java because .NET had an ngen utility for AOT native code generation. Of course, he favoured straight C++ even more because it was unmanaged; he felt that the management in managed systems just slowed things down.

I disagreed strongly with him at the time, citing the capabilities of the JIT compiler in Java at the time (it was at 1.4 back in those days, if I remember right; right about the time when there was that big fight between log4j versus java.util.logging). Well, of course, it's improved a lot since then, and I'm especially happy with the capabilities of Java 6 in the performance department!

John Rose (the mastermind behind the Da Vinci Machine project) has a page detailing all the various optimisations currently in place in Hotspot; there are sure to be even more optimisations for dynamic code invocation to come up.


On a related topic, I was perusing the OpenJDK 6 code today, and I learnt about a really cool technique they use in reflection (yes, I'm aware that the technique was implemented even in the 1.4 days, but OpenJDK code is easier to obtain, so that's what I'm looking at). Now, assuming that you, the reader, are not an expert in Java's core library implementation, how would you go about implementing reflection? Have a think about it, then read on (if you want to).

If you've played with JNI at all, I would presume you'd probably be thinking of that. Just write a native method that simply calls the JNI's builtin reflection functionality (or some shortcut thereto in the JVM). This, indeed, does work, and is one way that OpenJDK 6 does it.

It has a much cooler technique, though, called “inflation”. Inflation means that for the first few runs (default 15) of a reflected method/constructor (from now on, any reference to methods applies to constructors too), it does so via JNI; the next time after that, it assembles a class file on the fly, and loads it. At that point, full JITting applies, and further calls to that reflected method has the same performance as directly calling that method.

You can even disable inflation completely (set the sun.reflect.noInflation property to true), in which case the JNI approach is skipped completely, and all reflected method calls are assembled to class files straight away. Alternatively, you can tune the number of runs before the assembly occurs (use the sun.reflect.inflationThreshold property).

If that's not cool, I don't know what is!

8/11/2008

A big loss for the left, but hopefully not a victory for the (far) right

Well, New Zealanders have spoken. Not entirely in the way I wanted; I was hoping for a Labour-Green coalition, obviously, but, on the upside, the Greens have two more MPs in the House (even though the polls said they would get six more)! That is, indeed, a victory for the Greens.

With a National government coming up, the interesting part will be to see whom they'll make a coalition with. With Roger Douglas back in parliament, I'm not sure that an alliance with Act will make a very centrist government, but the Maori Party looks pretty strong too; I would be hoping for National to make an offer to them, instead of (or at least in addition to) Act. We'll see.

Winston Peters has lost his seat in parliament, after 30 years. Perhaps not surprising after all the recent dramas surrounding him, but still a shock.

More of a shock, though, is that Helen Clark has announced her resignation as the leader of the Labour Party. Very courageous, I think, and it definitely marks the end of an era. I hope the next leader of Labour will rebuild Labour to a new glory in three years (or whenever)!

Well, here's to a more centrist next three years in New Zealand! With John Key in the lead, I have some confidence this is well within reach.

7/11/2008

Do something good—get out of bed and get down to the polls before you start drinking!

It's coming up to midnight here, so I won't risk violating electoral advertising laws by advocating any party this time. :-P No matter what, do something good tomorrow, and be sure to vote!

I hate Noel Leeming, and kids who have no restraint

So, let's start with the kids. Two days after Guy Fawkes Day, the kids are still trying to burn their fireworks like there's no tomorrow. Well, maybe in some cases, if they're incautious, there will indeed be no tomorrow for them!

Right, now for the meat of the post. I totally, absolutely loathe Noel Leeming. I've ordered a laptop from them a whole fortnight ago, and they've managed to balls up my order in perhaps the most spectacular way imaginable.

I placed my order via their website on Friday morning, just at the start of their Labour Day online sale. I knew that it was a clearance product, so the earlier I ordered it, the more likely I'd get it soon. The only branch with units available for immediate pickup was Botany Downs, which was about an hour's drive from home, so I opted to have it delivered to the Wairau Park branch instead. I was told to expect to hear from Wairau Park in about 3 days, when the item would be ready for me to pick up.

Three days came and went. No news. I went to the store to ask about it on Tuesday, and was told by an impatient floor person that they received nothing, because it was a long weekend, and that I will hear from them when it arrives. Clearly, they didn't want to be bothered about it. All right, I thought, let's give them the benefit of the doubt, just in case it was a 3 business day delay.


Well, was I wrong. On Friday, I happened to be at the Lynnmall branch of Noel Leeming, so I took the opportunity to enquire about my order. The store person looked at the details, and said, the item should be available by now—Wairau Park had 7 in stock! So, I called them up and asked what was going on, as I waited a whole week and was sick of waiting.

The person at Wairau Park advised me that those 7 units were not actually for retail sale, but were instead reserved for a corporate order, and that the call centre people (who were in charge of Internet ordering) had stuffed up by listing that branch as a supply branch, when in fact they had no supply. He also said that he would get in touch with the call centre and have them contact me about whether I could pick up a unit from the Manukau branch, that was supposed to have one available.

(For non-Aucklanders: I live in the North Shore, and the Wairau Park branch is one of the closest to home. Botany Downs, Lynnmall, and Manukau are all ‘south of the bridge’, quite far from home, so we try to avoid unnecessary travelling.)

I asked for the call centre to call me back as soon as possible, because we didn't want to get stuck on the city side of the bridge without good reason. Well, most of an hour passed, we heard nothing, and so we went home, disappointed.


Irate, I decided to call up the call centre myself the next day. The first call I made, I got hung up on; the second call I made, the guy on the line expressed his sympathy, and arranged to have a unit delivered from a South Island branch (which I was told to expect by the end of the week). I said, emphatically, that I had waited more a whole week for the laptop, and that I would like it if they could provide me some compensation for the run-around I'd been given all along. I hinted that a spare laptop battery wouldn't be bad for them to throw in.

The call centre guy said, no, they don't do that, but they could provide a refund. And I was like, well, I bought the laptop during the Labour Weekend sale, and if I knew it was going to take that long to get here, I would have taken my fine money elsewhere, so really, because of this lost opportunity, they should refund me the pre-sale price, or offer a store credit with equivalent value.

And he was like, nope, sorry, the most we can offer is an apology, but perhaps you could write a complaint to customer services about this. (As a matter of fact, once I get calm enough to write a professional letter of complaint, I'll just use this post as the basic substance, so consider this the first draft. :-P)

Anyway, I was still expecting the laptop “by the end of the week”. Guess what? It's now Friday evening, and I still have received neither a call, nor a package at my door. This is really shocking, and inexcusable; it's been two whole weeks since I ordered, and never once have they ever called me. I had to be the one to do all the pushing. I dare say that this is the most shocking customer service I have ever encountered in my 20 years of living in New Zealand, topping even Telecom. Think about that.

What's the bet that, if the laptop arrives at all, it was a display unit, and already halfway broken from the rigours of ‘unsupervised test drives’. That would really take the cake, as far as raising my blood pressure beyond whatever notional savings I got from the sale could cover.

6/11/2008

On romanticism

Recently, I got into a disagreement with a correspondent because they didn't like the idea that a relationship is like a business transaction, and I stated that that was the honest truth. I still stand by that statement.

Now, the reader could be forgiven for wondering how a romantic cupid like me could get away stating with such supposed heresy. The idea of having to negotiate everything in a relationship, as though you're negotiating your working conditions, appears so unromantic on the surface, so much so that many couples dodge such topics as pre-nuptial agreements (when, in my opinion, they shouldn't). So, what sort of cupid advocates that kind of exchange?!

Well, this one does.

I call myself a cupid because seeing people in a happy relationship makes me happy. The operative word here is happy. To me, it's not enough that a couple appear superficially to be happy; I'm too much of an idealist for that. A relationship is a happy one, in my view, when both parties are getting what they need from it.


See, I hate fairy tales. Perhaps that had something to do with the my upbringing, that I was never exposed to any of them; my mother was a very matter-of-fact person, never really about doing any make-believe things. For example, Christmas was for giving gifts, not having a fluffy figure descend upon non-existent chimneys (chimneys in apartments? surely you jest) to magically deliver your reward "for being good". In my upbringing, there were other "rewards" for being good, such as not getting yelled at. :-P

Anyway, back to fairy tales. The Ferret tells me that fairy tales are evil because they instil into little children this idea of happy-ever-after relationships that magically appear, and these stories never talk about the maintenance and upkeep that go into making and keeping a beautiful relationship that way. It's the biggest crock of bullshit ever.

The Ferret and I have a happy relationship, not because we're "lucky", but because we know from watching other relationships what mistakes and traps we must avoid (and a purpose of this blog is to discuss these), and we work hard to do the right things (ditto). One of the things we do do (which is no doo-doo :-P) is regularly discuss what we need, and work out ways we can fulfil each other's needs.


Really, I think the reason many non-negotiated relationships fail is because many of them are on totally different pages about what each party wants and expects of their partner. Nobody is psychic in a relationship! (The Ferret is psychic, but that's a different story.) If there's something you need in a relationship, especially things that you can't budge on, make sure it's sorted out before you commit. Seriously.

There's nothing romantic about all the these gorgeous five children you've dreamt about for the last 20 years, only to find that you're in love with someone who hates kids and would leave if you actually seriously brought up the topic of kids.

Just don't go there. Please, for the love of cupid.

Important things have to be discussed. Even if you both agree on the number of kids to have, I'm sure there are lots of other things to disagree on, such as what to name them, how to raise them and discipline them, what aspirations you have for them (lawyer? hacker? hooker?), just to name a small handful of items.

And that's just about children. What about what styles you prefer in bed, and how frequently? Which way to you put the toilet seat after use? How compatible are your spending habits? What about the Big Forbidden Topics, politics and religion?


Anyway, I've rambled on enough for now, but I just want to leave a parting thought: romance seldom comes by magic, or luck, or whatever. Most of the time, it takes hard work. Identifying and honestly discussing your needs, and having your partner do the same, is a large component of that hard work. (Being able to listen to honest feedback with sincerity is arguably just as big.)

The reward here is that, once you do complete these discussions, and realise that the person you're with does fulfil your needs, and vice versa, then, you're well on your way to having that successful, life-changing romance. That would make this cupid very, very happy.

5/11/2008

On idealistic hope

This is a special post; I suspended my normal topic schedule, just to celebrate a very great event today.

I say this because, in the US, Barack Obama won the presidential election. Of course, he was the candidate I was backing, however, that's not what my post is directly celebrating, but instead:

  1. That there has been a greater turnout to this election than ever before, and
  2. That old-style politics can be laid to rest, clearing the way for a much more constructive style.

I think the speeches from both John McCain and Barack Obama were very touching, for different reasons. I applaud McCain's grace in conceding the presidency; the crowds were booing audibly, but he firmly told them no, we're all Americans, we must all work together for the good of our country. This is a very different line from that taken during a large part of the campaign, and I really do appreciate that.

Obama's speech is about a call to action; that the election is not the change, but just the enabler of the change that everybody must contribute to, and make sacrifices for. It's also a pledge of support to all Americans, be they Obama supporters or otherwise. It's about unity, despite differences. It's tear-inducing material. :-)

I hope, in seeing all the US election coverage here in New Zealand (and everywhere else in the world, but I emphasise NZ because we have a national election coming up in just a couple of days), that there will be more voters here coming out to vote, and that they will vote with their conscience, for the things they believe in, and not just because the media circus managed to make more dirt stick to one party than another.

As a Greens supporter (and maybe soon-to-be member), I would of course hope that more young voters would come and vote; Jeanette Fitzsimons mentioned that when she was campaigning at Auckland University, there was such a great level of support from the students that if their votes were representative of the country, then the Greens would be the clear major party, and all the other parties would become minor parties. :-P Well, it's not that way across the board, but I still hope for a 10%+ party vote for the Greens!

So if you're a New Zealander, be sure to vote! I'd prefer if you voted Greens, however, if you're not a Green supporter, that's fine, as long as you vote. :-)

4/11/2008

On food additives

What is with all the things commercial food producers put into food these days? The Ferret and I were shopping today for a cottage pie, and pretty much all the ones contain MSG (621) or similar related flavour enhancers (627 and 631 are the main culprits). So we ended up getting none of them.

The same comments apply to such things as potato chips, instant noodles, etc. Yes, I know some people say that healthy people don't eat such things, so people who eat them are unhealthy and therefore shouldn't care about flavour enhancers. I disagree with that stance, but even so, the use of flavour enhancers has crept into supposedly-wholesome foods too, such as the shepherd's pie I just mentioned, as well as many varieties of supermarket roast chickens, just to name a couple of examples.

Just in case people think this sort of stuff applies to savoury foods only, think again. A lot of desserts I see contain aspartame (951) and/or other artificial sweeteners (950, much of the time). Now, I can understand products made for diabetics and the like, who must watch their sugar intake. However, most of the artificially-sweetened items I see actually contain sugar too, in which case, why the artificial sweeteners?!

The Ferret and I can both taste MSG and aspartame when they've been added to food, and the aftertaste isn't particularly pleasant; furthermore, she is sensitive to these substances, which will give her headaches. So, we avoid any food items that contain them.

But really, why?! Do these additives actually make food that much more saleable? And in that case, why are they less saleable in the first place?

2/11/2008

Just another MLP post :-)

Still lots of coding to do today, so I thought I'd pass on another small handful of recent-event items. :-)

  1. If you live in the Auckland area, the Sky Tower will be orange between now and election day, as a commemoration. Take advantage of this once-in-three-years opportunity with your favourite camera!
  2. If you're as big a fan of the Brandenburg Concertos as I am, you might be interested in a sale currently on for an ASMF rendition of the series (1, 2, 3, 4, 5, 6)!
  3. If you're a programmer, may I recommend the Stack Overflow podcast series. I've been too busy to listen to the whole series yet, but I'm currently up to #15, and the dialogue between Joel and Jeff often have me in fits of laughter! The Stack Overflow site itself is pretty neat too, though apt to be addictive; currently I'm going through a somewhat-involuntary recovery, simply due to my workload. :-P

1/11/2008

Two public service announcements

At the moment I'm very busy and can't scrape together enough time to write a full blog post (yesterday's one took me 4 hours(!) to write), so I'll instead post a couple of current-event items:

  1. If you're a Green Party supporter (and in my opinion, if you aren't already one, you should certainly consider it), you can make your own Vote for us billboards! There's one that I found that's just as poignant as the original billboard.

    And, if you submit your creation to the Green Party Facebook page by Monday, you're in a draw to win a Greens t-shirt! (6 entries as of this writing, so your chances aren't too bad either.)

  2. WCPE is celebrating its 30th birthday this year! If you love classical music as much as I do, you should definitely tune in. For the values-conscious who choose to use patent-free media formats, WCPE has an Ogg Vorbis stream (in addition to MP3 and other formats); in fact, that's how I found them in the first place, as Vorbis.com linked to them.

On beauty (and related ideas and ideals)

So, I wanna know: what does beauty mean to you? Whom do you consider beautiful, and what do you find beautiful about them?

There's that often-heard saying, beauty is in the eye of the beholder. True enough. Let's set yourself up as the beholder, for the moment. Do you consider yourself beautiful? Why, or why not?


In many ways, I begrudge mainstream society for instilling a narrow view of beauty on everyone, but especially on the young and vulnerable, pushing a standard of beauty that very few can attain, but that many have learnt to lust after. Although the specifics are hard to pin down, I'd still say that looking at pictures of models, mainstream musicians, and Hollywood stars would still provide a fair idea of what I'm talking about.

Perhaps I'm barking up the wrong tree, though. It's easy to argue that in those fields, such standards of beauty are indeed maintained because they have mass appeal, and thus allow for mass promotion. Who am I to argue with products that are designed for mass marketing?

However, we, as individual persons, are not (I hope) supposed to be mass marketed. When we deal with individuals, and especially ourselves, we should carefully maintain a wider view of what constitutes beauty, beyond what we've been taught via mass media. History has been full of examples of society trying to mould everyone into the fashions of the day; ultimately we end up with lots of square pegs being rammed forcefully into lots of round holes, complete with lots of collateral damage in between.


I've had to learn this the hard way. For most of my teenage years, I had not considered myself beautiful; after all, I came into contact with people my own age everyday who were more (conventionally) beautiful and popular. I mention popularity for a reason, too; I feel that popular people, to some extent, mould the notion of beauty being applied by the community in question.

Now, I was completely opposite to that. I was (and still am) a hardcore introvert, and thus not only was I not popular, I also did not have relationships with popular people (from a teenage perspective, this is obvious: being associated with a less-popular person risks diminishing the status of the more-popular one). Really, then, I had no way to be around beautiful people, as far as beauty is meant in the mainstream sense.

At the end of my teenage years, I looked in the mirror one day, studied my face in detail, and saw a glimpse of what I would today consider beautiful. And I said to myself, if I couldn't find anyone beautiful to be with, I'd better make myself beautiful. It was the only kind of beauty I could be sure of.


Making myself beautiful was hard work. I would look in the mirror everyday, seeing how far from the (mainstream) ideal of beauty I was, and feel despondent. I went through a stage where I tried out all kinds of clothes, hair, makeup, and whatever else I could think of, to see how I would come out; I needed to know what worked best for me. In that process, I stopped worrying about how the ever-mystical others had to say or think. I had to, or else I wouldn't have had the courage to discover what I needed to.

At the end of it, five years later in my mid-twenties, I became a different person, and that was something other people noticed too. I was surer of myself, of what I believed in, and in particular what I believed of myself and my notion of beauty. I'd realised that all along I was a beautiful person, but that until that moment, the understanding of beauty was lost to me, awaiting discovery.

That was a major revelation to me, and it helped me feel a lot better about my self-image. But even more importantly, I'd learnt to see beauty in others that I had never in the past been aware enough to see. This is the sort of beauty you seldom see in mass media, but is the sort that enriches your life for good. It helps us see each other as individual persons, each beautiful in his/her/hir own way, and not as just another mass-produced item.


So, at the end of this rather long post, what I've been trying to say is this: I think it's important to understand a notion of beauty beyond what mass media and popular people portray; not only does it provide a stronger sense of self-worth (as far as seeing oneself as beautiful is concerned), but it helps us see people around us as whole individuals, not as just another face to be compared to the latest weekly.

Your mileage may vary, but I hope this post resonates with you more rather than less.

Administrative interlude

I've just signed up for NaBloPoMo for this month. Let's see if I find something to say for every day of November!

I've also picked out a new template, Sand Dollar. It's hard to find a good template on Blogger that uses the full width of the page, so it's nice when I see one. I did tweak some numbers so the sidebar makes more room for the content. (The original template uses 31/66 ≈ 1:2 ratio; my tweaks uses a 24/74 ≈ 1:3 ratio.)

Right! Onward with today's actual post! (I must say, after spending lots of time with my latest addiction, Stack Overflow, I've grown rather fond of Markdown, and would love nothing more than for Blogger to support it!)

14/10/2008

More about interfaces than you can throw a HissyFitException at

This post is in response to Mike Stone's Please Hold on the Interfaces.

I believe in judicious use of interfaces. Sometimes it's warranted, such as when it represents concepts that lend themselves to multiple unrelated implementations (e.g., Runnable or Callable).

This is doubly useful in, say, (pre-3.0) GNU C++, where you can cast references of any type to a given interface (or signature as G++ calls it) as long as all members of the interface are implemented. It's much, much more flexible than interfaces as seen in Java or C# (where you have to declare upfront in the implementing class that it's in fact implementing some interface).

Where all implementations are likely to have common functionality, having an abstract class is more sensible, in my opinion. Sometimes (such as in the Java collections framework), having both is sensible, too.


Interfaces do use vtables (in fact all interface method invocations are virtual), so whoever says using interfaces is faster because it avoids virtual calls has never profiled their code before, and worse, has no concept of how high-level source code translates to low-level object code. (Yes, I just downvoted their answer.)

I never see a reason to avoid virtual functions when polymorphism is warranted. Seasoned C++ programmers will tell you that what slows your program down with using virtual functions isn't the indirect function call, but the diminished opportunity for inlining.

However, with a managed platform like Java or .NET, the JIT can easily inline for the common case (profile-guided optimisation for the win!), and deoptimise when the common case is no longer the common case. (For lurkers and archives, what I mean is that if a virtual method is being called mostly through instances of one specific concrete class, that concrete implementation can be inlined into the JITted code until other concrete classes start being used more frequently—in which case the code is re-JITted appropriately.)

In contrast, programmers who think they know where their program's bottlenecks are, without profiling for the common cases, are performing ignorance-guided optimisation.


I don't agree with the "call protected virtual initialisation method from constructor" approach though. Remember that in the superclass constructor, the subclass portion of the object isn't constructed yet, and any such virtual methods you call had better make sure that they only access the superclass portion of the object.

In C++, this is enforced—this is a feature, not a bug. Virtual functions do not exhibit virtual behaviour when called (directly or indirectly) from constructors and destructors. I have some code to demonstrate this:


#include <iostream>

class Base {
public:
    Base() {hw();}
    virtual ~Base() {gw();}

    virtual void println(char const* line) = 0;
    void hw() {println("Hello, world!");}
    void gw() {println("Goodbye, world!");}
};

class Derived : public Base {
public:
    Derived() {}
    virtual ~Derived() {}

    virtual void println(char const* line) {
        std::cout << line << '\n';
    }
};

int
main()
{
    Derived d;
    return 0;
}

Here, even though we're instantiating a Derived object, where the println function is defined, while inside the Base constructor (and destructor) Derived::println is not being used at all.

If the constructor/destructor calls println directly, that will generate a compiler error (since a pure virtual function is being called). In this indirect case, the compilation will go fine, but because Base's vtable is used until execution reaches Derived's constructor, the println() call inside hw() will not work as (naively) expected.

Like I said, this is a feature, not a bug. While the price you pay is that you cannot use virtual behaviour inside constructors and destructors, this actually makes your program safer, by not requiring your virtual functions to be specially coded to only use members in the base class.

So, back to the use of a "protected virtual method" to allow mockable initialisation. The proper way to do this, in my view, is to make an initialize() method, that is called by client code after the object is constructed. Since initialize() isn't being called by the constructor, it means that the object is fully constructed, and that it's safe to call into the appropriate virtual method to do the appropriate type of database initialisation (or whatever).

28/05/2008

Lists and Lists: tail recursive fun

So, who's played Andrew Plotkin's Lists and Lists? It's a Scheme tutorial, and it's written in Inform, a language used to author interactive fiction.

What I find cool is that, despite (intentionally) being written for a platform not designed to host a programming language interpreter, Lists and Lists is exactly that—a very flexible interpreter, that allows you to write whatever program you like to solve its challenges. It simply verifies your programs according to the problem criteria, and you are not required to solve the problems ‘any particular way’.

Lists and Lists also has sample solutions, if you type help enough times for a given problem. These sample solutions are designed to be easy for beginners to understand, but are not necessarily optimised for any other criterion. So, variety being the spice of life and all, I've decided to post my answers to some of the questions. (I'm currently learning Scheme, so these are by no means any sort of ‘optimal’.)

My particular aim with my answers is that they should be tail recursive, so that when given a very large list to process, the programs don't use up huge amounts of stack.

In standard Scheme, the ‘named let’ syntax makes tail-recursive programs quite readable; sadly, this functionality is not available in Lists and Lists, so I also present my solution in both ‘formats’. Additionally, for extra functional-programming goodness, I present SRFI 1 versions where available.

(Sorry about the funny line wrapping; I wanted to help the two-column format display correctly on a variety of screen widths. I used Emacs to do the indentation, so hopefully the result is readable.)

  1. Define sum to be a function that adds up a list of integers. So (sum '(8 2 3)) should return 13. Make sure it works correctly for the empty list; (sum nil) should return 0.

    My approach here is: Why rewrite the + function when you can reuse it? Or, why take the high road when you can take the cheap road? :-P

    Standard SchemeLists and Lists
    (define (sum l)
      (apply + l))
    
    (define sum 
      (lambda (l)
        (eval (cons + l))))
    

    Okay, if I must actually write an iterative solution (if only so that we have something to compare the next problem against):

    SRFI 1Standard SchemeLists and Lists
    (define (sum l)
      (reduce + 0 l))
    
    (define (sum l)
      (let next ((sum 0) (tail l))
        (if (null? tail) sum
            (next (+ sum (car tail))
                  (cdr tail)))))
    
    (define sum
      (lambda (l)
        (letrec
            ((next 
              (lambda (sum tail)
                (cond
                 ((null? tail) sum)
                 (t (next (+ sum (car tail))
                          (cdr tail)))))))
          (next 0 l))))
    

    Yes, I quite deliberately reused the symbol sum, just to demonstrate that I'm not using sum to recurse. You can't, anyway, if you want a tail-recursive solution.

  2. This problem is like the last one, but more general. Define megasum to add up an arbitrarily nested list of integers. That is, (megasum '((8) 5 (2 () (9 1) 3))) should return 28.

    This solution is similar to the last one, but I'm not assuming that tail is a list, and more specifically, I only do the adding when I'm not dealing with a list. When I encounter a list, I just recurse; this catches cases where (car tail) is a list.

    SRFI 1Standard SchemeLists and Lists
    (define (megasum l)
      (let mega+ ((elem l) (sum 0))
        (cond
         ((null? elem) sum)
         ((list? elem) (fold mega+ sum elem))
         (else (+ elem sum)))))
    
    (define (megasum l)
      (let next ((sum 0) (tail l))
        (cond
         ((null? tail) sum)
         ((list? tail) 
          (next (next sum (car tail))
                (cdr tail)))
         (else (+ sum tail)))))
    
    (define megasum 
      (lambda (l)
        (letrec
            ((next 
              (lambda (sum tail)
                (cond
                 ((null? tail) sum)
                 ((list? tail) 
                  (next (next sum (car tail))
                        (cdr tail)))
                 (t (+ sum tail))))))
          (next 0 l))))
    
  3. Define max to be a function that finds the maximum of a list of integers. So (max '(5 14 -3)) should return 14. You can assume the list will have at least one term.

    My solution uses a pairmax helper function that simply returns the maximum between any two quantities.

    SRFI 1Standard SchemeLists and Lists
    (define (max l)
      (define (pairmax a b)
        (if (> a b) a b))
      (reduce pairmax #f l))
    
    (define (max l)
      (define (pairmax a b)
        (if (> a b) a b))
      (let next 
          ((best (car l)) (tail (cdr l)))
        (if (null? tail) best
            (next (pairmax best (car tail))
                  (cdr tail)))))
    
    (define max 
      (lambda (l)
        (letrec
            ((pairmax 
              (lambda (a b)
                (cond
                 ((> a b) a)
                 (t b))))
             (next 
              (lambda (best tail)
                (cond
                 ((null? tail) best)
                 (t (next (pairmax best (car tail))
                          (cdr tail)))))))
          (next (car l) (cdr l)))))
    

    You'll notice a pattern here, that there is a fairly mechanical way I'm translating from the original program to the Lists and Lists-compatible version. In fact, when tested with Guile, its macro expander actually expanded the named-let blocks on the left into something quite similar to the letrec blocks on the right.

    (Of course, in this problem, since Lists and Lists does not support internal define, the equivalent letrec is used instead.)

  4. Last problem. You're going to define a function called pocket. This function should take one argument. Now pay attention here: pocket does two different things, depending on the argument. If you give it nil as the argument, it should simply return 8. But if you give pocket any integer as an argument, it should return a new pocket function—a function just like pocket, but with that new integer hidden inside, replacing the 8.

    >>(pocket nil)
    8
    >>(pocket 12)
    [function]
    >>(define newpocket (pocket 12))
    [function]
    >>(newpocket nil)
    12
    >>(define thirdpocket (newpocket 3))
    [function]
    >>(thirdpocket nil)
    3
    >>(newpocket nil)
    12
    >>(pocket nil)
    8
    

    Note that when you create a new pocket function, previously-existing functions should keep working.

    My solution is pretty much the same as Lists and Lists' sample solution (just how many ways can you solve this one?!), just using a named let.

    Standard SchemeLists and Lists
    (define pocket
      (let construct ((num 8))
        (lambda (x)
          (if (null? x) num (construct x)))))
    
    (define pocket 
      (letrec
          ((construct 
            (lambda (num)
              (lambda (x)
                (cond
                 ((null? x) num)
                 ((construct x)))))))
        (construct 8)))
    

So, there you go. Feel free to comment with any corrections, improved answers, or whatever ideas you have. :-)

Job programming puzzles

Lately, I've been taking to job programming puzzles, to help my brain atrophy a bit less. :-P There are a couple of ones I've looked at so far: Justin.tv, and Weebly. I found both of these companies through the Hacker News job board. I look forward to doing more puzzles as I come across them. I'll probably store all my answers, and just submit them when my Green Card arrives and I can start applying for some of these jobs.

The Weebly one is easy. I solved that one in 15 minutes (and could have done it somewhat faster if not for a couple of false starts). In so saying, I didn't have to write any JavaScript code at all, I just used standard Unix tools, so perhaps that's cheating (as far as timing goes—I could have done using only client-side JavaScript code, but it would have taken me a little longer). :-P


The Justin.tv one is more challenging. I had some code from my OMGWTF 2007 submission that handled operator precedence (yes, my calculator was written to behave like a scientific calculator, and even provided buttons for parentheses so you could override the precedence), but I wanted to do it ‘the right way’, so I read up Wikipedia on parsing. I ended up using the shunting yard algorithm to do this (if you aren't familiar with this algorithm, I'd advise you to learn it from Dijkstra's paper, rather than from the Wikipedia article).

The extra-credit reduction option I implemented in a fairly conservative way, that does not reorder the values, nor change the evaluation order (even when such reordering would provide a correct final result). It does squash consecutive applications of the same operator into one function call (such that it would work as expected in Scheme), and it does so knowing that addition and multiplication are associative.

My implementation is written in Perl, by the way (and I use operator overloading on the S-expression class so that printing nested S-expressions requires very little code). It's written in a functional style, allowing easy translation to Ruby once I know the language better. And as an extension, it supports the exponentiation operator, **, which has higher precedence than * and /, and the output could even be fed to Scheme (with SRFI-1 support) if you had this function defined:

(define (** . args)
  (reduce-right expt 1 args))

Here is the test set I used (the first five are from the Justin.tv website, and the last four involve negative numbers, which are outside the spec, so change them appropriately if your implementation can't deal with negative numbers):

3
1 + 1
2 * 5 + 1
2 * ( 5 + 1 )
3 * x + ( 9 + y ) / 4
a + b + c + d
a + b + ( c + d )
a + ( b + c ) + d
a + ( b + c + d )
w - x - y - z
w - x - ( y - z )
w - ( x - y ) - z
w - ( x - y - z )
-1 + 0 - 1 * 2 * 3 - 4 - 5
-1 + 0 - 1 * 2 * 3 - 4 - 5 / 6 / 7 - 8 + 9
-1 + ( 0 - 1 * 2 * 3 - 4 - 5 / 6 - 7 / 8 ) + 9
-1 + ( 0 - 1 * 2 * 3 - 4 - 5 / 6 + 7 / 8 ) + 9

Results, without reduction:

3
(+ 1 1)
(+ (* 2 5) 1)
(* 2 (+ 5 1))
(+ (* 3 x) (/ (+ 9 y) 4))
(+ (+ (+ a b) c) d)
(+ (+ a b) (+ c d))
(+ (+ a (+ b c)) d)
(+ a (+ (+ b c) d))
(- (- (- w x) y) z)
(- (- w x) (- y z))
(- (- w (- x y)) z)
(- w (- (- x y) z))
(- (- (- (+ -1 0) (* (* 1 2) 3)) 4) 5)
(+ (- (- (- (- (+ -1 0) (* (* 1 2) 3)) 4) (/ (/ 5 6) 7)) 8) 9)
(+ (+ -1 (- (- (- (- 0 (* (* 1 2) 3)) 4) (/ 5 6)) (/ 7 8))) 9)
(+ (+ -1 (+ (- (- (- 0 (* (* 1 2) 3)) 4) (/ 5 6)) (/ 7 8))) 9)

Results, with reduction:

3
(+ 1 1)
(+ (* 2 5) 1)
(* 2 (+ 5 1))
(+ (* 3 x) (/ (+ 9 y) 4))
(+ a b c d)
(+ a b c d)
(+ a b c d)
(+ a b c d)
(- w x y z)
(- w x (- y z))
(- w (- x y) z)
(- w (- x y z))
(- (+ -1 0) (* 1 2 3) 4 5)
(+ (- (+ -1 0) (* 1 2 3) 4 (/ 5 6 7) 8) 9)
(+ -1 (- 0 (* 1 2 3) 4 (/ 5 6) (/ 7 8)) 9)
(+ -1 (- 0 (* 1 2 3) 4 (/ 5 6)) (/ 7 8) 9)