Saturday, 3 March 2012

Types of arrays in common lisp

Here's a gotcha that I've finally understood (with some hand-holding from stassats on #lisp).
The type of a lisp array describes how specialised it is, not the objects it contains.
How to tell if you're confused in the same way that I was: you think that (simple-array T (*)) should be a super-type of (simple-array '(signed-int 32) (*)). But see here:
CL-USER> (defparameter *x*
           (make-array 5 :element-type '(signed-byte 32)
                         :initial-element 0))
*X*
CL-USER> (typep *x* '(simple-array (signed-byte 32)))
T
CL-USER> (typep *x* '(simple-array T))
NIL
What's going on is that (simple-array (signed-byte 32)) means an array that is specialised to hold 32-bit integers more efficiently. If I was on an old 8-bit machine or was using a less enthusiastically optimising lisp implementation, the make-array call might not have managed to make a specialised array. In which case, it would have fallen back to making a (slower / less efficient) array that can hold any old thing. The resulting object would have type (simple-array T). In fact, the two types are disjoint. The SIMPLE-ARRAY documentation in the hyperspec makes it clear:
The types simple-vector, simple-string, and simple-bit-vector are disjoint subtypes of type simple-array, for they respectively mean (simple-array t (*)), the union of all (simple-array c (*)) for any c being a subtype of type character, and (simple-array bit (*)).
Well, now that makes sense. So what do you do if you want to allow specialised arrays or generic arrays in your type? There's one magic option (also explained in the CLHS at the link above). Use (simple-array *) and it'll match anything.

Tuesday, 1 November 2011

When libtool's dependency_libs goes weird

I had a strange problem last night where I couldn't build some code I was working on. The program (gpsim) depends on the Gtk stack and the errors I got seemed to refer to a nonexistant libtool .la file:

libtool: link: ar cru .libs/libgpsimgui.a gui_break.o gui_callbacks.o gui_dialog.o gui_init.o gui_main.o gui_menu.o gui_processor.o gui_regwin.o gui_src.o gui_src_asm.o gui_src_opcode.o gui_statusbar.o gui_symbols.o gui_watch.o gui_breadboard.o gui_stack.o gui_trace.o gui_profile.o gui_stopwatch.o gui_object.o gui_scope.o settings_exdbm.o gui_hextable.o preferences.o gui_marshal.o libtool: link: ranlib .libs/libgpsimgui.a /bin/sed: can't read /usr/lib/libgio-2.0.la: No such file or directory libtool: link: `/usr/lib/libgio-2.0.la' is not a valid libtool archive

Now, I'm running an up-to-date version of Debian unstable and I couldn't really believe that a problem that stopped people linking things depending on glib would survive very long. The solution came when I realised that references to /path/to/foo.la come from other .la files. Ahah! So, after some thought, I tried a grep command:

$ locate '*.la' | xargs grep libgio-2.0.la | grep '^/opt' | cut -d : -f 1
/opt/gnome/lib/gtk-2.0/modules/libgtkparasite.la
/opt/gnome/lib/libgpsim.la
/opt/gnome/lib/libgpsimcli.la
/opt/gnome/lib/libgpsimgui.la
/opt/gnome/lib/libgpsim_ds1307.la
/opt/gnome/lib/libgpsim_dspic.la
/opt/gnome/lib/libgpsim_graphicLCD.la
/opt/gnome/lib/libgpsim_lcd.la
/opt/gnome/lib/libgpsim_modules.la
/opt/gnome/lib/libgtkextra-x11-2.0.la

Ahah! The culprit was the last line: gpsim still links against the hideously obsolete libgtkextra and I'd forgotten to rebuild my local copy when upgrading my glib package to one that doesn't ship libgio-2.0.la. Editing the libgtkextra .la file might have worked but I just rebuilt the lot to make sure it worked. Sorted!

Tuesday, 4 October 2011

Bookmarklet to ease the pain from Warwick Uni Library's E-journal change

The University of Warwick Library no longer allow access to E-journals except after jumping through hoops to access them through the library's web catalogue. This is painful and, although I've now got a plethora of links to various journal home-pages, I still find myself cutting and pasting parts of URLs to avoid some journals' awful navigation interfaces.

But all is not lost! Here's a JavaScript bookmarklet which will open up the current page channelled through Pugwash, which appears to be the library's main server. It seems to work for me, but the "0-" prefix on the URLs looks worryingly magical and I'm not sure whether this is correct for all journals...

To use the bookmarklet on Firefox (or pretty much anywhere else), you can right-click on the link (the "here" above) and select something like "Bookmark this link". If you are (rightly) wary of trusting a random hunk of JavaScript, this is the code:

javascript:(function(){
  var str=location.href;
  if(str.substr(0,7)!="http://") {
    alert("Unrecognised protocol");
    return;
  }
  var ffs=str.substr(7).indexOf("/");
  if(ffs==-1) {
    fs=str.length-1;
  }
  else {
    ffs=ffs+7;
  }
  window.open("http://0-" + str.substr(7,ffs-7) +
              ".pugwash.lib.warwick.ac.uk/"+str.substr(ffs+1));
})()

(It has to be on one line to work, so you'll need to delete a lot of whitespace if you want to copy and paste this).

Thursday, 24 June 2010

Gray boxes in evince

If you're having a weird problem with evince showing gray boxes instead of text in some pdfs, the problem could well be a bug in cairo.


After some help from "danigm" on the #evince channel on irc.gnome.org, I found that the bug disappeared on upgrading libcairo to 1.9.8 (which is currently in debian experimental).


Anyway, hopefully the next person who searches google for grey boxes in evince or indeed gray boxes in evince should find the solution!

Friday, 31 July 2009

Huawei E1550 on Debian

If you want to get the Huawei E1550 USB modem working with linux, here's what you need to do, with specific instructions for those on Debian.

modem-modeswitch


This utility is part of udev as of version 145. You need it because the E1550 has a clever double-identity: when you insert it at first, it masquerades as a mass storage device (usb id 12D1:1446). Upon running this magical command, it disconnects and reappears as an "E620 USB modem" (usb id 12D1:1001). Magic eh?

Firstly, you need to get the program. If you have udev 145, life is good. Unfortunately, the current debian unstable package is on version 141, which doesn't have it. Trying to manually install udev 145, I managed to break my X setup quite spectacularly, so I don't advise that. Fortunately, if you download the latest udev source (from
http://www.kernel.org/pub/linux/utils/kernel/hotplug/) and compile it, you'll find that the binary in the extras/modem-modeswitch directory works fine without the rest of udev 145. Note that to compile udev, you'll need some libraries. On debian, an incantation like apt-get build-dep udev should get them. To put it somewhere useful, one option is to run checkinstall from inside the extras/modem-modeswitch
directory and make a fake debian package for the meantime.

Now you've got modem-modeswitch installed, you want to try it! Pull up a spare terminal window and run something like sudo tail -f /var/log/syslog in it. This lets you know what's going on, since drivers tend to report stuff there when they initialise
themselves. Now run the following command /path/to/modem-modeswitch -v 0x12d1 -p 0x1446 -t option-zerocd. On my dongle (courtesy of 3), there's a green LED that flashes at first and then turns blue at this point. In any case, the syslog window should report a USB disconnect and then a new device. Running lsusb should show a line something like Bus 002 Device 003: ID 12d1:1001 Huawei Technologies Co., Ltd. E620 USB Modem (the first two numbers will probably be different: they're
just to say where it's connected).

If you've got this far, the confusing bit's over. Unplug the device again and we'll set things up to run modem-modeswitch by default.

udev


To do so, we need to create a udev rule to tell it to run when the device is plugged in. Create a file in /etc/udev/rules.d/ called something like 50-huawei1550.rules containing the following line

SUBSYSTEM=="usb", SYSFS{idProduct}=="1446",SYSFS{idVendor}=="12d1", RUN+="/usr/libexec/modem-modeswitch --vendor 0x12d1 --product 0x1446 --type option-zerocd"

This tells udev that when a device with USB id 12d1:1446 gets plugged in, it should run modem-modeswitch to change its mode. (This snippet came from somewhere on the web, but I've lost the reference). You probably need to restart udev here (/etc/init.d/udev restart) to enable your rule. Re-insert the device and cross your fingers... if everything works, it should change mode.


usb-serial

If you're using a distro kernel, you can probably ignore this, but I was running a home-built kernel which didn't have the usb-serial kernel module. You need it.

If you've got stuff right, lines like the following should appear in your syslog:

Jul 31 10:49:03 hake kernel: usbcore: registered new interface driver usbserial
Jul 31 10:49:03 hake kernel: USB Serial support registered for generic
Jul 31 10:49:03 hake kernel: usbcore: registered new interface driver usbserial_generic
Jul 31 10:49:03 hake kernel: usbserial: USB Serial Driver core
Jul 31 10:49:03 hake kernel: USB Serial support registered for GSM modem (1-port)
Jul 31 10:49:03 hake kernel: option 2-2:1.0: GSM modem (1-port) converter detected
Jul 31 10:49:03 hake kernel: usb 2-2: GSM modem (1-port) converter now attached to ttyUSB0
Jul 31 10:49:03 hake kernel: option 2-2:1.1: GSM modem (1-port) converter detected
Jul 31 10:49:03 hake kernel: usb 2-2: GSM modem (1-port) converter now attached to ttyUSB1
Jul 31 10:49:03 hake kernel: option 2-2:1.2: GSM modem (1-port) converter detected
Jul 31 10:49:03 hake kernel: usb 2-2: GSM modem (1-port) converter now attached to ttyUSB2
Jul 31 10:49:03 hake kernel: usbcore: registered new interface driver option
Jul 31 10:49:03 hake kernel: option: v0.7.2:USB Driver for GSM modems

Thursday, 30 October 2008

Science Education Website

Well, I'm lame and not really updating this. However, my mother has a terribly nice (ahem?) website at WAS Science, which she doesn't seem to be able to get web spiders to notice. It's a site full of science education resources including powerpoint presentations and worksheets.

Maybe some googly goodness from blogger will go her way.

Thursday, 26 July 2007

Quick book recommendation

This summer, I've got an undergraduate research bursary at our university's Systems Biology centre, which is wierd as I always viewed myself of more of a pure mathematician, but is absolutely fascinating.

I have jumped in at the deep end, however (my supervisor seems to think I know a lot more than I really do and maybe hasn't realised I haven't done any serious integration/differentiation or ODEs stuff since the first year...).

What I'm actually doing is looking at the response of Neurons to different types of presynaptic noise, which I am slowly learning more about. In particular, noise is random (well, duh) and so all of the models I end up with have Stochastic Differential Equations, which are typically quite unpleasant. For those interested, the toy models I'm playing with end up with Brownian motion in a harmonic potential.

So the book I'm recommending is:

Paul and Baschnagel, "Stochastic Processes, From Physics to Finance"

Don't let the reference to finance put you off - I've found it absolutely fantastic for explaining the basics of Wiener processes, brownian motion and so on. In particular, it is written for the scientist rather than the mathematician and so everything's done in the real numbers, rather than over wierd and wonderful probability spaces. I really believe that even if you want to learn stochastic integration properly as a mathematician, this is a worthwhile read, because you end up for a real feel for what's going on.

The book's published by Springer and is on Amazon here at the terrifying price of about £50 used, so you might want to try a university library!

Now back to work!

Thursday, 5 July 2007

Fun with xorg.conf

So I've finally got my ALPS / Synaptics touchpad working along with a compose key on debian (the ALPS touchpad comes with, among many other laptops, the Dell Inspiron 500m which I own).

The touchpad

For the touchpad, I need to thank this walkthrough along with this forum thread.

Before anything will work, you need to make sure that the evdev module is being compiled in your kernel (it wasn't) and is being loaded (that bit worked for free for me, but one comment on the above forum post suggested you needed to add it to /etc/modules). You can find the option under Device Drivers/Input Device Support/Event Interface in the kernel config (e.g. make menuconfig)


Now you need to edit your xorg.conf (found in /etc/X11). There are three things that you need to do:

  • Load the synaptics module

  • Add a new input device to ServerLayout

  • Declare the input device


In order to load the synaptics module, add a line saying:

Load "synaptics"

to the "Module" section. Mine now looks like:

Section "Module"
Load "glx"
Load "extmod"
Load "xtrap"
Load "record"
Load "GLcore"
Load "dbe"
Load "dri"
Load "type1"
Load "synaptics"
EndSection


Now adding a new input device to ServerLayout is simple - just choose a name (I called it "ALPS") and add the line

InputDevice "your name for it here"

to the ServerLayout section just below the line saying

InputDevice "Configured Mouse"


Finally, add the declaration for the input device. A version with no clever options set is:

Section "InputDevice"
Identifier "ALPS"
Driver "synaptics"
Option "SendCoreEvents" "true"
Option "Device" "/dev/input/mouse1"
Option "Protocol" "evdev"
Option "SHMConfig" "on"
EndSection

(note that the identifier has to agree with the name you chose earlier). For a much more thorough list of settings you might wish to see the third page of the debuntu article here.

Assuming you got everything right, you should now be able to restart your X server and get exciting touchpad support. Note that to restart the X server, the best practice is to exit (gracefully) any open programs, log out of whatever desktop environment you're using and when you get to the display manager (xdm,kdm,gdm or whatever) hit Ctrl-Alt-Backspace.

The Compose Key

This is really simple, but it makes life much easier when writing auf Deutsch on an english keyboard. In your InputDevice section corresponding to your keyboard, add the line:
Option "XkbOptions" "lv3:ralt_switch, compose:ralt"
The relevant section in my xorg.conf now looks like this:

Section "InputDevice"
Identifier "Keyboard0"
Driver "kbd"
Option "XkbModel" "pc105"
Option "XkbLayout" "gb"
Option "XkbOptions" "lv3:ralt_switch, compose:ralt"
EndSection


Once you've restarted the X server, Alt-Gr S, S should give ß and you can have the fun of discovering umlauts, graves, circumflexes etc. yourself. It's a bit like a more logical version of the system they used in Microsoft Word.

Wednesday, 13 June 2007

Backing up your home to DVD with debian

I've finally got a DVD writer (external - it's a laptop) and it works quite amazingly well: no setup issues at all!

Now, the whole point of the exercise was that my Dell is getting a little dodgy (you have to press down hard around the power button to flex the PCB below in order to get it to boot!) and I wanted to back up my documents, sourcecode, emails etc. in case the worst should befall my poor old laptop. But I had a German exam the next day, and while I was burning the DVD I should also be revising!

In general, burning a data DVD on linux is easy. You get a copy of mkisofs and growisofs (on debian, this is easy: apt-get install dvd+rw-tools mkisofs). Then you call mkisofs on a directory tree to create an iso image (put it in /tmp!) and growisofs to write the lot to your DVD (I got the instructions here

Ahah, but not so fast! I wanted to do my home directory whilst xfce, claws-mail, firefox and probably a whole load of other processes were writing tiddly little bits to all my hidden dot-files and dot-directories in ~/ (you know, the ones like ~/.gnome and ~/.mozilla etc.). In fact, I didn't twig the first time and mkisofs threw a wobbly! Moreover, I didn't want to make another copy of everything because there's quite a lot of stuff and I didn't really have space for that on the harddisk partition.

So here's the solution:

First I made a folder called backmeup in my home directory:
mkdir backmeup
Then I moved all of the "normal" i.e. non-hidden files in my home directory into backmeup:
mv * backmeup
This will actually complain because at some point you've asked it to move backmeup into backmeup, but ignore the complaint - it did what we wanted! Now for the dot-files. We don't want to move them into backmeup, because some of the running programs would throw a wobbly, so we will actually need to copy this lot. Now, don't run cp -Rp .* backmeup/ because it'll go horribly wrong and try to make two copies of backmeup via the .. directory I think (disclaimer: Yes, this did go horribly wrong. And it took me 5 minutes to notice!)

Instead, call the following command:
cp -Rp `find -maxdepth 1 -name '.*' | sed '/^\.$/d'` backmeup
which is what we really wanted.

Now we're almost done! Just make the iso from the backmeup tree (no-one's going to be writing automatically to something called that!):
nice -n 19 mkisofs -r -o /tmp/rupert.iso /home/rupert/backmeup
The nice command is just to stop the job hogging the whole system.

... And write the iso to the disk:
growisofs -Z /dev/scd0=/tmp/rupert.iso

(clearly the device to which you write will need to depend on what the kernel calls it - I'm trying to do something clever with udev, but haven't quite got there yet!)

Wednesday, 6 June 2007

Ical subscriptions with orage

Orage looks to be an exciting program for my new Xfce desktop, but what good's a calendar if it can't subscribe to ical feeds from the internet?! Fortunately, I'm supposed to be revising at the moment, so have had an urge to hack code together...

And so we present a simple shell script to download .ics files periodically from the internet to your filesystem. The one nifty feature is that if the internet's down, this won't blat the current copies as using wget blindly would, which should be useful with my laptop.


There are probably more sophisticated systems, but I was going for the 25 minute job option. Firstly, we need a list of feeds - mine looked something like this:

## Lines must be either urls, blank, or start with a #.

# UK holidays
http://ical.mac.com/ical/UK32Holidays.ics

# NASA Space Missions
http://ical.mac.com/tonyfarley/SpaceMissions.ics

# Astronomical Events
http://hewgill.com/astrocal/astrocal.ics


The lines beginning with hashes really are comments - they get stripped by the downloading script, which uses wget:

#!/bin/sh

dir=/home/rupert/.icals/

for f in `cat $dir/feeds.list | sed -e '/^$/d' -e '/^\#/d'`
do
fout=$dir/`echo $f | md5sum | cut -f 1 -d ' '`.ics
tmp=`mktemp $dir/getfile.XXXXXX`
wget -q --tries=3 -O $tmp $f
if [ -s $tmp ]; then
mv $tmp $fout
else
rm $tmp
fi
done


To make it work, you need to call the first file feeds.list and set dir to a directory containing feeds.list and in which you'd like your downloaded .ics files to end up. The quickest way I could think of to give the files names was the md5sum of the url, so once you've got everything in place, set the executable bit on the script (which I called get_feeds.sh) and run it.

If all goes to plan, you should get some files called things like "5b7ed8afdd4a8f71525d0df0e47231e5.ics" appearing in $dir. Now we need to automate it: I used cron, so call crontab -e and add the following line to your crontab:

*/15 * * * * /home/rupert/.icals/get_feeds.sh

(well clearly, you'll need to make the directory right!) This calls the get_feeds.sh script every 15 minutes. Maybe we should slow that down, but I was testing!

Finally, use the new "Exchange Data" item on Orage's File menu to add a foreign file corresponding to the .ics you downloaded and you're away! Wahey!

Incidentally, the SVN orage that I downloaded this evening doesn't appear to be getting multiple foreign files quite right - if I can work out what's going on, I'll file a bug tomorrow!

Sunday, 13 May 2007

Tree display program (update)

Well, I handed in a rather inconclusive version of the essay I mentioned. It and the program used to generate the trees are both here.

I'd love it if anyone found the program useful - and anyone's more than welcome to do anything with the code, although I'd appreciate a heads-up out of interest. Enjoy!

Copying UTF into Emacs

It's been annoying me for ages, but I never really got UTF copy/paste working right in Emacs. Finally (procrastinating from German revision) I found a solution that works for copying from Firefox at least.

The solution is detailed here and I'm eternally grateful. Basically, just put the following code somewhere in your .emacs:

(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))


And, voila, umlauts (like ö) and I presume strange characters from other languages will appear in all their glory!

Sunday, 8 April 2007

Leptonica 1.43 in debian

What a load of effort! I've been developing a program using leptonica recently, using the debian package without thinking. Irritatingly, however, debian is at version 1.37 and upstream is at 1.43.

The good news is that the debian diff still works - to get a quick and dirty install (which is all I'm interested in!) do the following:


  1. Download the debian diff from this page.
  2. Patch a copy of the current source (from here) using the command:

    patch <../leptonlib_1.37-1.diff -p1


    from within the directory into which you extracted leptonica.
  3. Make the "debian/rules" file executable:

    chmod 0755 debian/rules

  4. Edit the "debian/docs" file and remove the line with library-notes.txt (it no longer exists in the package)
  5. cd into the debian directory and run the command

    dch -v 1.44-1

    (The dch program is part of the devscripts package)
  6. Finally, make sure a copy of the original 1.43 tar.gz is in the parent directory with the name
    leptonlib_1.43.orig.tar.gz
    and call
    debuild
    from the main source directory. Hopefully, it should compile and you'll have the relevant debfiles in the parent directory from which to install leptonica 1.43!

Friday, 30 March 2007

Tree display program

I've been writing away at a compulsory second year essay for what seems ages now. In particular, this one's about Goodstein's Theorem. When it's done (which will definitely be before the deadline in 3 weeks' time), I'll see what I can do about posting a version.

However, for now, I've been working on a bit in the proof of the theorem. What you need to do is express a number in hereditary base-n notation which is explained on the Wikipedia article I just linked to. Now suppose you draw a rooted finite tree in the following way (let's express 93 in base 2).

First note that:


Now build a tree one level of branches at a time. In the first level, is a branch for each term in the expansion above. Then a child of each node has children that are the terms in the exponent of that child... It'll make more sense with a picture:



I hope :)

So what I wrote was a program that would generate that sort of picture because I needed some in the essay and couldn't find something on the internet to draw it. To save problems with file formats etc., the program actually exports to SVG - to generate png images for here and for PdfLatex in the essay I used the command "inkscape -f file.svg -e file.png", which I found here. So I'm going to try and post the code below. The command line options are:

Options:

--help produce help message
-b [ --base ] arg (=2) set base of expansion
-s [ --stroke ] arg (=black) set colour of stroke
-f [ --fill ] arg (=red) set colour of fill
-w [ --width ] arg (=800) width of image
-h [ --height ] arg (=600) height of image
-o [ --output ] arg (=-) Output file

And to make the picture above, use:
maketree 93 -b 2
To compile, you just need boost program options.



Anyway, here are the sources:



Makefile:

maketree: maketree.o NumberTree.o SVGOut.o
g++ -g -lboost_program_options -o $@ $^

%.o: %.cc
g++ -O0 -g -I/usr/include/my-boost -Wall -c $<

clean:
rm *.o


NumberTree.cc:

#include "NumberTree.hh"
#include <math.h>
#include <boost/foreach.hpp>
#include <sstream>

using std::ostream;

NumberTree::NumberTree( long n, int b )
: output_width(800), output_height(600), width(-1), height(-1),
base(b)
{
while( n ) {
int hp = find_hipow(n,b);

children.push_back( TreePtr(new NumberTree(hp,b)) );
n -= static_cast<int>(pow(b,hp));
}
}

void
NumberTree::BracketPrint( ostream& os ) const
{
os << "(";
for( unsigned int i=0; i<children.size(); i++ ) {
children[i]->BracketPrint(os);
if(i<children.size()-1)
os << ",";
}
os << ")";
}

int
NumberTree::find_hipow( long n, int b ) const // TESTED
{
if(n < 1 || b < 2)
throw "Invalid parameters";
return static_cast<int>(floor(log(n)/log(b)));
}

long
NumberTree::eval( int b ) const // TESTED
{
long total=0;

BOOST_FOREACH( const TreePtr& pt, children ) {
total += static_cast<long>(pow(b, pt->eval(b)));
}

return total;
}

void
NumberTree::printSVG( SVGOut& so )
{
so.start();

std::ostringstream os;
os << eval(base);
so.annotated_circle(0.5,0,os.str());
printSVGBranches(so,0,1,1,0,0.5,
1.0/calcHeight(),1.0/calcWidths());
so.end();
}

int
NumberTree::calcWidths()
{
if( widths.size() ) return width;

if(!children.size()) return 0;

int w = 0;
BOOST_FOREACH( TreePtr& pt, children ) {
int t = pt->calcWidths();
w += t+1;
widths.push_back(t);
}

return width = w-1; // subtract one to lose padding on RHS
}

int
NumberTree::calcHeight()
{
if( height != -1 ) return height;

int max=-1;
BOOST_FOREACH( TreePtr& pt, children ) {
int th = pt->calcHeight();
if(max<th) max = th;
}
return height = max+1;
}

void
NumberTree::printSVGBranches( SVGOut& o,
float l, float t, float r, float b,
float px, float lineheight, float xscale )
{
int lhs = 0;

BOOST_FOREACH( const TreePtr& pt, children ) {
int w = pt->calcWidths();

float newmid = lhs + w/2.0;
if(calcWidths() == 0)
newmid = 0;
else
newmid /= calcWidths();
newmid *= r-l;
newmid += l;

std::ostringstream os;
os << pt->eval(base);
o.annotated_circle(newmid, lineheight+b, os.str());

o.line(newmid,lineheight+b,px,b);

pt->printSVGBranches(o, l+lhs*xscale, t, l+(lhs+w)*xscale,
b+lineheight, newmid, lineheight, xscale );

lhs += w+1;
}
}


NumberTree.hh:

#ifndef NUMBERTREE_HH
#define NUMBERTREE_HH

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
#include "SVGOut.hh"

class NumberTree
{
public:
/* Construct tree from n with given base */
NumberTree( long n, int b );

/* Print as parentheses */
void BracketPrint( std::ostream& os ) const;

/* Evaluate as powers of b */
long eval( int b ) const;

/* Output SVG to stream */
void printSVG( SVGOut& so );

private:
/* Find the largest power of b that is less than n */
int find_hipow( long n, int b ) const;
/* Find the width in branches of the tree and populate knowledge of
* widths of our immediate children */
int calcWidths();
/* Find the height of the subtree below us */
int calcHeight();
/* Draw our subtree to SVG */
void printSVGBranches( SVGOut& os,
float l, float t, float r, float b,
float px, float lineheight, float xscale );

/* Output sizes for SVG */
int output_width, output_height;

/* The actual important data! */
typedef boost::shared_ptr<NumberTree> TreePtr;
std::vector<TreePtr> children;

/* Widths for printing */
std::vector<int> widths;
/* Width - must be dirtied to -1 if we change something */
int width;
/* Height - must be dirtied to -1 if we change something */
int height;

/* Base of expansion */
int base;
};

#endif


SVGOut.cc:

#include "SVGOut.hh"
#include <cmath>

using std::string;

SVGOut::SVGOut( std::ostream& os, int width, int height, int margin )
: o(os), top(margin), left(margin), right(width-margin),
bottom(height-margin), _margin(margin), fill("black"),
stroke("black")
{
set_circle_radius(0.01);
set_font_size(0.03);
}

void
SVGOut::start()
{
o << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>\n"
<< "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n"
<< "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
<< "<svg xmlns=\"http://www.w3.org/2000/svg\"\n"
<< " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
<< " width=\""
<< static_cast<int>(fabs(right-left) + 2*_margin)
<< "\" height=\""
<< static_cast<int>(fabs(top-bottom) + 2*_margin)
<< "\">\n";
}

void
SVGOut::end()
{
o << "\n</svg>\n";
}

void
SVGOut::circle( float x, float y )
{
boost::tuple<float,float> ddpos = xyToSVG(x,y);

o << " <circle cx=\""
<< ddpos.get<0>()
<< "\" cy=\""
<< ddpos.get<1>()
<< "\" r=\"" << circle_radius << "\" style=\"stroke: "
<< stroke << "; fill: " << fill << ";\"/>\n";
}

void
SVGOut::line( float x1, float y1, float x2, float y2 )
{
boost::tuple<float,float>
ddpos1 = xyToSVG(x1,y1),
ddpos2 = xyToSVG(x2,y2);

o << " <line x1=\""
<< ddpos1.get<0>() << "\" y1=\""
<< ddpos1.get<1>() << "\" x2=\""
<< ddpos2.get<0>() << "\" y2=\""
<< ddpos2.get<1>() << "\" style=\"stroke: "
<< stroke << ";\"/>\n";
}

boost::tuple<float,float>
SVGOut::xyToSVG( float x, float y ) const
{
return
boost::make_tuple( float( (right-left)*x + left ),
float( (top-bottom)*y + bottom ) );
}

void
SVGOut::text( const string& s, float x, float y )
{
boost::tuple<float,float> ddpos = xyToSVG(x,y);

o << " <text x=\"" << ddpos.get<0>() << "\" y=\"" << ddpos.get<1>()
<< "\" font-family=\"Verdana\" font-size=\""
<< font_size << "\" fill=\"black\">"
<< s
<< "</text>\n";
}

void
SVGOut::set_fill_colour( const string& colour )
{
fill = colour;
}

void
SVGOut::set_stroke_colour( const string& colour )
{
stroke = colour;
}

void
SVGOut::set_circle_radius( float rad )
{
circle_radius = rad*fabs(right-left);
vanilla_circle_radius = rad;
}

void
SVGOut::set_font_size( float size )
{
font_size = size*fabs(top-bottom);
}

void
SVGOut::annotated_circle( float x, float y, const std::string& s )
{
circle(x,y);
text(s, x+vanilla_circle_radius*1.1, y );
}


SVGOut.hh:

#ifndef SVGOUT_HH
#define SVGOUT_HH

#include <iostream>
#include <string>
#include <boost/tuple/tuple.hpp>

class SVGOut
{
public:
SVGOut( std::ostream& os, int width, int height, int margin );

void set_fill_colour( const std::string& colour );
void set_stroke_colour( const std::string& colour );
// rad, size are compared to the [0,1]x[0,1] canvas!
void set_circle_radius( float rad );
void set_font_size( float size );

void start();
void end();

/* Element creation functions */
void circle( float x, float y );
void line( float x1, float y1, float x2, float y2 );
void text( const std::string& s, float x, float y );
void annotated_circle( float x, float y, const std::string& s );

private:
boost::tuple<float,float> xyToSVG( float x, float y ) const;

/* Output stream ref */
std::ostream& o;
/* Input sizes */
float top,left,right,bottom;
float _margin;
/* Colours */
std::string fill, stroke;

/* Other settings: Only change throught set_ functions */
float circle_radius, font_size;
float vanilla_circle_radius;
};

#endif


maketree.cc:

#include <iostream>
#include <string>
#include <errno.h>
#include <boost/program_options.hpp>
#include <fstream>
#include "NumberTree.hh"
#include "SVGOut.hh"

void usage()
{
std::cerr << "I need exactly 1 argument, which is a number.\n";
exit(1);
}

int main( int argc, char* argv[] )
{
using std::string;

namespace po = boost::program_options;
po::options_description standard("Options");
standard.add_options()
("help", "produce help message")
("base,b", po::value<unsigned int>()->default_value(2), "set base of expansion")
("stroke,s", po::value<string>()->default_value("black"), "set colour of stroke")
("fill,f", po::value<string>()->default_value("red"), "set colour of fill")
("width,w", po::value<unsigned int>()->default_value(800), "width of image")
("height,h", po::value<unsigned int>()->default_value(600), "height of image")
("output,o", po::value<string>()->default_value("-"), "Output file")
;

po::options_description hidden("Options");
hidden.add_options()
("number", po::value<unsigned long int>(), "number to be represented");

po::options_description all("Options");
all.add(standard).add(hidden);

po::positional_options_description pos;
pos.add("number", 1);

po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(all).positional(pos).run(), vm);
po::notify(vm);

if (vm.count("help")) {
std::cerr << standard << "\n";
return 1;
}

string output = vm["output"].as<string>();

std::ostream* os;
if( output == "-" ) os = &std::cout;
else {
os = new std::ofstream(output.c_str());
if(!os) {
std::cerr << "Could not open output file.";
return 1;
}
}

if (vm.count("number")) {
NumberTree tree(vm["number"].as<unsigned long int>(),
vm["base"].as<unsigned int>() );
SVGOut sout(*os,
vm["width"].as<unsigned int>(),
vm["height"].as<unsigned int>(),
30);

sout.set_fill_colour( vm["fill"].as<string>() );
sout.set_stroke_colour( vm["stroke"].as<string>() );

tree.printSVG(sout);
}

if( output != "-" ) delete os;
}

Latex in Posts

So the question is whether as posted here will actually work. OK. So I'm impressed :)


Now can it work with ScribeFire? I fear not...