Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, Swift, BASIC, and whatever else I happen to feel like hacking at.

A simple math pad in Perl

Jerry Stratton, May 13, 2012

I finally upgraded to Lion at the office; that means no more Mark Widholm MathPad. I mostly use it just to do freeform calculations in a series, kind of a very cheap spreadsheet. It occurred to me that I ought to be able to get something close to that using the “bc” command line app that comes with Mac OS X. Combine with SilverService and it’s not as dynamic as MathPad but it’s pretty close.

[toggle code]

  • #!/usr/bin/perl
  • #NOTE: BC does not handle upper-case in variable names
  • use warnings;
  • use strict;
  • use IPC::Open3;
  • my($bc) = open3(\*WRITE, \*READ, 0, '/usr/bin/bc');
  • print WRITE "scale=4\n";
  • my($ender) = 'ENDXXQQQEND';
  • my($lastResult) = '';
  • my($printCommand, $tail);
  • #handle both Unix and Mac line endings
  • my($lines) = '';
  • while(<>) {
    • $lines .= $_;
  • }
  • my(@lines) = split(/[\n\r\l]/, $lines);
  • #handle the math a line at a time
  • for (@lines) {
    • #handle mathpad comments
    • if (/^--/ || /^$/) {
      • print;
    • } else {
      • chomp;
      • my($command);
      • $_ =~ /^(.+?)(: ?[0-9.-]+)?([ \t]*--.*)?$/;
      • $command = $1;
      • $tail = $3;
      • $printCommand = $command;
      • if ($lastResult ne '' && $command =~ /#/) {
        • $command =~ s/\#/$lastResult/g;
      • }
      • print WRITE $command;
      • print WRITE "\n";
      • print WRITE "print \"$ender\\n\"\n";
      • my($result, $printed);
      • #a bit of a hack to ensure that there is always something to read
      • #when it detects the last thing to read, it leaves
      • while($result = <READ>) {
        • chomp($result);
        • last if $result eq $ender;
        • if ($result ne '') {
          • print "$printCommand: $result";
          • if ($tail) {
            • print $tail;
          • }
          • $lastResult = $result;
          • $printed = 1;
        • }
      • }
      • if (!$printed) {
        • print;
      • }
    • }
    • print "\n";
  • }

I saved it as “padmath” and then set it up in SilverService. It worked great; the two problems are:

  1. bc sometimes returns a line and sometimes doesn’t. Perl’s pipe input reads a line at a time, so if nothing happens, the read sits there waiting for something to happen that never does. There’s probably a better way to fix it, but what I do is just echo an unlikely string after every command. Then, I put the read into a while loop and exit if I see that string.
  2. bc can’t handle uppercase in variable names, so any existing mathpad files using camelCase need to be modified.

Obviously this won’t do graphs or any of the other stuff that MathPad did, but it will handle by far most of what I use MathPad for.

Examples

Here’s a MathPad file I used for quick calculation of download speeds:

  • --kilobytes per second
  • 15
  • --kilobytes per minute
  • #*60
  • --kilobytes per hour
  • #*60
  • --megabytes per hour
  • #/1024
  • --megabytes per day
  • #*24
  • --megabytes per month
  • #*30
  • --gigabytes per month
  • #/1024

Here’s what it looks like after selecting it and running padmath on it:

  • --kilobytes per second
  • 15: 15
  • --kilobytes per minute
  • #*60: 900
  • --kilobytes per hour
  • #*60: 54000
  • --megabytes per hour
  • #/1024: 52.7343
  • --megabytes per day
  • #*24: 1265.6232
  • --megabytes per month
  • #*30: 37968.6960
  • --gigabytes per month
  • #/1024: 37.0788

Change the 15 to something else, and it will recalculate; just like MathPad, it ignores the parts of the lines after the colons.

Here’s another simple one, I don’t even remember what it was for, possibly something to do with time:

  • remaining = 566
  • used = 90
  • total = remaining + used
  • total/60: 10.9333
February 28, 2013: MathPad is back!

Just an FYI, MathPad is back! Version 3.0.4 supports Mac OS X 10.6 and up, and works on Intel Macs without Rosetta. You’ll want to read the What’s New file; for example, I’ve been using .mathpad as the extension to get Leopard/Snow Leopard to associate my MathPad files with MathPad. In 3.0.4, the extension is officially .mp.

Another major change is that the default file format is RTF instead of text. You can save as text but there is unfortunately no preferences setting to prefer text over RTF. MathPad continues to read text files fine.

Some neat changes (I think) include the ability to use π instead of ‘pi’ in calculations.

There are several other changes as well, likely related to the switch from Carbon to Cocoa.

August 23, 2012: Adding functions to padmath

The simple padmath replacement for MathPad works for almost everything I used MathPad for. Every once in a while, however, I need a function that bc doesn’t have but that MathPad did. Three of the most common things I end up needing are π for determining area, and abs and trunc for getting the absolute value or to drop the fractions from a value.

The bc program does support creating functions, so I’ve added those three items to the prepended text that, previously, just set the system to use four decimal points.

Replace “print WRITE "scale=4\n";” with:

[toggle code]

  • #functions
  • print WRITE <<'ENDFUNCTIONS';
  • #padmath values
  • pi = 3.1416
  • #padmath functions
  • define abs(value) {
    • if (value < 0) {
      • return -value
    • } else {
      • return value
    • }
  • }
  • define trunc(value) {
    • old_scale=scale
    • scale=0
    • int_value=value/1
    • scale=old_scale
    • return int_value
  • }
  • #not in MathPad
  • define ceiling(value) {
    • return -floor(-value);
  • }
  • define floor(value) {
    • int_value = trunc(value)
    • if (value != int_value && value < 0) {
      • int_value = int_value -1
    • }
    • return int_value
  • }
  • scale=4
  • ENDFUNCTIONS

So I can now do something like this because I’m looking for a year:

  • --25-year shingles reduced 25% for being overlay, and then they’re already five years old
  • 25*.75-5: 13.75
  • 2012+trunc(#): 2025

Or, calculate the area of a circle:

  • diameter = 9
  • radius = diameter/2
  • pi*radius^2: 63.6174

Doing this I also got carried away and added ceiling and floor to the mix, because just using trunc isn’t always exactly right. Using either of those extra functions will make the pad incompatible with MathPad, however.

July 6, 2012: Yet Another Pad Calculator

Jose Cueva has written a very rough pad interface for OS X and Windows, inspired by Mark Widholm’s MathPad. His YAPC looks like it does just calculations, but it seems to work well. And it works fine in Mac OS X 10.7.4 Lion.

  1. <- GraphicConverter album art
  2. ModelForms and FormViews ->