Monday, January 28, 2008

A new lifestyle: losing 85 lbs in 7 months

Back in late June/early July, I found that I weighed 300 lbs. For the first time, I had to buy a pair of pants at a "big men" store: size 46 waist. I'm a big guy. I'm almost 6'3", and have broad shoulders. But this was ridiculous. I decided to do something.

I had already tried Body for Life, with which I lost (then regained) 30lbs a couple of times. I also ran into some blog posts by Tim Ferris. These shared much with Body for Life, but were new enough that I tried them for a few weeks. Unfortunately, they didn't work well for me. The problem was that I was on a diet, and what I really needed was a new lifestyle.

7 months: -85lbs, -10 inches, same jeans

For me, the difference between a diet and a lifestyle is that a diet is temporary, while a lifestyle is something you can continue forever. A diet is something you can't wait to be done with, while a lifestyle is your daily routine.

I learned two key lessons while finding a good lifestyle:

  1. You must want to be fit more than you want to binge or not sweat. A healthy lifestyle will involve some moderation in eating, and some exercise. So, if binging and not sweating are more important to you than being fit, you'll have a hard time even starting.
  2. You can't resent your lifestyle. This is crucial, even if it means a lifestyle that's somewhat less effective. Simply put, if you resent it, eventually you won't do it.

Since I quite love stuffing myself, and I hate exercise, one would think I'd resent any lifestyle that required differently. However, I found that if I keep such sacrifices reasonable, the resulting weight loss makes up for them. In other words, I don't resent moderate exercise so much when the pounds are coming off.

Now I'll describe the lifestyle I settled on after a few weeks of experimentation. I've maintained it with little effort for 7 months, during which I've gone from 300 lbs and size 46 pants to 215 lbs and size 36 (the pictures above show me then and now, wearing the same jeans). Most importantly, it's easy enough to maintain that I see no reason to ever stop. It comprises two aspects: eating right and exercising.

Eating Right

My goal is to keep my metabolism high, and avoid foods that quickly become fat. Thus, I follow these guidelines:

  • Eat four to six small meals a day, every 3 to 4 hours (I try to leave feeling slightly hungry; I'll eat again soon enough)
  • Eat more protein than carbohydrates.
  • Drink lots of water (I drink multiple cups with each meal, and between meals).
  • Avoid carbohydrates after lunch.
  • Avoid fatty foods, such as dairy, certain cuts of meat, etc.
  • No empty calories, such as desserts or non-diet soda.

Then, there's the two most important guidelines of all:

  • Once a week, eat whatever and however much you want. Indian buffets, pizza, ice cream, pasta...anything. Partly, this actually helps you lose weight because it convinces your body there's no famine. But more importantly, it lets you address any cravings that build during the week. My free day is Saturday, and I look forward to it all week.
  • If a rare opportunity for food comes along, it's okay to take it. Opportunities like parties, or travel (I feasted for a whole week while in Vienna). And don't feel bad, or give up your weekly binge for it. Sure, it's sub-optimal. But what's even less optimal is feeling so restricted you give up on your lifestyle entirely. Just make sure it happens rarely.

These last two are the most important guidelines because they keep away that fatal resentment.

Given these guidelines, my typical meals are as follows:

Breakfast: 9:00am

  • 1/4 cup low fat cottage cheese, or
  • 1 egg, fried in olive oil or hard-boiled

I prefer quick breakfasts, and I'm not hungry in the morning. Thus, a typical breakfast is 1/4 cup of low fat cottage cheese (0.75g saturated fat, 2.5g carbs, 6g protein), with some freshly ground black pepper for seasoning. Another option is an egg fried in olive oil, or hard-boiled.

Lunch: 12:30pm

  • Moderate portion at restaurant, with vegetables and as much protein as carbs

Since it's still early in the day and my metabolism is going, I eat more for lunch than other meals, including more carbs. This makes lunch my most free meal. Anything with moderate portions, vegetables, and low-fat carbs and protein is good. The key is to eat as much protein as carbs, and to leave a bit hungry.

Typical lunch, from a Peruvian restaurant

There are several restaurants near my workplace that I frequent for lunch. I like almost any special at a nearby Chinese restaurant, though I eat only half the rice. There are Peruvian, Korean, and African restaurants with perfect meals (Madison is great for restaurants). There's also a sports bar with a good chicken salad, and a burger joint with lean bison and ostrich burgers. Again, the key is low fat, as much protein as carbs, and leaving a bit hungry. Given this, it's easy to find good lunches almost anywhere.

Snack: 4:00pm

  • 3 or 4 pieces of beef jerky, or
  • 1/3 of a high protein meal replacement bar
1/3 of a high-protein meal replacement bar for a snack

Three or four hours later, I have a snack. This is a very light meal, about 100 calories, and protein-heavy. One good snack is three or four pieces of beef jerky (0g saturated fat, 5g carbs, 11g protein). Since I get tired of jerky, I've started alternating with high-protein meal replacement bars from nutrition stores like GNC. I look for low fat and few net carbs, then eat about a third of a bar (1/3 of a Myoplex Carb Control bar is 90 calories, 1.5g saturated fat, 1.3g net carbs, 8.3g protein). The bars aren't as ideal as jerky, but they're nice variety and sweet, which helps to avoid resentment.

Dinner: 7:30pm

  • 1 cup of mixed vegetables and protein

I eat dinner after getting home and exercising. Dinner isn't as light as my breakfast or snack, but it's much lighter than lunch. I typically have 1 cup of mixed vegetables and protein. For example, my last few dinners were tofu stir-fry. Other common dinners are seasoned ground beef with peas or broccoli, or leftovers from a Mexican restaurant (no carbs, so a meat dish in sauce, like pollo ranchero or steak in green chile sauce)

Light dinner: tofu stir fry

Since dinner is small, I typically cook a large portion, then have a little each evening. This applies equally well to take-out: a dinner portion at a Mexican restaurant serves for three or four dinners.

Dinner is usually my last meal. However, if I'm going to be up past midnight, I sometimes have another snack around 11:00pm.

Exercising

I detest exercising. I hate it beforehand, during, and afterwards. Thus, to keep resentment low, I minimize exercise time: 20-30 minutes per day, 5-6 days a week. I don't exercise on Saturday, my free day, and I only exercise on Sunday if I feel inspired. However, when I exercise, it's with high intensity.

I do both cardio and resistance training: on Mondays, Wednesdays, and Thursdays, I use a stationary bike or run; on Tuesdays and Thursdays, I lift weights. Since I hate exercising, if I have an excuse, I'll eventually take it. To combat this, I bought a stationary bicycle, a weight bench, and a dumbbell set. This minimizes time overhead, since I can exercise at home.

Below I describe my typical workouts. They're all short, but high-intensity.

Cardio: Stationary Bike

I use a stationary bicycle for cardio, largely because they're relatively inexpensive and fit easily in my apartment. However, my workout works well with any aerobic exercise with variable intensity. It is a variation on the Body for Life cardio workout.

The workout comprises four parts:

  • Low intensity warm-up: 1 minute
  • 3 sets of
    • Mid intensity: 3 minutes
    • High intensity: 3 minutes
  • Very high intensity sprint: 1 minute
  • Low intensity cool-down: 1 minute

This is a tougher workout than it sounds. I push so that I don't think I'll be able to finish the last set. Then, when I do, I push one more minute of all-out sprinting. I am definitely sweaty and exhausted by the end. However, since it only lasts 21 minutes, it's over quickly.

Resistance: Bench and Dumbbells

Muscle burns fat, so more muscle burns more fat. It also makes you stronger and look better. Thus, resistance training is just as important as cardio. It doesn't take much equipment, either: my whole workout uses only one pair of dumbbells with changeable weights, and a weight bench.

For each muscle group I exercise, I do three sets of six repetitions, with 30-60 seconds of rest between sets. Then, without rest after the third set, I do a fourth set of six reps, but of a different exercise for the same muscle. For instance, for pectorals, I might do a set of fly, rest, another fly set, rest, a third fly set, and immediately after a set of bench press. The key is to exhaust the muscle; if I can finish the fourth set a couple workouts in a row, then I increase the weight.

I concentrate on my upper body. I know this is sub-optimal, since it ignores the big muscles in my legs. However, I really don't like lower body exercises, and they were making me dread my workout. Thus, to minimize resentment, I left my lower body to the stationary bicycle.

I exercise four muscle groups: chest, biceps, triceps, and shoulders. For each group, I pick a primary and secondary exercise, then swap every few months. These are the exercises I like:

Results

So far, I've stuck to my new lifestyle for 7 months, without much effort. I've lost 85 pounds, and 10 inches off my waist. I've also gained noticeable muscle. I feel much healthier: I've got more energy, and can participate in many more activities. I've been told I even move much differently. Given all this, I'd say my new lifestyle is a success, and I plan to continue it indefinitely.

Wednesday, January 23, 2008

Dotfile replication with subversion

Whenever I got a new *nix account, the first thing I did was copy over dotfiles (.bashrc, .vimrc, .screenrc, etc.). Managing these copies was a pain, since I had to manually replicate changes. For example, if I thought of something clever to add to .vimrc, I had to manually add it to .vimrc on every account. Of course, this meant my dotfiles got hopelessly out of sync. This in turn made copying dotfiles to a new account even more painful, since I had to remember which copies were the latest.

To address this annoyance, I now keep all my dotfiles in a subversion repository. I assume by now everyone knows of subversion. If not, in short, it's a version control system, like CVS. If you don't know what that is either, chances are you're not worried about managing dotfiles.

The process was actually pretty simple, comprising only four steps (I'll assume basic familiarity with subversion, or at least a willingness to read the subversion handbook):

1. Create the subversion repository

See here for creating subversion repositories, and here for serving them. I called my repository "dotfiles".

2. Create the .dotfiles/ directory

I'll assume your most recent dotfiles are all on one account. First, on this account, create a .dotfiles/ directory in your home directory. Then, copy the following shell script to .dotfiles/linkToDotFiles.sh:

~/.dotfiles/linkToDotFiles.sh
#!/bin/sh

SCRIPT_DIR=$(echo $0 | sed -e 's/\(.*\)\/.*/\1/')
SCRIPT_FILE=$(echo $0 | sed -e 's/.*\///')
[ "$SCRIPT_DIR" = "." ] && SCRIPT_DIR=$(pwd)

for F in $(/bin/ls $SCRIPT_DIR | grep -v $SCRIPT_FILE)
do
   echo -n "$SCRIPT_DIR/$F: "
   if [ -e "$HOME/.$F" ]; then
       echo "~/.$F already exists, skipping."
   else
       if ln -s "$SCRIPT_DIR/$F" "$HOME/.$F"; then
           echo "linked to ~/.$F."
       fi
   fi
done

We'll use this script shortly to create symlinks (i.e., shortcuts) from your home directory to dotfiles in the .dotfiles/.

Finally, import .dotfiles/ to your subversion repository

3. Add dotfiles to .dotfiles/ and the repository

First, copy any dotfile you want to replicate to other accounts into .dotfiles/, but renaming them to remove the leading dot. For example, copy .vimrc to .dotfiles/vimrc. You can do the same for configuration directories, like .vim/. Then, add and commit these additions to the repository.

4. Replicate dotfiles to a new account

First, in the home directory of a new account (not the one where you created the .dotfiles/), checkout your repository. This will create a .dotfiles/ directory in the new account.

Then, run the linkToDotfiles.sh script by using this command from your home directory: bash .dotfiles/linkToDotfiles.sh. This creates symlinks in your home directory to files in .dotfiles/. For instance, it will create a symlink from ~/.vimrc to ~/.dotfiles/vimrc.

That's pretty much it. You can add new dotfiles by repeating step 3, and you can modify existing dotfiles and commit them from within .dotfiles/. If you commit changes, you can update .dotfiles/ in each account, which will preserve local, uncommitted modifications.

Friday, January 18, 2008

mediactrl: controlling laptop media buttons

My laptop media buttons don't do quite what I want. Specifically, I want

  • Volume changes to affect multiple channels.
  • A sound to play when I change volume.
  • Play/pause, forward, rewind, and stop to control whatever application I'm using, not just always the same one (e.g., pause the video if I'm watching a video, but the music if I'm listening to music).

So I first wrote a script for volume changing and media control, then told KDE to call it when I press media buttons.

Modifying multiple channels and playing a sound on volume changes is trivial. Controlling multiple media players is trickier. A heuristic that works well is to have a list of players that are controlled in order. In my case, I first list my video players, kaffeine and kplayer, then my music player, amarok. Thus, since amarok is always running, the media buttons control it by default. However, when I watch a video with kplayer, the media buttons control it instead because I listed it before amarok.

The player list, and other parameters, are in a config file:

/etc/mediactrl.conf
################################################################################
# Volume settings
################################################################################

# What percent to step on volume up/down
VOL_STEP_PERCENT=5

# The sound to play when volume is changed
VOL_NOTIFICATION_SOUND="/usr/share/sounds/KDE_Click.wav"

# The player to use when volume is changed (default is /usr/bin/aplay)
#VOL_NOTIFICATION_PLAYER="/usr/bin/artsplay"

# Comma-separated list of channels to alter (default is "PCM")
VOL_CHANNELS="Master, Master Mono, Headphone, PCM"


################################################################################
# Media player settings
################################################################################

# Comma-separated list of media players in the order in which they should be
# checked. (these need to be the actual command name). For each of these, the
# command to be run when play/pause, stop, prev, and next are pressed should be
# specified. Examples are below.
MEDIA_PLAYERS="kaffeine, kplayer, amarok"

kaffeine_playpause='dcop kaffeine KaffeineIface pause'
kaffeine_stop="dcop kaffeine KaffeineIface stop"
kaffeine_prev="dcop kaffeine KaffeineIface posMinus"
kaffeine_next="dcop kaffeine KaffeineIface posPlus"

kplayer_playpause="dcop kplayer kplayer-mainwindow#1 activateAction player_pause"
kplayer_stop="dcop kplayer kplayer-mainwindow#1 activateAction player_stop"
kplayer_prev="dcop kplayer kplayer-mainwindow#1 activateAction player_backward"
kplayer_next="dcop kplayer kplayer-mainwindow#1 activateAction player_forward"

amarok_playpause="dcop amarok player playPause"
amarok_stop="dcop amarok player stop"
amarok_prev="dcop amarok player prev"
amarok_next="dcop amarok player next"

For each player, the config specifies commands to execute for each media button. Since I happen to use all KDE apps, these commands are calls to dcop. The config should be placed in /etc/mediacntrl.conf.

The script itself is below:

mediactrl
#!/bin/bash

function usage() {
  echo "Usage: mediactrl "
  echo "  playpause    - toggles play/pause on active media player"
  echo "  stop         - stops play on active media player"
  echo "  prev         - goes to previous on active media player"
  echo "  next         - goes to next on active media player"
  echo "  volume  - alters volume.  is up, down, mute, unmute, or toggle"
  exit
}

if [ ! -f /etc/mediactrl.conf -o ! -r /etc/mediactrl.conf ]; then
   echo "Cannot read /etc/mediactrl.conf!" >&2
   exit 1
fi
. /etc/mediactrl.conf

case "$1" in
   playpause|stop|prev|next)
       echo $MEDIA_PLAYERS | perl -ne 's/,\s*/\n/g; print' | while read PLAYER; do
           if pgrep -u "$USER" "$PLAYER" > /dev/null; then
               eval CMD="\${${PLAYER}_${1}}"
               [ -n "$CMD" ] && eval "$CMD"
               break
           fi
       done
       ;;
   volume)
       case "$2" in
           up|down)
               echo ${VOL_CHANNELS:=PCM} | perl -ne 's/,/\n/g; print' | while read CHANNEL; do
                   if [ -z "$VOL" ]; then
                       VOL=$(amixer get "$CHANNEL" | grep % | head -n 1 | sed -e 's/.*\[\(.*\)%\].*/\1/')
                       PLUS_MINUS=$(if [ "$2" = "up" ]; then echo "+"; else echo "-"; fi)
                       VOL=$((VOL $PLUS_MINUS ${VOL_STEP_PERCENT:=5}))
                       [ $VOL -lt 0 ] && VOL=0
                       amixer set "$CHANNEL" "$VOL%" > /dev/null
                       dcop kded kmilod displayProgress "Volume " "$VOL"
                   else
                       amixer set "$CHANNEL" "$VOL%" > /dev/null
                   fi
               done
               ;;
           mute|unmute|toggle)
               echo ${VOL_CHANNELS:=PCM} | perl -ne 's/,/\n/g; print' | while read CHANNEL; do
                   amixer set "$CHANNEL" "$2" > /dev/null
               done

               CHANNEL=$(echo $VOL_CHANNELS | sed -e 's/,.*$//')
               MUTE=$(amixer get "$CHANNEL" | grep % | head -n 1 | sed -e 's/.*\[\(.*\)\]/\1/')
               dcop kded kmilod displayText "Sound $MUTE"
               ;;
           *) usage;;
       esac

       [ -n "$VOL_NOTIFICATION_SOUND" ] && eval "${VOL_NOTIFICATION_PLAYER:=/usr/bin/aplay}" "$VOL_NOTIFICATION_SOUND" 1>/dev/null 2>&1
       ;;
   *) usage ;;
esac

The bit of craziness with setting volumes ensures all channels are truly set to the same level.

Note that this script was written for a laptop running Kubuntu, and may need tweaking for other settings. For example, it uses kmilo to display a volume bar, which will not work unless the kmilo service is running.

Once this script is executable (chmod u+x mediactrl), I map it to the laptop media buttons through the KDE Accessibility settings (K Menu -> System Settings -> Accessibility -> Input Actions). Voila! More intelligent media buttons.

Monday, January 14, 2008

Gentoo to Ubuntu, or bottom-up to top-down

After many years of using and proselytizing to others about Gentoo, last week, I converted to Ubuntu (Kubuntu, to be precise).

This transition has made me think about how I use computers. Thanks to years of *nixes before user-friendly distros like Ubuntu, my understanding is bottom-up: I start with low-level functionality, then build higher-level abstractions. This is in contrast to most users' top-down understanding: start with provided high-level abstractions, then dig deeper if necessary.

To illustrate the difference, consider connecting to a wireless network. I know that I first bring up the network interface with ifconfig, then assign an ESSID and WEP key with iwconfig, then finally get an IP address with dhcpcd. I can execute these low-level steps by hand. However, to make life easier, I create a script to execute them. I can then extend this script to pick one of a set of networks, run a VPN program after connecting to a particular network, etc. This script is a higher-level abstraction that hides low-level details.

In contrast, a typical user has a top-down understanding; they start with the high-level abstraction. For example, in Kubuntu, the knetworkmanager program puts a nice icon in the system tray from which users manage their networks. The user never needs to know about ifconfig, iwconfig, or dhcpcd. Until something breaks and they have to dig deeper, anyway.

The top-down approach is vital to making computers usable to laymen. For years, Linux effectively required a bottom-up approach, which is one reason it was so inaccessible. This is why it's good to see new distros, like Ubuntu, moving toward a top-down approach.

In keeping with this spirit, I'm trying to use Ubuntu in a top-down manner. In other words, I'm trying to use only the high-level abstractions it provides, without digging into low-level programs and configuration files to build my own. This has worked well enough that I'm recommending Ubuntu to my non-Linux friends. Still, I miss the power and configurability of the bottom-up approach. Which is why you'll periodically see some of my nicer abstractions on this blog.

And so it begins

A conversation at Del's with friends Steve and Jay convinced me to find a place to collect and share random thoughts, ideas, and things. Here it is.

I've got a few things saved up, which I'll post by and by.

Also, as a side note: don't expect this blog to look pretty, barring someone else's template. I'm very left-brained, and though I can recognize prettiness, I have a hard time creating it.