Thursday, May 26, 2011
Modf ready for the public
Edit: I went ahead with the FUNDS support, but it isn't really much. Unlike FSet, FUNDS doesn't intend to be a replacement to the fundamental Lisp types. Most FUNDS data structures do not include the ability to edit arbitrary elements. FUNDS has trees, and Modf functions are defined for those, but the queue, stack, and heap data structures don't really make sense with Modf. You access them in a different place than you add elements.
This library is small, but kind of reaching completion. Any suggestions or patches are welcome.
Friday, May 20, 2011
Introducting Modf: Setf for functional programming
Setf is such a wonderful macro. Sometimes we forget. It is one of the first things I find missing when I have to resort to an imperative language, which is exactly the place where you want to use it the most. But there seems to be a shortcoming in Common Lisp when it comes to functional programming. What I mean is, it is incredibly easy to mutate state with setf, but when we want to use data in a functional way, it becomes much more cumbersome to do what is needed. For example…
;; Let's try something in a non-fuctional way (let ((list '(1 2 3 4 5 6 7))) ... ;; Change the 5th element in the list to t (setf (fifth list) t) ... ) ;; Let's try it functionally (let ((list '(1 2 3 4 5 6 7))) ... ;; Change the 5th element in the list to t (labels ((replace-nth (nth list new-val) (if (> nth 0) (cons (car list) (replace-nth (- nth 1) (cdr list) new-val)) (cons new-val (cdr list)) ))) (setf list (replace-nth 4 list t)) ) ... )
The cleanest way I could think to do this (barring what will follow) is to define a function that would do this for me. There is nothing wrong with that, and many will state that this general function should be placed in a toolbox so it can be used in the future saving me such effort from now on.
I find, however, that such functions are defined and often never used again, not because I don't need them but because I forget the work I've done in the past. (Not to mention that the function above isn't nearly general enough. What happens if you want to replace the cdr of the nth element, or the cadar?).
If you've read Graham's On Lisp, there are probably a couple of functions (ttrav, trec) floating around in your head that might serve our purposes here. I believe that Graham would state that making functions like the one above easier to write alleviates the problem. I find that using such "one off" methods still uses a bit too much mental overhead when coding.
Now, look at what setf does for us. It hides functions like rplaca and rplacd which no sane individual should spend grey matter on. Setf knows how to invert (car x) and (cdr x) into the proper mutating "function". Wouldn't it be nice if we had a functionality like setf that keeps track of and inserts the proper functional manipulation code. To this end I have created modf.
Modf is a macro that behaves like setf, except it returns a new object with the requested properties rather than makes the changes in place. It changes functional example from above into…
;; Let's try it functionally (let ((list '(1 2 3 4 5 6 7))) ... ;; Change the 5th element in the list to t (modf (nth 4 list) t) ... )
Right, that's all well and good, but that isn't nearly the most use you can get out of this. Let's say we were using the FSet seq data structure to hold a Sudoku board. We might use a seq of seq structures to represent our board. If we want to set a number on the board, we would do this…
(let ((board (make-board))) ;; Set element (4,3) to a 2 (modf (fset:@ (fset:@ board 4) 3) 2) )
…instead of like this…
(let ((board (make-board))) ;; Set element (4,3) to a 2 (fset:with board 4 (fset:with (fset:@ board 4) 3 2)) )
…which is considerably more convoluted. But to really nail this home, consider we have a mess like crazy-datastructure which is a mixture of classes, arrays, lists, and strings…
(defclass test-class () ((slot1 :initform 1 :initarg :slot1) (slot2 :initform 2 :initarg :slot2) (array :initform #() :initarg :array) )) (defparameter crazy-datastructure (list 1 (make-instance 'test-class :array (vector 'a 'b "hello") ) 3 4 )) (defmethod print-object ((obj test-class) str) (with-slots (slot1 slot2 array) obj (format str "#<TEST-CLASS: SLOT1: ~S, SLOT2: ~S, ARRAY: ~S>" slot1 slot2 array )))
CL-USER> (subseq (aref (slot-value (second crazy-datastructure) 'array) 2) 1 3) "el"
Let's say we want to modify that substring to "EL" instead.
CL-USER> (modf (subseq (aref (slot-value (second crazy-datastructure) 'array) 2) 1 3) "EL") (1 #<TEST-CLASS: SLOT1: 1, SLOT2: 2, ARRAY: #(A B "hELlo")> 3 4) CL-USER> ;; And the original CRAZY-DATASTRUCTURE remains unchanged crazy-datastructure (1 #<TEST-CLASS: SLOT1: 1, SLOT2: 2, ARRAY: #(A B "hello")> 3 4) CL-USER> ;; If we compare to the SETF form (setf (subseq (aref (slot-value (second crazy-datastructure) 'array) 2) 1 3) "EL") "EL" CL-USER> crazy-datastructure (1 #<TEST-CLASS: SLOT1: 1, SLOT2: 2, ARRAY: #(A B "hELlo")> 3 4)
If we consider how we might perform this functional modification without modf…
CL-USER>
(cons (car crazy-datastructure)
(cons
(make-instance 'test-class
:slot1 (slot-value (second crazy-datastructure) 'slot1)
:slot2 (slot-value (second crazy-datastructure) 'slot2)
:array (vector
(aref (slot-value (second crazy-datastructure)
'array) 0)
(aref (slot-value (second crazy-datastructure)
'array) 1)
(concatenate 'string
(subseq
(aref (slot-value
(second crazy-datastructure)
'array) 2)
0 1)
"EL"
(subseq
(aref (slot-value
(second crazy-datastructure)
'array) 2)
3))))
(cddr crazy-datastructure) ))
Here we made it simple to functionally modify parts of a data structure that includes a string nested in an array nested in a class nested in a list. Modf acts as a shorthand for complicated functional manipulations. If you examine the macro expansion of the modf form, you will see something very similar to the hand written code to change the deeply nested substring.
How it works
Really, when it comes down to it, modf is a simpler functionality than setf because any modifier can be represented as a function. This isn't true with setf. Consider…
(let ((x 5))
(setf x 5) )
…there is no function you can call with arguments x and 5 which has the effect of setting the lexical variable x to 5 in this scope. This isn't true of functional changes as we are returning the modified value. That is not to say that this didn't turn out to be a tricky macro to write. This is due to the fact that the construction mechanism has to be in the reverse order of the access forms encountered during the expansion.
You can define macro like "rewrites" with define-modf-rewrite that translate access code into other access code that modf knows how to deal with (e.g. (cadr x) -> (car (cdr x))).
You define expansion functions similar (defun (setf func) ...) and (defmethod (setf func) ...) with define-modf-function and define-modf-method, respectively.
You define expansions based on the lexical structure of the code via define-modf-expander (this is analogous in some sense to define-setf-expander). This allows you to invert forms like (car x) to the builder code (cons new-value (cdr x)). These functions return new code that will replace the old code that was passed as an argument to the function.
There is a big difference between these functions and the setf equivalents. You need to specify which argument in the form contains the object that is being modified. This is taken as an extra argument right after the name of the expander.
In principle there is no need to have define-modf-expander, since any modifier can be expressed as a function. It might be beneficial to "open code" certain modf expansions as it will give the compiler a crack at optimizing the resultant code.
There is one special form in the "modf syntax," modf-eval. Modf-eval marks sections of code that modf shouldn't try to invert, and should just leave for the Lisp system to evaluate or compile as it will (the same way modf treats any atom it encounters). This is important if so you can have code like this…
(modf (second (modf-eval '(1 2 3 4 5))) 5)
Without modf-eval, modf would try to invert the form (quote (1 2 3 4 5)), rather than modify the list (1 2 3 4 5). You can even go so far as…
(modf (second (modf-eval
(modf (third (modf-eval '(1 2 3 4 5))) 10) )) 5)
Which allows you to chain modf statements. This can get a little clunky, so to ease the reuse of previously calculated results, you can use extra modf arguments to reuse previous results.
(modf (third (modf-eval '(1 2 3 4 5))) 10
last-result
(second last-result) 5 )
== (let ((last-result (modf (third (modf-eval '(1 2 3 4 5))))))
(modf (second last-result) 5) )
You can even use previous results in non-trivial ways…
(let ((lst '(1 2 3 4 5)))
(modf (third lst) 10
result-a
(second lst) 5
result-b
(fourth lst) (list result-a result-b) ))
==> (1 2 3 ((1 2 10 4 5) (1 5 3 4 5)) 5)
An Example
As an example of how to use this, here is how you might set up modf to work with an affine matrix data structure based on FSet seqs.
;;; First we define how our data structure like we always would. (defclass fset-matrix () ((dims :initarg :dims :accessor mat-dimensions :accessor dims-of) (seq :initarg :seq :accessor seq :accessor seq-of) (a :initarg :a :initform #(1 0 0 1) :type (array integer (4)) :accessor a-of) (b :initarg :b :initform #(0 0) :type (array integer (2)) :accessor b-of) )) (defun make-fset-matrix (dims &key (initial-element 0)) (let ((arr (make-instance 'fset-matrix :seq (fset:with (fset:empty-seq initial-element) (apply #'* dims) initial-element ) :dims dims ))) arr )) (defun fref (mat &rest idx) (destructuring-bind (i j) idx (aif2 (fset:@ (seq-of mat) (+ (* (car (dims-of mat)) (+ (* (aref (a-of mat) 0) i) (* (aref (a-of mat) 1) j) (aref (b-of mat) 0) )) (+ (* (aref (a-of mat) 2) i) (* (aref (a-of mat) 3) j) (aref (b-of mat) 1) ))) it (error "Indicies ~A out of bounds ~A." idx (dims-of mat)) ))) (defun (setf fref) (val mat &rest idx) (setf (seq-of mat) (destructuring-bind (i j) idx (fset:with (seq-of mat) (+ (* (car (dims-of mat)) (+ (* (aref (a-of mat) 0) i) (* (aref (a-of mat) 1) j) (aref (b-of mat) 0) )) (+ (* (aref (a-of mat) 2) i) (* (aref (a-of mat) 3) j) (aref (b-of mat) 1) )) val ))) val ) ;; Then we define a modf function that will inform modf how to invert ;; access function. (define-modf-function fref (val mat &rest idx) (destructuring-bind (i j) idx (modf (fset:@ (slot-value mat 'seq) (+ (* (car (dims-of mat)) (+ (* (aref (a-of mat) 0) i) (* (aref (a-of mat) 1) j) (aref (b-of mat) 0) )) (+ (* (aref (a-of mat) 2) i) (* (aref (a-of mat) 3) j) (aref (b-of mat) 1) ))) val )))
The Code
I am putting up a clone of my repository on Github. I am not sure that the code is ready for public consumption, yet. I will try, in the somewhat near future, to strip out some of the dependencies and make sure it builds on Lisp images other than mine. I would like to see the removal of the dependency on my toolbox library and implementing facilities for FUNDS.
Monday, May 16, 2011
Backtracking with the Common Lisp Condition System
The Common Lisp condition system is a pretty nice facility. I use it extensively in my work. One of its strongest benefits from my point of view is the smooth transition between programmatic error handling and interactive error handling.
The Common Lisp condition system is capable of much more than handling errors, though. However, I have yet to find time to really learn its "ins and outs". I decided try out a project using the condition system in a way that most wouldn't. Here I will show you how to use the CL condition system to implement a backtracking framework.
Update (June 2015): You can read some Reddit discussion on this. Nikodemus Siivola provided a superior roll-your-own backtracking method, pointing out that using the condition system is wasteful for this purpose. He is right, of course.
The Basic Idea
A failure of an assertion is a condition. For example, if we co-opt the error condition for our purposes…
(let ((options '(2 4 6 7 2))) ; These are our options (labels ((fail (&rest args) ; If we fail, for what ever reason (error "Failed: ~A" args) ) (success (&rest args) ; If we find a successful condition, call this args ) ;; This is our re-entry point (retry-with (x) (handler-case ;; This is the actual body of our function (if (oddp x) (success x) (fail x) ) ;; If we find an error... (error (cond) (warn "Failed: ~A" cond) (if options ;; ...we recall the function (retry-with (pop options)) (error "No solution found") ))))) (retry-with (pop options)) ))
All we need is to write a macro that translates the following into that.
(let ((x '(2 4 6 7 2))) (if (oddp x) (success x) (fail x) ))
Of course, using the error condition is a bit of a faux pas. Instead, let's define our own condition type, failure. Finding a solution to the problem could also be a condition, if we choose, and we will this time. While we're at it, we'll define two functions, fail and success, that are pretty self explanatory.
;; Define a parent type for flexibility... (define-condition success (backtracking-condition) ()) ;; NOTE can hold some data about how it failed (define-condition failure (backtracking-condition) ((note :initarg :note))) ;; VALUE holds the solution (define-condition success (backtracking-condition) ((value :initarg :value))) (defun fail (&rest args) "For whatever reason, this has failed. Backtrack." (signal 'failure :note args) ) (defun success (&rest args) "We found a solution. Either return it, or add it onto the list of solutions depending on the value of *MODE* \(as set by WITH-BACKTRACKING)." (cond ((eql *mode* 'find-one) (signal 'success :value args) ) ((eql *mode* 'find-all) (push args *solutions*) (signal 'failure :value args) )))
These really don't do anything by themselves. We need to define a special way to bind nondeterministic variables, or variables that will change their values during the search. I've done this with the bt-let* macro, which is the real meat and potatoes of this. It behaves like let* except that it recognizes two special forms, one-of and one-in (one for objects, one for a list of objects), that perform these nondeterministic bindings. It's not perfect, these forms have to be in simple locations, but it works. We have to have a macro that sets up an environment to do things like handle success conditions, control the way the search is performed, and set up variables that save solutions that are found. This environment is set up with with-backtracking.
(defmacro bt-let* (bindings &body body) "Like LET*, but if you find a special nondeterministic choice form like ONE-OF or ONE-IN, treat it specially by setting up the framework for nondeterministic search." (let (bt-var (option-list (gensym)) rest-bindings ) `(let* ,(iter (for (binding . rest) on bindings) (until bt-var) (cond ((and (consp binding) (consp (second binding)) (eql 'one-of (first (second binding))) ) (setf bt-var (first binding) rest-bindings rest ) (collect (list option-list (cons 'list (rest (second binding))) ))) ((and (consp binding) (consp (second binding)) (eql 'one-in (first (second binding))) ) (setf bt-var (first binding) rest-bindings rest ) (collect (list option-list (second (second binding)) ))) (t (collect binding)) )) ,(if bt-var `(labels ((try-with (,bt-var) (handler-case (bt-let* ,rest-bindings ,@body) (failure () (if ,option-list (try-with (pop ,option-list)) (fail) ))))) (try-with (pop ,option-list)) ) `(progn ,@body) )))) (defmacro with-backtracking ((mode) &body body) "Set up the environment where backtracking can be performed. MODE can be set as one of FIND-ONE or FIND-ALL in order to specify where just the first or all possible solutions should be returned." `(let ((*mode* ',mode) *solutions* ) (handler-case (progn ,@body) (failure () (cond ((eql 'find-one *mode*) (error "No solutions found.") ) ((eql 'find-all *mode*) *solutions* ))) (success (cond) (slot-value cond 'value) ))))
Pythagorean Triples
Pythagorean Triples are three integers, $a$, $b$, and $c$, that satisfy the equation, $a^{2} + b^{2} = c^{2}$. In other words, $a$, $b$, and $c$ are the lengths of the sides of a right triangle.
(defun pyth-triples (n) (with-backtracking (bt-let* ((a (one-in (iter (for i from 1 below n) (collect i)))) (b (one-in (iter (for i from 1 below n) (collect i)))) (c (one-in (iter (for i from 1 below n) (collect i)))) ) (if (= (+ (* a a) (* b b)) (* c c)) (success (list a b c)) (fail) ))))
Performance
Common Lisp already has a pretty good backtracking library, Screamer. We can compare the performance of our library with Screamer. Here's our version…
CL-USER> (time (pyth-triples 100)) Evaluation took: 0.892 seconds of real time 0.880000 seconds of total run time (0.840000 user, 0.040000 system) [ Run times consist of 0.030 seconds GC time, and 0.850 seconds non-GC time. ] 98.65% CPU 2,135,319,408 processor cycles 345,196,992 bytes consed
…and the Screamer version?
CL-USER> (time (screams::pythagorean-triples 100)) Evaluation took: 0.060 seconds of real time 0.060000 seconds of total run time (0.060000 user, 0.000000 system) 100.00% CPU 143,821,854 processor cycles 490,032 bytes consed
So, looks like around a factor of ten. Not so good, but maybe it's not so bad that people can't use it. Now, it might seem like exhaustive search is the time, if there ever is one, where we should be the most concerned about performance. That has some truth, but we should also remember that if we ever really rely on the exhaustive search to do the work, we probably have already lost the battle. Being ten times faster will allow you to handle an input size one larger than the slow version. I like to use nondeterminism to write an algorithm that's guaranteed correct, then improve from there. So, perhaps the slow version isn't unusable after all.
Conclusions
Well, this method is a pretty simple method of implementing backtracking in Common Lisp. It is less performant and versatile than Screamer, but Screamer is a pretty gigantic library.
Monday, April 18, 2011
The difference between Open Source and Free Software
To quickly paraphrase the FSF's position, Open Source describes a method of developing, distributing, and supporting software, whereas Free Software deals with the rights people should be granted when receiving software. These are two very different things. Looking at this, I would say that what people are coming to realize is that Android is certainly Free Software, but it is not Open Source. It is Free Software because it is released under a Free Software license; your rights are protected, period. However, Android is not developed in the same way most Open Source software is. It is not so much open to community patches and development. It is developed behind closed doors by a team that may choose to listen to outside opinion but is in no way obligated to.
RMS remarks that by and large Open Source projects are Free Software projects. He poses it as an extension of Open Source, a step further, a statement of commitment to protecting user and developer rights. If we all remember correctly, the term Open Source was first coined to act as a synonym for Free Software that didn't have the same anti-corporate stigma and later evolved into something with its own foundation. People introduced the term FOSS or FLOSS (Free (Libre) Open Source Software) to lump the two together. I find it interesting to see how we really shouldn't lump them together at all; they truly are independent. Projects can be Open Source, projects can be Free Software, and they can be both.
Monday, February 7, 2011
Using Git even when collaborating with those that don't
I did, however, just move a bunch of snapshots of my supervisors simulation code into a git repo. This had the effect of organizing all of my files nicely and in one spot, provided a natural place to put weird side versions (as abandoned branches), as well as cutting about 1 GiB of files into around 20 MiB since versioned snapshots are basically 90% or more the same. In addition, I am hoping that the merge tools will allow me to reduce the number of times I will have a directory labeled v1.36, one labeled v1.36_matt, and one labeled v1.36_robert.
Next time a colleague sends me a new version, I will just create a temporary branch, merge it into my current version, and tag it with the new version number. So, even though I doubt that my supervisor will ever be able to spend the time to learn how to use Git, I think I will be able to use it to organize things on my end, which makes me a bit happier.
Tuesday, January 25, 2011
Firefox still holding its own, for me
A new complaint I have heard from my office mate recently is that Firefox uses more CPU than necessary for windows or, more appropriately tabs, that are not currently displayed. He suggested I should consider trying Chromium since I am often annoyed at how much background CPU I find Firefox occupying. As stated in previous posts, I am very sensitive to CPU usage in a very literal sense. The skin on my hands and lap are very sensitive to the rise in temperature due to stray computation on my aluminum bottomed, first generation, MacBook Pro. I have gone so far as to make a script that toggles freezing any Firefox and Adobe Flash processes on my system so I can force Firefox to stop doing anything when I don't need it. Anyway, I decided to give Chromium a try as an alternative with hopes that it would use less background CPU.
Now, I'm not sure I'm the average user, but I think that there are many out there that have a lazy, clutter filled mind such as mine. Let me describe my typical habits when using Firefox. I tend to let projects build up, and tabs in my browser have only facilitated this behavior. At any given time, I usually have more than 30 tabs open. Right now I have around 36, I have seen upwards of 45, but Firefox's background CPU usage usually persuades me to move tabs to bookmarks and or drop something I find interesting because I don't have the time. Each tab takes up a little CPU, especially if the pages periodically check in with the server. It seems my friend was wrong though. Firefox handles my use case much more elegantly than Chromium.
Looking at the process and how much they are loading the system, we see that Firefox weighs in pretty heavy:
smithzv@ciabatta:~$ ps -ausmithzv -eo pcpu,size,pmem,comm | grep firefox 0.0 304 0.0 firefox 7.6 484264 16.5 firefox-bin smithzv@ciabatta:~$ ps -ausmithzv -eo pcpu,size,pmem,comm | grep plugin 1.2 92000 1.7 plugin-containe
The Firefox program acts as a monolithic blob except for the Flash plugin which is kept in a separate process. It can be quite shocking how much load it's placing on the system; around 8.8 percent CPU and 18.2 percent of the RAM. Chromium, on the other hand, is split among many different sub processes. Let's take a look there:
smithzv@ciabatta:~$ ps -ausmithzv -eo pcpu,size,pmem,comm | grep chromium 2.1 157892 3.2 chromium-browse 0.2 2316 0.3 chromium-browse 0.0 10908 0.5 chromium-browse 3.9 90556 2.6 chromium-browse 4.9 85524 2.1 chromium-browse 0.1 10520 0.6 chromium-browse 0.1 63736 1.9 chromium-browse 0.0 16000 0.8 chromium-browse 3.4 58412 1.4 chromium-browse 0.0 53500 1.2 chromium-browse 0.1 62696 1.7 chromium-browse 0.0 54680 1.2 chromium-browse 0.1 60872 1.5 chromium-browse 1.5 82252 2.3 chromium-browse 0.6 89188 2.4 chromium-browse 0.0 55596 1.3 chromium-browse 0.0 56424 1.3 chromium-browse 0.0 17840 0.8 chromium-browse 0.0 51752 1.1 chromium-browse 0.1 59376 1.5 chromium-browse 1.9 61228 1.6 chromium-browse 0.0 56260 1.3 chromium-browse 0.1 56552 1.3 chromium-browse 0.3 57656 1.4 chromium-browse 0.0 16000 0.7 chromium-browse 0.0 56824 1.3 chromium-browse 0.0 10600 0.6 chromium-browse
We see that the Chromium processes add up to 19.4 percent CPU, over twice what Firefox is using and 37.9 percent of the RAM, again around twice that of Firefox. I'm not sure anything can be understood from the memory usage, but the CPU usage is pretty cut and dry. That said, I did notice when playing with Chromium, that tabs don't scroll like they do in Firefox, but rather squish together. At around the 30 tab mark, all you can see is the icons for each tab, soon after that any identifying characteristic disappears completely. So I don't think I would ever keep so many tabs remain open since I wouldn't be able to read the title of the tabs I am working with.
Monday, January 3, 2011
Dropped laptop and file system errors
![]() |
| USB, Firewire, Ethernet, DVI, in that order. Yes I'm watching 24 on Netflix right now. |
![]() |
| With thumb drive inserted as an illustration of the damage to the USB port. |
![]() |
| The picture doesn't quite convey how ridiculously bent-up this looks. |
Anyway, after the drop and subsequent freak out, I closed the laptop and left it alone until yesterday evening. When I started using it again, I slowly realized that the root filesystem was mounted read only. This happens when Ubuntu detects an error in the file system. Now, I cannot say that this has anything to do with the drop, especially considering my root file system has mysteriously been remounted read only in the past. However, in the past, I typically restart the system, it does a fsck on the un-clean partitions, and repairs any errors. I tried this and, boom, stopped up in GRUB.
![]() |
| From above: BUG: unable to handle kernel paging request at 01bc0000. After this, GRUB can't mount a bunch of stuff and craps out to the (initramfs) prompt. |
Around 18 months ago I set up a pretty good backup system so that I never have to worry about it. It performs autonomous, daily, incremental backups of my important stuff (see rdiff-backup). This mean I don't have to worry about a long recovery or data lost if the disk is really pooched, what a relief. Let's not get ahead of ourselves, though, let's see if it is recoverable.
The OS X partition still boots, but you can't repair (or even read) ext4 from OS X. Okay, no biggie, I'll just boot off a live CD and repair the partition there. Uh-oh, on vacation and didn't bring any live CDs. Okay, download Maverick and burn a new one. Let's boot it up. Shoot, forgot, the CD drive in my MBPro has always been finicky on reading CD's that it didn't write (to the point that I bet it is worthless for most CD-R's); it sees it as a blank CD. Try again writing from the OS X parition of the MBPro, Uh-oh, that failed, couldn't write the CD. Use the wifes laptop to write one; sees that one as blank too. Now I have one coaster and two Maverick CD's but no way to boot from them.
Then I realized that I have a thumb drive and booting from it is just about my only chance to recover the computer. I install the Maverick image onto the thumb drive (which I had to do from Windows as the Mac directions didn't work). Then I booted Maverick. A note here, I had to alter some kernel options (remove prevalence or whatever from the options) to make sure that it didn't try to mess with the screwy partitions as that hung the boot. Then I tried to run fsck on the partition, but it says the device is busy. That's weird as it isn't even mounted. A little Googling and I find that some people see similar behavior when using raid (not sure why this would effect me). I found someone else that hit it in a situation like mine and was able to work around it using a different distribution, SLAX. So back to the Windows computer, download SLAX, write it to the thumb drive. Boot up, fsck, and it's fixed. Phew.
Another day in the life of a GNU/Linux user. Moral of the story, always carry bootable media if you hold your computer together with shoelaces and bubblegum like I do.
Update (01/14): The file system errors continued, sometimes as frequently as having to repair it each day. I figured this must be detrimental to my data even though I haven't noticed any problems yet. I decided to instead backup my entire home space, blow away everything and reinstall (and upgrade while I'm at it, now at 10.10). Things have been working since... fingers crossed. I guess the worst case scenario is that I am forced to purchase a new computer, maybe one that can last longer than an hour and a half unplugged.
Update (01/31): Still having issues. Twice my drive was remounted RO while I was using it (and not doing anything really odd). The first time I brought it up in SLAX and fixed the file system errors. I wonder if the fact that fsck does does anything means, definitively, that I have lost some data, somewhere. The backups showed no difference, but I don't back up every file. I also did a non-destructive, read-write surface scan on the entire 72 GiB partition (for me that took about 4.5 hours). It showed no bad blocks. A few days later (yesterday) I found my computer had remounted RO the root file system again. This time I backed up my entire home directory and deleted the partition, then repartitioned and reinstalled Ubuntu with three differences. I made sure to have parted align the partitions to the cylinder instead of the MiB (align to MiB is the default). Shouldn't matter but it also got rid of the annoying tiny blocks of free space between my partitions. I also included a small 100 MiB partition that I can mount and save stuff to if the root file system is remounted RO. Lastly, I used Ext3 partitions for everything this time instead of the default Ext4 file system. Not sure if it will matter though I hear Ext4 is more performant; I can always upgrade later. Anyway, fingers crossed again.




