Monday, November 04, 2013

Combining lots of ".aiff" files using sox

There was a memorial service yesterday, and the sound guy at the church made an audio CD. I'm surprised everything fit, really; it was about 81 minutes.

My dad noted that the CD had 81 tracks, each about a minute long. When trying to listen to the CD on my mom's computer, he could only hear a minute at a time, and a track would typically end in the middle of a song or sentence. Then he'd have to hit the "Play" button and wait for the CD to spin up again.

I supposed that there was no actual pause on the CD, but that the CD player software just stopped when it saw a track marker. To make this friendlier for playback on computer, we decided to concatenate the "tracks" into a single long file. We could divide it up into smaller pieces (inserting a track marker when a particular song or speech starts, say) later.

My guess is that the sound guy had a single very long audio track, and he told his CD burning software to insert a track marker every 60 seconds. Why was this a good idea? Well, suppose you're playing the CD on a CD player (these things still exist). Some CD players don't tell you how far into a given track they've gotten. So if you wanted to find a spot 23 minutes into the thing, you'd be pretty much stuck with either waiting 23 minutes after you hit "PLAY," or using the skip/review function (if your CD player has one) while listening... assuming of course that you knew what came before and after the point of interest.

With the every-60-second track markers, you can skip 23 tracks. It still might start in the middle of a song or sentence, but that's better than waiting 23 minutes, right?

My newphew came into the room, and upon hearing the problem, proceeded to copy 81 "AIFF" files into a directory on the computer's hard drive. (Do audio CDs actually have AIFF files? No, but on Mac OS X that's what it looks like in the Finder.) This took a long time. After looking at the Activity Monitor app and seeing no CPU or RAM hogs, we decided that having a 98% full hard drive might have something to do with the issue. That took care of some of our waiting time, anyway.

Anyway, he seemed to remember hearing that Quick Time 7 could concatenate sound files, and he tried it, taking the first file (I think the filename was "1 Track 1.aiff" or something like this) and doing the click-and-drag thing with "2 Track 2.aiff". After some seconds of computing, the computer showed us that we indeed had a two-minute file. We played it and there was no audible gap or pause as we crossed the one-minute mark.

But nobody had the patience to drag the other 79 files, and I certainly didn't feel confident that we could do it without error. Attention wanders when doing tasks like that. The worst thing was, if we did make a mistake (leaving out track #24 for example, or appending track #72 twice), we might not notice it for some hours.

So we did a web search on concatenating .AIFF files. One result mentioned using cat(1), so I gamely opened a terminal window. My thought was to get all the files listed in order, then pass them to cat, which would concatenate them end-to-end. We would route the output to an output file, say x.aiff.

How to get a list of all the files? Well, one could type something like "echo *.aiff" and get a result that looks something like this:

% echo *.aiff
1 Track 1.aiff 10 Track 10.aiff 11 Track 11.aiff 12 Track 12.aiff
13 Track 13.aiff 14 Track 14.aiff 15 Track 15.aiff 16 Track 16.aiff
17 Track 17.aiff 18 Track 18.aiff 19 Track 19.aiff 2 Track 2.aiff
20 Track 20.aiff 21 Track 21.aiff 22 Track 22.aiff…
Actually the result is one very long line, which ends as follows:
79 Track 79.aiff 8 Track 8.aiff 80 Track 80.aiff 81 Track 81.aiff 9 Track 9.aiff
Is the first problem obvious? When we say "echo *.aiff" or whatever, the output is sorted character-by-character, so for example "19" comes before "2" because the first character of "19" (i.e., "1") sorts lower than the first character of "2". The ls command does that too. If you knew the files were written in a certain order (e.g., track 2 has an earlier modification time than track 19 for example) then ls might help, but since the source was an audio CD with no mtime data, this didn't occur to me.

What did occur to me, though, was to say "sort -n"; as the manpage tells us (type "man sort")

   -n     Compare according to arithmetic value an initial numeric  string
          consisting of optional white space, an optional - sign, and zero
          or more digits, optionally followed by a decimal point and  zero
          or more digits.
So if we said "ls|sort -n"...
collin@v2:~/tmp/CD-tracks 
% ls *.aiff|sort -n
1 Track 1.aiff
2 Track 2.aiff
3 Track 3.aiff
4 Track 4.aiff
5 Track 5.aiff
6 Track 6.aiff
7 Track 7.aiff
8 Track 8.aiff
9 Track 9.aiff
10 Track 10.aiff
…
78 Track 78.aiff
79 Track 79.aiff
80 Track 80.aiff
81 Track 81.aiff
collin@v2:~/tmp/CD-tracks 
% 
OK, that's more like it. Suppose then we piped all these to cat(1). Umm, let's try with just the first three files.
collin@v2:~/tmp/CD-tracks 
% ls *.aiff | sort -n | head -n3
1 Track 1.aiff
2 Track 2.aiff
3 Track 3.aiff
collin@v2:~/tmp/CD-tracks 
% 
The head command gives the first <howevermany> lines of its input. To say how many lines (the default I think is 10), we use -n# where # is the number of lines we want -- in this case, three.

Given the three lines, how do we pass all of them to cat? Easiest thing is the xargs command. If it had been invented last week, there would probably be a patent application in the works, but fortunately the idea came in an earlier era so all can use it for free. Here's the thing: If you give xargs five lines, then it will append them to the end of a single command line. So if we said for example

collin@v2:~/tmp/CD-tracks 
% ls *.aiff | head -n3 | xargs echo
1 Track 1.aiff 10 Track 10.aiff 11 Track 11.aiff
collin@v2:~/tmp/CD-tracks 
% 
Right? We can do that with cat and send the output to a file we'll call "x", maybe like this:
collin@v2:~/tmp/CD-tracks 
% ls *.aiff | sort -n | head -n3 | xargs cat > x.aiff
cat: 1: No such file or directory
cat: Track: No such file or directory
cat: 1.aiff: No such file or directory
cat: 2: No such file or directory
cat: Track: No such file or directory
cat: 2.aiff: No such file or directory
cat: 3: No such file or directory
cat: Track: No such file or directory
cat: 3.aiff: No such file or directory
collin@v2:~/tmp/CD-tracks 
% 
Were you surprised, or did you wonder how cat would be able to tell where one filename ended and the next began? Let's get rid of the spaces in those filenames. There's any number of ways to do that, but I typed something like this to see if it would work:
collin@v2:~/tmp/CD-tracks 
% for X in *.aiff; do echo mv "$X" ${X// /.}; done | head -n3
mv 1 Track 1.aiff 1.Track.1.aiff
mv 10 Track 10.aiff 10.Track.10.aiff
mv 11 Track 11.aiff 11.Track.11.aiff
collin@v2:~/tmp/CD-tracks 
% 
What's "${X// /.}"? Well, let me go back to the beginning of the line.
  1. for X in *.aiff; do
    says to assign the variable X to successive values of whatever filenames match *.aiff, and execute everything until the done keyword.
  2. echo &hellip
    I want to see what's about to happen, rather than just executing it.
  3. mv "$X" …
    The command mv is the Unix™ command "move", which is how we rename things. What do we rename? The $X says "whatever's in shell variable X".

    Why do I put the $X in "double quotes"? Because that tells the shell to treat the entire name (1 Track 1.aiff for example) as a single "word".

    What new name will we give to $X? That's coming next.

  4. ${X// /.}
    This tells the shell to start with the variable X (which will be "1 Track 1.aiff" the first time through, etc.) and replace all instances of " " by ".". How did I know this? From reading the manpage for the shell. Type "man sh" or "man bash" some evening when you're having trouble getting to sleep:
    BASH(1)                                                          BASH(1)
    
    NAME
           bash - GNU Bourne-Again SHell
    
    SYNOPSIS
           bash [options] [file]
    …
    Parameter Expansion
       The `$' character introduces parameter expansion, command substitution,
       or arithmetic expansion.  The parameter name or symbol to  be  expanded
       may  be enclosed in braces, which are optional but serve to protect the
       variable to be expanded from characters immediately following it  which
       could be interpreted as part of the name.
    …
       ${parameter/pattern/string}
       ${parameter//pattern/string}
          The pattern is expanded to produce a pattern just as in pathname
          expansion.  Parameter is expanded and the longest match of  pat-
          tern  against  its  value is replaced with string.  In the first
          form, only the first match is replaced.  The second form  causes
          all  matches  of pattern to be replaced with string.  If pattern
          begins with #, it must match at the beginning  of  the  expanded
          value  of parameter.  If pattern begins with %, it must match at
          the end of the expanded value of parameter.  If string is  null,
          matches  of  pattern are deleted and the / following pattern may
          be omitted.  If parameter is @ or *, the substitution  operation
          is  applied to each positional parameter in turn, and the expan-
          sion is the resultant list.  If parameter is an  array  variable
          subscripted  with  @ or *, the substitution operation is applied
          to each member of the array in turn, and the  expansion  is  the
          resultant list.  
  5. done;
    see item 1 above.
Convinced that that ought to work now, I do this:
collin@v2:~/tmp/CD-tracks 
% for X in *.aiff; do mv "$X" ${X// /.}; done
collin@v2:~/tmp/CD-tracks 
% ls *.aiff | sort -n | head -n3 | xargs echo cat
cat 1.Track.1.aiff 2.Track.2.aiff 3.Track.3.aiff
collin@v2:~/tmp/CD-tracks 
% 
That's more like it. Now let's do the real thing:
collin@v2:~/tmp/CD-tracks 
% ls *.aiff | sort -n | xargs cat > x.aiff
collin@v2:~/tmp/CD-tracks 
% 
Great! We looked at the size of each file (about 10 Mbytes per track, except the last), and the output file, x.aiff, was about 800 Mbytes, so we were OK. Opening the result in Quick Time Player 7, we found it was only as long as the last track, i.e., less than 40 seconds.

Disillusionment

Well, we went back to the web search, to the post that mentioned using cat, and found that basically, you can't do that.

I did another web search and found that sox can in fact concatenate sound files, including aiff files. Yes! We went to the sourceforge site and downloaded the Mac OS X zip file. I think it was a zip file anyway (actually happened about 12 hours ago). Unlike a lot of Mac OS apps where you click here to install, etc., this one you just unpack and run it. I ended up typing something like

% ~/Downloads/sox-14.2/sox… 
but I'm getting ahead of myself.

First we looked at the documentation for sox, which told us that if you want to concatenate sound files, you put them on the command line, with the output file last. If I recall correctly the example was

% sox short.aiff long.aiff longer.aiff
but of course we had 81 input files. What would the output file be? I would want it to sort last, so I did something like this:
% touch 999.aiff; ls *.aiff | sort -n | xargs ~/Downloads/sox-14.2/sox
Then, opening 999.aiff in Quick Time Player 7, we found that it indeed appeared to be about 81 minutes long.

Technology is great when it works. I copied the result to my USB flash drive, so the lovely Carol can listen to it after I return home.

Remembering Danny

My brother-in-law Danny died a week ago. He was in great pain and ready to go to his reward. I called my sister the day before, and she filled me in a little on his condition. He'd been hallucinating off and on. Did I want to talk with him? She handed him the phone.

"Hi Collin!" he said. He sounded cheerful, or was that just me wanting him not to be in pain? I told him I was sorry he was going through all this. He seemed not to hear me, but said, "I'll call you back, OK?"

No you won't, I thought, but what I said was, "OK." What a miserable situation! He wanted to go, he was deeply uncomfortable, and if he recovered, my sister would have had a horrific time trying to care for him: a leg was due to be amputated, and his heart was going to need a valve job within a year.

And yet how terrible for him not to be there any more!

Well, I wanted to write a few things about him while I can still see.

The lovely Carol mentioned a time when our kids were very young. There were frustrations, but she never heard Danny yell at his boys. She asked him about it, and he explained that he'd put a hand on the kid's shoulder and lower his face to be directly in front of the kid's. (Maybe that was one hand on each shoulder—I don't recall exactly.) His nose no more than 3 inches from his son's, he would say firmly, "You need to put away your toys now." It was not loud, but it was quite effective.

Danny's musical gifts were considerable, and he used them at various churches. At yesterday's memorial service, I kept reminding myself that he wasn't about to appear on stage with his cane and sit down at the piano. Several people mentioned yesterday that he started many musical groups and worked patiently with almost anyone who wanted to sing or play.

What I remember most about him most was his gentle manner and the way he talked about his health challenges. "Everybody's got something," he would say, and he was certainly right about that. But Danny had way more than his fair share.

"I'll call you back, OK?" he said. When I get there, some decades hence I suppose, I'll hold him to that promise.

Sunday, October 20, 2013

An almost-painless OS upgrade: OpenSUSE 12.3

I must be prone to bouts of gratuitous optimism, because this upgrade had its rough spots.

Getting the download image was easy enough. Went to the OpenSUSE website and downloaded it. ("It" here means the 32-bit DVD image.) I used an ancient powerbook to burn a DVD, as my old DVD writer stopped working some time back. I slid the DVD into the drive on this old IBM lease return I got a few years ago for $150 or whatever. It's got 1GB of RAM and a 32-bit P4 processor (bogomips : 6384.75).

It booted fine, with speed what you'd expect from an optical drive. But it took the longest time to probe disk drives and to find Linux partitions. Some time (an hour?) later, there was an error message... I suspected too many retries on the DVD reader.

Fine. Made a bootable USB drive following these instructions and booted it.

Well, it still took too long (IMO) to probe disk drives and find Linux partitions; in fact I gave up once, powered the box down and disconnected the DVD drive cable because I suspected it was goofing things up. It still took an awfully long time (literally hours) but finally I got to the place where it asked me if I wanted to continue.

I clicked the Next button, and then... installation just hung while updating the boot configuration. Huh? The lovely Carol was out, so I slid my chair to her computer and did a web search on "opensuse installation hangs" which landed me here, which included this:

are you sure you are not running out of disk space? (sorry, i know you
are probably have plenty of room, but what partitioning scheme did you
elect to use...that is: is /tmp on its own partition and is it REALLY
REALLY big?)
Whoa! ctl-alt-f2 and "df" -- my root partition was like 93% full. Ouch!

Freed up some space, and things started moving again. Whew! I was working on a shell script for the lovely Carol, and the next time I looked at my Linux box, there was the login screen. Yippee! I typed my login and password and... can't find home directory. Dang!

My homedir is NFS mounted; more than that, it's automounted. So I asked if nfs was running. Yes, but NFS whined at me: "portmap/rpcbind" was not running. OK, chkconfig portmap but apparently OpenSUSE doesn't use "portmap" any more.

$ chkconfig portmap
portmap: unknown service
$ chkconfig rpcbind
rpcbind  off
$ 
O...kay. I started rpcbind, and now nfs was happy. Was automounter running? You guessed it. So I said "chkconfig autofs" and of course it was off.

Enabled that, explicitly started it (going Neanderthal, I typed
$ /etc/init.d/autofs start
then alt-f7 back to the X11 screen, logged out and back in and... voilà! all was good.

I ejected the USB stick (which by now was /dev/sdb) and rebooted, and things pretty much worked. Google-chrome (upon which I'm typing this post) didn't, though; library incompatibility. I downloaded a new RPM, then
$ sudo rpm -Uvh Downloads/google-chrome-stable_current_i386
and after a few minutes (it had to delete the previous version) that was working too.

But wait; there's more!

I decided to try to get mail(1) working. xhost +; sudo yast2 and said I had a permanent connection to the mail server. Then I typed something like
date|mail -s test other-email@yahoo.com
and messages like these appeared:
postdrop: warning: mail_queue_enter: create file maildrop/856011.3977: Permission denied
postdrop: warning: mail_queue_enter: create file maildrop/856204.3977: Permission denied
postdrop: warning: mail_queue_enter: create file maildrop/856417.3977: Permission denied
postdrop: warning: mail_queue_enter: create file maildrop/856738.3977: Permission denied
postdrop: warning: mail_queue_enter: create file maildrop/857467.3977: Permission denied
…and just kept coming. A web search led me to a couple of helpful links: And now I've got working mail too.

Tuesday, October 08, 2013

Matthew Explains Himself

No one knows the Son except the Father, and no one knows the Father except the Son and those to whom the Son chooses to reveal him. from Matthew 11:27

We are going through Matthew in our marvelous Sunday morning group. Last week, our teacher asked, How does Jesus (“the Son” in the passage above) decide who will get to know the Father?

Immediately John 14:23 came to mind, where Jesus says, If anyone loves me, he will obey my teaching. My father will love him, and we will come to him and make our home with him. But another answer, probably a better one, is close at hand. Rather than going to another gospel, we can look around, right where we are. Here's a bit more of the context:

25At that time Jesus said, “I praise you, Father, Lord of heaven and earth, because you have hidden these things from the wise and learned, and revealed them to little children. 26Yes, Father, for this was your good pleasure.

27“All things have been committed to me by my Father. No one knows the Son except the Father, and no one knows the Father except the Son and those to whom the Son chooses to reveal him.

28“Come to me, all you who are weary and burdened, and I will give you rest. 29Take my yoke upon you and learn from me, for I am gentle and humble in heart, and you will find rest for your souls. 30For my yoke is easy and my burden is light.”

Matthew 11:27-30
To whom does the Son reveal the Father? Those who come to him and take his (easy) yoke upon them! Now that's not a mathematical proof, but look at verse 25: things (such as "who is the Father?") have been revealed to little children (i.e., those who follow Jesus—that is, those who came to Jesus) but were hidden from the "wise and learned" who did not.

The knowledge of the secrets of the kingdom

That was last week; this week's passage included a verse about secrets: The knowledge of the secrets of the kingdom of heaven has been given to you, but not to them. (from Matthew 13:11, NIV)

What are the secrets of the kingdom of heaven, and how did Jesus’ listeners get this knowledge? Again my mind went quickly to John (in 8:32 Jesus says, "Then you will know the truth") but why go there when there's plenty right here?

2[H]e got into a boat and sat in it, while all the people stood on the shore. 3Then he told them many things in parables, saying: "A farmer went out to sow his seed. … 9He who has ears, let him hear."

10The disciples came to him and asked, "Why do you speak to the people in parables?"

11He replied, "The knowledge of the secrets of the kingdom of heaven has been given to you, but not to them. 12Whoever has will be given more, and he will have an abundance. Whoever does not have, even what he has will be taken from him.

from Matthew 13:2–12
What am I saying here? As our teacher pointed out this week, Jesus was in a boat, and a crowd stood on the shore. As he spoke, his words landed on various kinds of people in the crowd, just as the farmer's seeds landed on various kinds of soil in the parable.

After Jesus’s closing line, He who has ears, let him hear, many in the crowd said, "Great sermon, Rabbi," and went home with no idea of what Jesus had just said. But some came to him—Matthew calls them "disciples" (i.e., followers)—and said, basically, "Huh?"

It is exactly to these people that Jesus says, "You've got the knowledge of kingdom secrets." The secrets actually come to one big secret, an open one: it's Jesus. To paraphrase Archilochus: the wise and learned know many things; Jesus’s disciples know one big thing.

Thursday, October 03, 2013

Nobody has the right to be this happy

Have you ever had one of those days when you can't believe how fortunate you are? I get together with a small group on Fridays, and we tell each other what God has done, as Psalm 16:23-24 says. At one of these gatherings, I told my friends it's easy for me to love God because he is so good to me.

Tuesday morning started early—earlier than expected, because I had trouble sleeping, worrying uselessly about something or another. Happily, I found this beautiful passage from Merton's No Man Is an Island:

All nature is meant to make us think of paradise. Woods, fields, valleys, hills, the rivers and the sea, the clouds traveling across the sky, light and darkness, sun and stars, remind us that the world was first created as a paradise for the first Adam…. Heaven is even now mirrored in created things. All God's creatures invite us to forget our vain cares and enter into our own hearts, which God Himself has made to be His paradise and our own. If we have God dwelling within us, making our souls His paradise, then the world around us can also become for us what it was meant to be for Adam—his paradise. But if we seek paradise outside ourselves, we cannot have paradise in our hearts.
chapter 6 (Asceticism and Sacrifice) 15 (p. 115)
I went back to sleep with the thought that God is working to make my heart a paradise, for him and for me.

A few hours later I rose for a prayer meeting at church. They even provided coffee! It was a sweet time of fellowship, talking and praying with men and women who so love the church[audio].

After the prayer and fellowship, I drove my '86 Corolla (which still runs great!) to a side street, then ran like heck to catch the just-arriving Caltrain. Little did I know that my younger daughter had seen me running, and was leaving a note for me even as I rode the train—yes, I made it!

At work, I was putting a few finishing touches on a project when the boss cropped by my cubicle. She was happy about my results (we'd had a phone conference with India the night before) and stopped by to congratulate me on well-done job. That's not an everyday occurrence! The other thing was, she asked me how I would feel about working on a certain other project—something I'd mentioned before as an area I'd like to focus. So "I'd love to!" was no surprise.

At lunch, I went for a walk around the block, a little over a mile, and enjoyed the beautiful weather.

I had another chat with the boss after lunch, where she went further and told me to go ahead and spend some time on that new project, managing my time with the other two things I'm working on. She also indicated that there were things I could do that would gain "points" and that would be interesting (if not as much fun as actually writing code) and not all that difficult. I spent the rest of the day working on some code, which as usual put me in a good mood.

Walking out to catch the light rail, I pulled out my phone and called Peet's, because I had coffee duty for the evening meeting at church. The barista/manager said they'd start on those right away; it had been a hectic day, she said. I told her I'd be by just about six (i.e., about 10 minutes later than the "reservation" time) so no need to rush. I congratulated myself for remembering to call ahead.

Returning to my car in Menlo Park, I found the note Sheri had left for me. My heart overflowed with thankfulness. It occurred to me that we can bring joy to God's heart by expressing our love to him.

Dinner was served at our meeting, which is always nice; the meeting itself started with Scripture, a little sharing of things we were thankful for, and prayer. (I didn't say anything because once I started I might have taken the whole time.) There were some significant issues, but I enjoyed the blessings of fellowship and the presence of God's Spirit as we thought and discussed together.

Sing to the Lord, all the earth;
    proclaim his salvation day after day. 
Declare his glory among the nations, 
     his marvelous deeds among all peoples.
Psalm 16:23-24

Wednesday, October 02, 2013

How to Use Your Lottery Winnings

That wasn't the subtitle of Babette’s Feast, but that’s how I took it. Let me explain.

There's a backstory (several actually), but here's a very brief outline: The pastor in a village in Jutland had two daughters, who have spent their lives caring for the poor in their village. Now elderly themselves, they take in an impoverished French cook to help them with their work. One day the news arrives: their cook has won the lottery! She desires to use her bounty to prepare a feast for the village, as the centenary of the pastor's birth approaches. The sisters are reluctant, but are won over by their cook's earnest desire.

Babette prepares her eponymous feast. The sisters assume that after this feast is served, Babette will return to France to live in luxury. They are surprised, however, to learn that Babette has spent all her winnings to host this fabulous meal.

Earlier in life, I would have thought Babette ridiculous, but I begin to appreciate Babette's extravagance.

In reality, is Babette brilliantly generous, or imprudently profligate? What do you think? Suppose Babette died the next morning; would that change your thinking?

And what would/should/could I do with my winnings? Because in a real sense, I have won the lottery: my prize is the days that lie ahead. How should I use them? Who should I bless with them? Because I certainly can't take them with me.

Sunday, September 15, 2013

What made the past week so great

Sorry, this isn't about Assad supposedly agreeing to turn over his stockpile of chemical weapons or about progress containing California fires—great though those things are. And I am grateful that violence and devastation are averted or reduced or at least not increased.

No, what I want to tell you about is just stuff about my life. Starting with about a week ago, when I had a wonderful time interacting with our fellowship group about Jesus in Matthew 8–9. What hit me then, and I am still processing today, is the question: "What is my life about?" Because it's so different from the life of Jesus.

Looking at Jesus in those two particular chapters (which happened to be my teaching assignment) made me see that his life was all about bringing grace and truth into the world.

Grace and truth—an important combination. Earlier in the week, when talking with the lovely Carol about this, I said that Jesus was all about bringing salvation—salvation in the broad sense: freedom from debilitating illness, illusions, alienation &c.—but perhaps “grace and truth” is a better phrase.

My life on the other hand is a lot about staying out of trouble. Yes, I want to do good, and sometimes I actually do. (And now that I think of it, I actually have a lot of joy at times.) But am I all about bringing grace and truth into the world? Healing and mercy? Not so much.

So yes, last Sunday's class was a lot of fun for me. I even wrote a paper (I spent about 5 minutes of class time introducing it) on an engineering/inductive approach to faith and miracles. I'll send you softcopy if you're interested; let me know.

More joy came in the form of writing code. Some of that is for work (I could show it to you, but then my boss would have to kill me) but more of it had to do with a couple of little projects. One was related to "kenken" puzzles. I wrote a solver on last month's vacation, but the input was kinda unfriendly. I mean it looked like this:

    11+ 1A 2A
    2/ 1b 1c
    3- 2b 2c
    20* 1d 2d
    6* 1e 1f 2f 3f
    3/ 2e 3e
    6x 3c 3d
    240* 3a 3b 4a 4b
    …remainder elided
What I did this week was change the front-end to accept input that looks more like this:
11+    2/    <    20*   6*   <
^      3-    <    ^     3/   ^
240*   <     6x   <     ^    ^
>      ^     …remainder elided
So now the kenken solver takes friendlier (or at least not-so-cryptic) input. Am I getting my inner nerd on or what?

My old friend Jan (she's younger than I am; what I mean is we met in 1968, before either of us met Jesus) was in the area, and we had breakfast Friday morning. I heard about some of the joys and disappointments in her family, some close calls and deliverance. Jan exemplifies Colossians 4:6 for me: Let your speech always be with grace, seasoned with salt…, as well as those verses in Psalms that encourage us to tell of [God's] works (see for example Psalm 73:28).

Another cause for celebration: my keys, which had been missing for the past week, were discovered Friday. They once were lost, but now are found. Happy day!

And yesterday, I worked with my daughter Sheri on her new website. Javascript was involved. So was coffee. As of about 9 this morning, it worked. I discovered happily that network solutions have Python 2.6; they do support cgi-bin. The layout is a big quirky, though; if you create a directory "icons" under htdocs, you can't ever get to it, because "/icons" redirects to some magic place where they put their icons. Arrows and dots and such. My icons, which were a few arrows and dots I created with convert(1), were nowhere to be found. So of course I renamed the directory my_icons. Hurmpf.

But it works! The challenge now is to make it easy to update and maintain.

Church was lovely this morning; besides the amusing quote If you have to ask whether you're young or old, you're old, the message was powerful: the importance of community. I heard something recently on NPR about how with economic prosperity comes a breakdown of community. Exactly how does this happen? I'll tell you. I don't know. The basic idea described on the radio was the idea that in poor villages, people have to rely on each other a lot more. But as prosperity comes, people become less reliant on each other; they're more "self-sufficient" (or so they/we tend to think) and therefore become more isolated. Dumb, huh? Yet I resemble that remark. I need to ponder that one too.

The lovely Carol and I went for a walk at Pulgas Ridge this afternoon. Our faithful pup also enjoyed the walk, though it was rather warm. It was great being outdoors and moving our bodies around.

Dinner tonight was fried rice—many leftover veggies make for an interesting dish. The lovely Carol offered to do the dishes since I'd done the cooking.

So I am thankful today. Sometimes I wonder how it was that I got to live in such a time and place as this, to have only minor problems in life, and so on. Not that I'm complaining. Praise God from whom all blessings flow! Amen.